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