]> git.pond.sub.org Git - empserver/blob - src/lib/gen/ioqueue.c
Fix trailing whitespace
[empserver] / src / lib / gen / ioqueue.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  ioqueue.c: Read and write i/o queues
29  *
30  *  Known contributors to this file:
31  *
32  */
33
34 /*
35  * Read and write onto io queues.  Note that
36  * the io queues don't actually do any writing;
37  * that is left for a higher level.
38  */
39
40 #include <config.h>
41
42 #include <stdlib.h>
43 #include <string.h>
44 #include <sys/uio.h>
45 #include "ioqueue.h"
46 #include "misc.h"
47 #include "queue.h"
48
49 static int ioqtocbuf(struct ioqueue *ioq, char *buf, int cc, int stopc);
50 static int ioqtoiov(struct ioqueue *ioq, struct iovec *iov, int max);
51 static int ioqtobuf(struct ioqueue *ioq, char *buf, int cc);
52 static int appendcc(struct ioqueue *ioq, char *buf, int cc);
53 static int removecc(struct ioqueue *ioq, int cc);
54
55 struct ioqueue *
56 ioq_create(int size)
57 {
58     struct ioqueue *ioq;
59
60     ioq = malloc(sizeof(struct ioqueue));
61     emp_initque(&ioq->list.queue);
62     ioq->list.nbytes = 0;
63     ioq->list.offset = 0;
64     ioq->list.size = 0;
65     ioq->list.data = 0;
66     ioq->bufsize = size;
67     ioq->cc = 0;
68     return ioq;
69 }
70
71 void
72 ioq_destroy(struct ioqueue *ioq)
73 {
74     ioq_drain(ioq);
75     free(ioq);
76 }
77
78 void
79 ioq_drain(struct ioqueue *ioq)
80 {
81     struct emp_qelem *qp;
82     struct io *io;
83
84     while ((qp = ioq->list.queue.q_forw) != &ioq->list.queue) {
85         io = (struct io *)qp;
86         emp_remque(&io->queue);
87         free(io->data);
88         free(io);
89     }
90
91     ioq->cc = 0;
92 }
93
94 /*
95  * copy batch of pointers into the passed
96  * iovec, but don't actually dequeue the data.
97  * return # of iovec initialized.
98  */
99 int
100 ioq_makeiov(struct ioqueue *ioq, struct iovec *iov, int cc)
101 {
102     if (ioq->cc <= 0)
103         return 0;
104     return ioqtoiov(ioq, iov, cc);
105 }
106
107 /*
108  * Copy the specified number of characters into the buffer
109  * provided, without actually dequeueing the data.  Return
110  * number of bytes actually found.
111  */
112 int
113 ioq_peek(struct ioqueue *ioq, char *buf, int cc)
114 {
115     return ioqtobuf(ioq, buf, cc);
116 }
117
118 int
119 ioq_dequeue(struct ioqueue *ioq, int cc)
120 {
121     return removecc(ioq, cc);
122 }
123
124 void
125 ioq_append(struct ioqueue *ioq, char *buf, int cc)
126 {
127     appendcc(ioq, buf, cc);
128 }
129
130 int
131 ioq_qsize(struct ioqueue *ioq)
132 {
133     return ioq->cc;
134 }
135
136 /*
137  * read a line of text up to (but not including)
138  * the newline.  return -1 and read nothing if
139  * no input is available
140  */
141 int
142 ioq_gets(struct ioqueue *ioq, char *buf, int cc)
143 {
144     int nbytes;
145     int actual;
146
147     nbytes = ioqtocbuf(ioq, buf, cc - 1, '\n');
148     if (nbytes >= 0) {
149         actual = nbytes;
150         if (actual > cc - 1)
151             actual = cc - 1;
152         /* telnet terminates lines with "\r\n", get rid of \r */
153         if (actual > 0 && buf[actual-1] == '\r')
154             actual--;
155         buf[actual] = '\0';
156         /* remove the newline too */
157         removecc(ioq, nbytes + 1);
158     }
159     return nbytes;
160 }
161
162 int
163 ioq_puts(struct ioqueue *ioq, char *buf)
164 {
165     return appendcc(ioq, buf, strlen(buf));
166 }
167
168 /*
169  * copy cc bytes from ioq to buf.
170  * this routine doesn't free memory; this is
171  * left for a higher level.
172  */
173 static int
174 ioqtobuf(struct ioqueue *ioq, char *buf, int cc)
175 {
176     struct io *io;
177     struct emp_qelem *qp;
178     struct emp_qelem *head;
179     int nbytes, nleft;
180     char *offset;
181
182     nleft = cc;
183     offset = buf;
184     head = &ioq->list.queue;
185     for (qp = head->q_forw; qp != head && nleft > 0; qp = qp->q_forw) {
186         io = (struct io *)qp;
187         if ((nbytes = io->nbytes - io->offset) < 0) {
188             /* XXX log something here */
189             continue;
190         }
191         if (nbytes > 0) {
192             if (nleft < nbytes)
193                 nbytes = nleft;
194             memcpy(offset, io->data + io->offset, nbytes);
195             offset += nbytes;
196             nleft -= nbytes;
197         }
198     }
199     return offset - buf;
200 }
201
202 /*
203  * copy at most cc bytes from ioq to buf,
204  * terminating on the stop character.
205  */
206 static int
207 ioqtocbuf(struct ioqueue *ioq, char *buf, int cc, int stopc)
208 {
209     int nbytes;
210     char *p;
211     int n;
212     struct io *io;
213     struct emp_qelem *qp;
214     struct emp_qelem *head;
215     int total;
216     int found;
217
218     head = &ioq->list.queue;
219     found = 0;
220     total = 0;
221     for (qp = head->q_forw; qp != head; qp = qp->q_forw) {
222         io = (struct io *)qp;
223         if ((nbytes = io->nbytes - io->offset) <= 0)
224             continue;
225         p = io->data + io->offset;
226         for (n = 0; n < nbytes && p[n] != stopc; n++) ;
227         total += n;
228         if (n < nbytes) {
229             found++;
230             break;
231         }
232     }
233     if (found == 0)
234         return -1;
235     ioqtobuf(ioq, buf, cc < total ? cc : total);
236     return total;
237 }
238
239 /*
240  * initialize an iovec to point at max bytes worth
241  * of data from the ioqueue.
242  */
243 static int
244 ioqtoiov(struct ioqueue *ioq, struct iovec *iov, int max)
245 {
246     struct io *io;
247     int cc, niov, len;
248     struct emp_qelem *qp;
249
250     cc = max;
251     niov = 0;
252     qp = ioq->list.queue.q_forw;
253     while (qp != &ioq->list.queue && cc > 0) {
254         io = (struct io *)qp;
255         len = io->nbytes - io->offset;
256         if (len > cc)
257             len = cc;
258         iov->iov_base = io->data + io->offset;
259         iov->iov_len = len;
260         cc -= len;
261         niov++;
262         iov++;
263         qp = qp->q_forw;
264         if (niov >= 16)
265             break;
266     }
267     return niov;
268 }
269
270 /*
271  * append a buffer to the end of the ioq.
272  */
273 static int
274 appendcc(struct ioqueue *ioq, char *buf, int cc)
275 {
276     struct io *io;
277     int len;
278     char *ptr;
279     int avail;
280
281     /* determine if any space is left */
282     io = (struct io *)ioq->list.queue.q_back;
283     avail = io->size - io->nbytes;
284     if (avail > 0) {
285         /* append to existing buffer */
286         len = cc > avail ? avail : cc;
287         memcpy(io->data + io->nbytes, buf, len);
288         io->nbytes += len;
289         ioq->cc += len;
290         if (avail < cc)
291             appendcc(ioq, buf + len, cc - len);
292     } else {
293         /* create a new buffer, minimum bufsize bytes */
294         len = cc > ioq->bufsize ? cc : ioq->bufsize;
295         ptr = malloc(len);
296         memcpy(ptr, buf, cc);
297         io = malloc(sizeof(struct io));
298         io->nbytes = cc;
299         io->size = len;
300         io->offset = 0;
301         io->data = ptr;
302         emp_insque(&io->queue, ioq->list.queue.q_back);
303         ioq->cc += cc;
304     }
305     return cc;
306 }
307
308 /*
309  * remove cc bytes from ioqueue ioq
310  * free memory, dequeue io elements
311  * which are no longer used.
312  */
313 static int
314 removecc(struct ioqueue *ioq, int cc)
315 {
316     struct io *io;
317     struct emp_qelem *qp;
318     int nbytes, there, remain;
319
320     nbytes = 0;
321     remain = cc;
322     while ((qp = ioq->list.queue.q_forw) != &ioq->list.queue) {
323         io = (struct io *)qp;
324         there = io->nbytes - io->offset;
325         if (there < 0) {
326             /* error */
327             emp_remque(&io->queue);
328             free(io);
329             continue;
330         }
331         if (remain >= there) {
332             /* not enough or exact; free entry */
333             nbytes += there;
334             remain -= there;
335             emp_remque(&io->queue);
336             free(io->data);
337             free(io);
338         } else {
339             /* too much; increment offset */
340             io->offset += remain;
341             nbytes += remain;
342             remain = 0;
343         }
344         if (remain <= 0)
345             break;
346     }
347     ioq->cc -= nbytes;
348     return nbytes;
349 }