]> git.pond.sub.org Git - empserver/blob - src/lib/gen/ioqueue.c
926c0f55f03ee3480b81427f9eb88ca8d704ff68
[empserver] / src / lib / gen / ioqueue.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2007, 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 <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <sys/uio.h>
46 #include "ioqueue.h"
47 #include "misc.h"
48 #include "queue.h"
49
50 static int ioqtocbuf(struct ioqueue *ioq, char *buf, int cc, int stopc);
51 static int ioqtoiov(struct ioqueue *ioq, struct iovec *iov, int max);
52 static int ioqtobuf(struct ioqueue *ioq, char *buf, int cc);
53 static int appendcc(struct ioqueue *ioq, char *buf, int cc);
54 static int removecc(struct ioqueue *ioq, int cc);
55
56 struct ioqueue *
57 ioq_create(int size)
58 {
59     struct ioqueue *ioq;
60
61     ioq = malloc(sizeof(struct ioqueue));
62     emp_initque(&ioq->list.queue);
63     ioq->list.nbytes = 0;
64     ioq->list.offset = 0;
65     ioq->list.size = 0;
66     ioq->list.data = 0;
67     ioq->bufsize = size;
68     ioq->cc = 0;
69     return ioq;
70 }
71
72 void
73 ioq_destroy(struct ioqueue *ioq)
74 {
75     ioq_drain(ioq);
76     free(ioq);
77 }
78
79 void
80 ioq_drain(struct ioqueue *ioq)
81 {
82     struct emp_qelem *qp;
83     struct io *io;
84
85     while ((qp = ioq->list.queue.q_forw) != &ioq->list.queue) {
86         io = (struct io *)qp;
87         emp_remque(&io->queue);
88         free(io->data);
89         free(io);
90     }
91
92     ioq->cc = 0;
93 }
94
95 /*
96  * copy batch of pointers into the passed
97  * iovec, but don't actually dequeue the data.
98  * return # of iovec initialized.
99  */
100 int
101 ioq_makeiov(struct ioqueue *ioq, struct iovec *iov, int cc)
102 {
103     if (ioq->cc <= 0)
104         return 0;
105     return ioqtoiov(ioq, iov, cc);
106 }
107
108 /*
109  * Copy the specified number of characters into the buffer
110  * provided, without actually dequeueing the data.  Return
111  * number of bytes actually found.
112  */
113 int
114 ioq_peek(struct ioqueue *ioq, char *buf, int cc)
115 {
116     return ioqtobuf(ioq, buf, cc);
117 }
118
119 int
120 ioq_dequeue(struct ioqueue *ioq, int cc)
121 {
122     return removecc(ioq, cc);
123 }
124
125 void
126 ioq_append(struct ioqueue *ioq, char *buf, int cc)
127 {
128     appendcc(ioq, buf, cc);
129 }
130
131 int
132 ioq_qsize(struct ioqueue *ioq)
133 {
134     return ioq->cc;
135 }
136
137 /*
138  * read a line of text up to (but not including)
139  * the newline.  return -1 and read nothing if
140  * no input is available
141  */
142 int
143 ioq_gets(struct ioqueue *ioq, char *buf, int cc)
144 {
145     int nbytes;
146     int actual;
147
148     nbytes = ioqtocbuf(ioq, buf, cc - 1, '\n');
149     if (nbytes >= 0) {
150         actual = nbytes;
151         if (actual > cc - 1)
152             actual = cc - 1;
153         /* telnet terminates lines with "\r\n", get rid of \r */
154         if (actual > 0 && buf[actual-1] == '\r')
155             actual--;
156         buf[actual] = '\0';
157         /* remove the newline too */
158         removecc(ioq, nbytes + 1);
159     }
160     return nbytes;
161 }
162
163 int
164 ioq_puts(struct ioqueue *ioq, char *buf)
165 {
166     return appendcc(ioq, buf, strlen(buf));
167 }
168
169 /*
170  * copy cc bytes from ioq to buf.
171  * this routine doesn't free memory; this is
172  * left for a higher level.
173  */
174 static int
175 ioqtobuf(struct ioqueue *ioq, char *buf, int cc)
176 {
177     struct io *io;
178     struct emp_qelem *qp;
179     struct emp_qelem *head;
180     int nbytes, nleft;
181     char *offset;
182
183     nleft = cc;
184     offset = buf;
185     head = &ioq->list.queue;
186     for (qp = head->q_forw; qp != head && nleft > 0; qp = qp->q_forw) {
187         io = (struct io *)qp;
188         if ((nbytes = io->nbytes - io->offset) < 0) {
189             /* XXX log something here */
190             continue;
191         }
192         if (nbytes > 0) {
193             if (nleft < nbytes)
194                 nbytes = nleft;
195             memcpy(offset, io->data + io->offset, nbytes);
196             offset += nbytes;
197             nleft -= nbytes;
198         }
199     }
200     return offset - buf;
201 }
202
203 /*
204  * copy at most cc bytes from ioq to buf,
205  * terminating on the stop character.
206  */
207 static int
208 ioqtocbuf(struct ioqueue *ioq, char *buf, int cc, int stopc)
209 {
210     int nbytes;
211     char *p;
212     int n;
213     struct io *io;
214     struct emp_qelem *qp;
215     struct emp_qelem *head;
216     int total;
217     int found;
218
219     head = &ioq->list.queue;
220     found = 0;
221     total = 0;
222     for (qp = head->q_forw; qp != head; qp = qp->q_forw) {
223         io = (struct io *)qp;
224         if ((nbytes = io->nbytes - io->offset) <= 0)
225             continue;
226         p = io->data + io->offset;
227         for (n = 0; n < nbytes && p[n] != stopc; n++) ;
228         total += n;
229         if (n < nbytes) {
230             found++;
231             break;
232         }
233     }
234     if (found == 0)
235         return -1;
236     ioqtobuf(ioq, buf, cc < total ? cc : total);
237     return total;
238 }
239
240 /*
241  * initialize an iovec to point at max bytes worth
242  * of data from the ioqueue.
243  */
244 static int
245 ioqtoiov(struct ioqueue *ioq, struct iovec *iov, int max)
246 {
247     struct io *io;
248     int cc, niov, len;
249     struct emp_qelem *qp;
250
251     cc = max;
252     niov = 0;
253     qp = ioq->list.queue.q_forw;
254     while (qp != &ioq->list.queue && cc > 0) {
255         io = (struct io *)qp;
256         len = io->nbytes - io->offset;
257         if (len > cc)
258             len = cc;
259         iov->iov_base = io->data + io->offset;
260         iov->iov_len = len;
261         cc -= len;
262         niov++;
263         iov++;
264         qp = qp->q_forw;
265         if (niov >= 16)
266             break;
267     }
268     return niov;
269 }
270
271 /*
272  * append a buffer to the end of the ioq.
273  */
274 static int
275 appendcc(struct ioqueue *ioq, char *buf, int cc)
276 {
277     struct io *io;
278     int len;
279     char *ptr;
280     int avail;
281
282     /* determine if any space is left */
283     io = (struct io *)ioq->list.queue.q_back;
284     avail = io->size - io->nbytes;
285     if (avail > 0) {
286         /* append to existing buffer */
287         len = cc > avail ? avail : cc;
288         memcpy(io->data + io->nbytes, buf, len);
289         io->nbytes += len;
290         ioq->cc += len;
291         if (avail < cc)
292             appendcc(ioq, buf + len, cc - len);
293     } else {
294         /* create a new buffer, minimum bufsize bytes */
295         len = cc > ioq->bufsize ? cc : ioq->bufsize;
296         ptr = malloc(len);
297         memcpy(ptr, buf, cc);
298         io = malloc(sizeof(struct io));
299         io->nbytes = cc;
300         io->size = len;
301         io->offset = 0;
302         io->data = ptr;
303         emp_insque(&io->queue, ioq->list.queue.q_back);
304         ioq->cc += cc;
305     }
306     return cc;
307 }
308
309 /*
310  * remove cc bytes from ioqueue ioq
311  * free memory, dequeue io elements
312  * which are no longer used.
313  */
314 static int
315 removecc(struct ioqueue *ioq, int cc)
316 {
317     struct io *io;
318     struct emp_qelem *qp;
319     int nbytes, there, remain;
320
321     nbytes = 0;
322     remain = cc;
323     while ((qp = ioq->list.queue.q_forw) != &ioq->list.queue) {
324         io = (struct io *)qp;
325         there = io->nbytes - io->offset;
326         if (there < 0) {
327             /* error */
328             emp_remque(&io->queue);
329             free(io);
330             continue;
331         }
332         if (remain >= there) {
333             /* not enough or exact; free entry */
334             nbytes += there;
335             remain -= there;
336             emp_remque(&io->queue);
337             free(io->data);
338             free(io);
339         } else {
340             /* too much; increment offset */
341             io->offset += remain;
342             nbytes += remain;
343             remain = 0;
344         }
345         if (remain <= 0)
346             break;
347     }
348     ioq->cc -= nbytes;
349     return nbytes;
350 }