]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - contrib/cygtermd/sel.c
Add a directory 'contrib/cygtermd', containing the source code for my
[PuTTY.git] / contrib / cygtermd / sel.c
1 /*
2  * sel.c: implementation of sel.h.
3  */
4
5 #include <stddef.h>
6 #include <string.h>
7 #include <errno.h>
8 #include <assert.h>
9
10 #include <fcntl.h>
11 #include <unistd.h>
12 #include <sys/time.h>
13 #include <sys/types.h>
14 #include <sys/select.h>
15
16 #include "sel.h"
17 #include "malloc.h"
18
19 /* ----------------------------------------------------------------------
20  * Chunk of code lifted from PuTTY's misc.c to manage buffers of
21  * data to be written to an fd.
22  */
23
24 #define BUFFER_GRANULE  512
25
26 typedef struct bufchain_tag {
27     struct bufchain_granule *head, *tail;
28     size_t buffersize;                 /* current amount of buffered data */
29 } bufchain;
30 struct bufchain_granule {
31     struct bufchain_granule *next;
32     size_t buflen, bufpos;
33     char buf[BUFFER_GRANULE];
34 };
35
36 static void bufchain_init(bufchain *ch)
37 {
38     ch->head = ch->tail = NULL;
39     ch->buffersize = 0;
40 }
41
42 static void bufchain_clear(bufchain *ch)
43 {
44     struct bufchain_granule *b;
45     while (ch->head) {
46         b = ch->head;
47         ch->head = ch->head->next;
48         sfree(b);
49     }
50     ch->tail = NULL;
51     ch->buffersize = 0;
52 }
53
54 static size_t bufchain_size(bufchain *ch)
55 {
56     return ch->buffersize;
57 }
58
59 static void bufchain_add(bufchain *ch, const void *data, size_t len)
60 {
61     const char *buf = (const char *)data;
62
63     if (len == 0) return;
64
65     ch->buffersize += len;
66
67     if (ch->tail && ch->tail->buflen < BUFFER_GRANULE) {
68         size_t copylen = BUFFER_GRANULE - ch->tail->buflen;
69         if (copylen > len)
70             copylen = len;
71         memcpy(ch->tail->buf + ch->tail->buflen, buf, copylen);
72         buf += copylen;
73         len -= copylen;
74         ch->tail->buflen += copylen;
75     }
76     while (len > 0) {
77         struct bufchain_granule *newbuf;
78         size_t grainlen = BUFFER_GRANULE;
79         if (grainlen > len)
80             grainlen = len;
81         newbuf = snew(struct bufchain_granule);
82         newbuf->bufpos = 0;
83         newbuf->buflen = grainlen;
84         memcpy(newbuf->buf, buf, grainlen);
85         buf += grainlen;
86         len -= grainlen;
87         if (ch->tail)
88             ch->tail->next = newbuf;
89         else
90             ch->head = ch->tail = newbuf;
91         newbuf->next = NULL;
92         ch->tail = newbuf;
93     }
94 }
95
96 static void bufchain_consume(bufchain *ch, size_t len)
97 {
98     struct bufchain_granule *tmp;
99
100     assert(ch->buffersize >= len);
101     while (len > 0) {
102         size_t remlen = len;
103         assert(ch->head != NULL);
104         if (remlen >= ch->head->buflen - ch->head->bufpos) {
105             remlen = ch->head->buflen - ch->head->bufpos;
106             tmp = ch->head;
107             ch->head = tmp->next;
108             sfree(tmp);
109             if (!ch->head)
110                 ch->tail = NULL;
111         } else
112             ch->head->bufpos += remlen;
113         ch->buffersize -= remlen;
114         len -= remlen;
115     }
116 }
117
118 static void bufchain_prefix(bufchain *ch, void **data, size_t *len)
119 {
120     *len = ch->head->buflen - ch->head->bufpos;
121     *data = ch->head->buf + ch->head->bufpos;
122 }
123
124 /* ----------------------------------------------------------------------
125  * The actual implementation of the sel interface.
126  */
127
128 struct sel {
129     void *ctx;
130     sel_rfd *rhead, *rtail;
131     sel_wfd *whead, *wtail;
132 };
133
134 struct sel_rfd {
135     sel *parent;
136     sel_rfd *prev, *next;
137     sel_readdata_fn_t readdata;
138     sel_readerr_fn_t readerr;
139     void *ctx;
140     int fd;
141     int frozen;
142 };
143
144 struct sel_wfd {
145     sel *parent;
146     sel_wfd *prev, *next;
147     sel_written_fn_t written;
148     sel_writeerr_fn_t writeerr;
149     void *ctx;
150     int fd;
151     bufchain buf;
152 };
153
154 sel *sel_new(void *ctx)
155 {
156     sel *sel = snew(struct sel);
157
158     sel->ctx = ctx;
159     sel->rhead = sel->rtail = NULL;
160     sel->whead = sel->wtail = NULL;
161
162     return sel;
163 }
164
165 sel_wfd *sel_wfd_add(sel *sel, int fd,
166                      sel_written_fn_t written, sel_writeerr_fn_t writeerr,
167                      void *ctx)
168 {
169     sel_wfd *wfd = snew(sel_wfd);
170
171     wfd->written = written;
172     wfd->writeerr = writeerr;
173     wfd->ctx = ctx;
174     wfd->fd = fd;
175     bufchain_init(&wfd->buf);
176
177     wfd->next = NULL;
178     wfd->prev = sel->wtail;
179     if (sel->wtail)
180         sel->wtail->next = wfd;
181     else
182         sel->whead = wfd;
183     sel->wtail = wfd;
184     wfd->parent = sel;
185
186     return wfd;
187 }
188
189 sel_rfd *sel_rfd_add(sel *sel, int fd,
190                      sel_readdata_fn_t readdata, sel_readerr_fn_t readerr,
191                      void *ctx)
192 {
193     sel_rfd *rfd = snew(sel_rfd);
194
195     rfd->readdata = readdata;
196     rfd->readerr = readerr;
197     rfd->ctx = ctx;
198     rfd->fd = fd;
199     rfd->frozen = 0;
200
201     rfd->next = NULL;
202     rfd->prev = sel->rtail;
203     if (sel->rtail)
204         sel->rtail->next = rfd;
205     else
206         sel->rhead = rfd;
207     sel->rtail = rfd;
208     rfd->parent = sel;
209
210     return rfd;
211 }
212
213 size_t sel_write(sel_wfd *wfd, const void *data, size_t len)
214 {
215     bufchain_add(&wfd->buf, data, len);
216     return bufchain_size(&wfd->buf);
217 }
218
219 void sel_wfd_setfd(sel_wfd *wfd, int fd)
220 {
221     wfd->fd = fd;
222 }
223
224 void sel_rfd_setfd(sel_rfd *rfd, int fd)
225 {
226     rfd->fd = fd;
227 }
228
229 void sel_rfd_freeze(sel_rfd *rfd)
230 {
231     rfd->frozen = 1;
232 }
233
234 void sel_rfd_unfreeze(sel_rfd *rfd)
235 {
236     rfd->frozen = 0;
237 }
238
239 int sel_wfd_delete(sel_wfd *wfd)
240 {
241     sel *sel = wfd->parent;
242     int ret;
243
244     if (wfd->prev)
245         wfd->prev->next = wfd->next;
246     else
247         sel->whead = wfd->next;
248     if (wfd->next)
249         wfd->next->prev = wfd->prev;
250     else
251         sel->wtail = wfd->prev;
252
253     bufchain_clear(&wfd->buf);
254
255     ret = wfd->fd;
256     sfree(wfd);
257     return ret;
258 }
259
260 int sel_rfd_delete(sel_rfd *rfd)
261 {
262     sel *sel = rfd->parent;
263     int ret;
264
265     if (rfd->prev)
266         rfd->prev->next = rfd->next;
267     else
268         sel->rhead = rfd->next;
269     if (rfd->next)
270         rfd->next->prev = rfd->prev;
271     else
272         sel->rtail = rfd->prev;
273
274     ret = rfd->fd;
275     sfree(rfd);
276     return ret;
277 }
278
279 void sel_free(sel *sel)
280 {
281     while (sel->whead)
282         sel_wfd_delete(sel->whead);
283     while (sel->rhead)
284         sel_rfd_delete(sel->rhead);
285     sfree(sel);
286 }
287
288 void *sel_get_ctx(sel *sel) { return sel->ctx; }
289 void sel_set_ctx(sel *sel, void *ctx) { sel->ctx = ctx; }
290 void *sel_wfd_get_ctx(sel_wfd *wfd) { return wfd->ctx; }
291 void sel_wfd_set_ctx(sel_wfd *wfd, void *ctx) { wfd->ctx = ctx; }
292 void *sel_rfd_get_ctx(sel_rfd *rfd) { return rfd->ctx; }
293 void sel_rfd_set_ctx(sel_rfd *rfd, void *ctx) { rfd->ctx = ctx; }
294
295 int sel_iterate(sel *sel, long timeout)
296 {
297     sel_rfd *rfd;
298     sel_wfd *wfd;
299     fd_set rset, wset;
300     int maxfd = 0;
301     struct timeval tv, *ptv;
302     char buf[65536];
303     int ret;
304
305     FD_ZERO(&rset);
306     FD_ZERO(&wset);
307
308     for (rfd = sel->rhead; rfd; rfd = rfd->next) {
309         if (rfd->fd >= 0 && !rfd->frozen) {
310             FD_SET(rfd->fd, &rset);
311             if (maxfd < rfd->fd + 1)
312                 maxfd = rfd->fd + 1;
313         }
314     }
315
316     for (wfd = sel->whead; wfd; wfd = wfd->next) {
317         if (wfd->fd >= 0 && bufchain_size(&wfd->buf)) {
318             FD_SET(wfd->fd, &wset);
319             if (maxfd < wfd->fd + 1)
320                 maxfd = wfd->fd + 1;
321         }
322     }
323
324     if (timeout < 0) {
325         ptv = NULL;
326     } else {
327         ptv = &tv;
328         tv.tv_sec = timeout / 1000;
329         tv.tv_usec = 1000 * (timeout % 1000);
330     }
331
332     do {
333         ret = select(maxfd, &rset, &wset, NULL, ptv);
334     } while (ret < 0 && (errno == EINTR || errno == EAGAIN));
335
336     if (ret < 0)
337         return errno;
338
339     /*
340      * Just in case one of the callbacks destroys an rfd or wfd we
341      * had yet to get round to, we must loop from the start every
342      * single time. Algorithmically irritating, but necessary
343      * unless we want to store the rfd structures in a heavyweight
344      * tree sorted by fd. And let's face it, if we cared about
345      * good algorithmic complexity it's not at all clear we'd be
346      * using select in the first place.
347      */
348     do {
349         for (wfd = sel->whead; wfd; wfd = wfd->next)
350             if (wfd->fd >= 0 && FD_ISSET(wfd->fd, &wset)) {
351                 void *data;
352                 size_t len;
353
354                 FD_CLR(wfd->fd, &wset);
355                 bufchain_prefix(&wfd->buf, &data, &len);
356                 ret = write(wfd->fd, data, len);
357                 assert(ret != 0);
358                 if (ret < 0) {
359                     if (wfd->writeerr)
360                         wfd->writeerr(wfd, errno);
361                 } else {
362                     bufchain_consume(&wfd->buf, len);
363                     if (wfd->written)
364                         wfd->written(wfd, bufchain_size(&wfd->buf));
365                 }
366                 break;
367             }
368     } while (wfd);
369     do {
370         for (rfd = sel->rhead; rfd; rfd = rfd->next) 
371             if (rfd->fd >= 0 && !rfd->frozen && FD_ISSET(rfd->fd, &rset)) {
372                 FD_CLR(rfd->fd, &rset);
373                 ret = read(rfd->fd, buf, sizeof(buf));
374                 if (ret < 0) {
375                     if (rfd->readerr)
376                         rfd->readerr(rfd, errno);
377                 } else {
378                     if (rfd->readdata)
379                         rfd->readdata(rfd, buf, ret);
380                 }
381                 break;
382             }
383     } while (rfd);
384
385     return 0;
386 }