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