OSDN Git Service

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