]> git.pond.sub.org Git - empserver/blob - src/lib/gen/ioqueue.c
Indented with src/scripts/indent-emp.
[empserver] / src / lib / gen / ioqueue.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2000, 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 = (struct ioqueue *)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 #if !defined(aix) && !defined(NeXT)
84 /* ioq_drain doesn't work under aix or NeXT... dunno why --ts */
85     ioq_drain(ioq);
86 #endif /* aix */
87     free((s_char *)ioq);
88 }
89
90 void
91 ioq_drain(struct ioqueue *ioq)
92 {
93     struct emp_qelem *qp;
94     struct io *io;
95
96     while ((qp = ioq->list.queue.q_forw) != &ioq->list.queue) {
97         io = (struct io *)qp;
98         (void)emp_remque(&io->queue);
99         (void)free((s_char *)io->data);
100         (void)free((s_char *)io);
101     }
102
103     ioq->cc = 0;
104 }
105
106 /*
107  * copy batch of pointers into the passed
108  * iovec, but don't actually dequeue the data.
109  * return # of iovec initialized.
110  */
111 #if !defined(_WIN32)
112 int
113 ioq_makeiov(struct ioqueue *ioq, struct iovec *iov, int cc)
114 {
115     if (ioq->cc <= 0)
116         return 0;
117     return ioqtoiov(ioq, iov, cc);
118 }
119 #endif
120
121 /*
122  * Copy the specified number of characters into the buffer
123  * provided, without actually dequeueing the data.  Return
124  * number of bytes actually found.
125  */
126 int
127 ioq_peek(struct ioqueue *ioq, s_char *buf, int cc)
128 {
129     return ioqtobuf(ioq, buf, cc);
130 }
131
132 int
133 ioq_dequeue(struct ioqueue *ioq, int cc)
134 {
135     return removecc(ioq, cc);
136 }
137
138 void
139 ioq_append(struct ioqueue *ioq, s_char *buf, int cc)
140 {
141     appendcc(ioq, buf, cc);
142 }
143
144 int
145 ioq_qsize(struct ioqueue *ioq)
146 {
147     return ioq->cc;
148 }
149
150 /*
151  * read a line of text up to (but not including)
152  * the newline.  return -1 and read nothing if
153  * no input is available
154  */
155 int
156 ioq_gets(struct ioqueue *ioq, s_char *buf, int cc)
157 {
158     int nbytes;
159     int actual;
160
161     nbytes = ioqtocbuf(ioq, buf, cc - 1, '\n');
162     if (nbytes >= 0) {
163         actual = nbytes;
164         if (actual > cc - 1)
165             actual = cc - 1;
166         buf[actual] = '\0';
167         /* remove the newline too */
168         removecc(ioq, nbytes + 1);
169 #if defined(_WIN32)
170         loc_StripDels(buf);
171 #endif
172     }
173     return nbytes;
174 }
175
176 int
177 ioq_puts(struct ioqueue *ioq, s_char *buf)
178 {
179     return appendcc(ioq, buf, strlen(buf));
180 }
181
182 /*
183  * all the rest are local to this module
184  */
185
186
187 /*
188  * copy cc bytes from ioq to buf.
189  * this routine doesn't free memory; this is
190  * left for a higher level.
191  */
192 static
193     int
194 ioqtobuf(struct ioqueue *ioq, s_char *buf, int cc)
195 {
196     struct io *io;
197     struct emp_qelem *qp;
198     struct emp_qelem *head;
199     register int nbytes;
200     register int nleft;
201     register s_char *offset;
202
203     nleft = cc;
204     offset = buf;
205     head = &ioq->list.queue;
206     for (qp = head->q_forw; qp != head && nleft > 0; qp = qp->q_forw) {
207         io = (struct io *)qp;
208         if ((nbytes = io->nbytes - io->offset) < 0) {
209             /* XXX log something here */
210             continue;
211         }
212         if (nbytes > 0) {
213             if (nleft < nbytes)
214                 nbytes = nleft;
215             bcopy(io->data + io->offset, offset, nbytes);
216             offset += nbytes;
217             nleft -= nbytes;
218         }
219     }
220     return offset - buf;
221 }
222
223 /*
224  * copy at most cc bytes from ioq to buf,
225  * terminating on the stop character.
226  */
227 static
228     int
229 ioqtocbuf(struct ioqueue *ioq, s_char *buf, int cc, register int stopc)
230 {
231     register int nbytes;
232     register s_char *p;
233     register int n;
234     struct io *io;
235     struct emp_qelem *qp;
236     struct emp_qelem *head;
237     int total;
238     int found;
239
240     head = &ioq->list.queue;
241     found = 0;
242     total = 0;
243     for (qp = head->q_forw; qp != head; qp = qp->q_forw) {
244         io = (struct io *)qp;
245         if ((nbytes = io->nbytes - io->offset) <= 0)
246             continue;
247         p = io->data + io->offset;
248         for (n = 0; n < nbytes && p[n] != stopc; n++) ;
249         total += n;
250         if (n < nbytes) {
251             found++;
252             break;
253         }
254     }
255     if (found == 0)
256         return -1;
257     ioqtobuf(ioq, buf, cc < total ? cc : total);
258     return total;
259 }
260
261 /*
262  * initialize an iovec to point at max bytes worth
263  * of data from the ioqueue.
264  */
265 #if !defined(_WIN32)
266 static
267     int
268 ioqtoiov(struct ioqueue *ioq, struct iovec *iov, register int max)
269 {
270     struct io *io;
271     register int cc;
272     register int niov;
273     register int len;
274     struct emp_qelem *qp;
275
276     cc = max;
277     niov = 0;
278     qp = ioq->list.queue.q_forw;
279     while (qp != &ioq->list.queue && cc > 0) {
280         io = (struct io *)qp;
281         len = io->nbytes - io->offset;
282         if (len > cc)
283             len = cc;
284         iov->iov_base = io->data + io->offset;
285         iov->iov_len = len;
286         cc -= len;
287         niov++;
288         iov++;
289         qp = qp->q_forw;
290         if (niov >= 16)
291             break;
292     }
293     return niov;
294 }
295 #endif
296
297 /*
298  * append a buffer to the end of the ioq.
299  */
300 static
301     int
302 appendcc(struct ioqueue *ioq, s_char *buf, int cc)
303 {
304     struct io *io;
305     int len;
306     s_char *ptr;
307     int avail;
308
309     /* determine if any space is left */
310     io = (struct io *)ioq->list.queue.q_back;
311     avail = io->size - io->nbytes;
312     if (avail > 0) {
313         /* append to existing buffer */
314         len = cc > avail ? avail : cc;
315         bcopy(buf, io->data + io->nbytes, len);
316         io->nbytes += len;
317         ioq->cc += len;
318         if (avail < cc)
319             appendcc(ioq, buf + len, cc - len);
320     } else {
321         /* create a new buffer, minimum bufsize bytes */
322         len = cc > ioq->bufsize ? cc : ioq->bufsize;
323         ptr = malloc(len);
324         bcopy(buf, ptr, cc);
325         io = (struct io *)malloc(sizeof(struct io));
326         io->nbytes = cc;
327         io->size = len;
328         io->offset = 0;
329         io->data = ptr;
330         emp_insque(&io->queue, ioq->list.queue.q_back);
331         ioq->cc += cc;
332     }
333     return cc;
334 }
335
336 /*
337  * remove cc bytes from ioqueue ioq
338  * free memory, dequeue io elements
339  * which are no longer used.
340  */
341 static
342     int
343 removecc(struct ioqueue *ioq, register int cc)
344 {
345     struct io *io;
346     struct emp_qelem *qp;
347     register int nbytes;
348     register int there;
349     register int remain;
350
351     nbytes = 0;
352     remain = cc;
353     while ((qp = ioq->list.queue.q_forw) != &ioq->list.queue) {
354         io = (struct io *)qp;
355         there = io->nbytes - io->offset;
356         if (there < 0) {
357             /* error */
358             (void)emp_remque(&io->queue);
359             (void)free((s_char *)io);
360             continue;
361         }
362         if (remain >= there) {
363             /* not enough or exact; free entry */
364             nbytes += there;
365             remain -= there;
366             (void)emp_remque(&io->queue);
367             (void)free(io->data);
368             (void)free((s_char *)io);
369         } else {
370             /* too much; increment offset */
371             io->offset += remain;
372             nbytes += remain;
373             remain = 0;
374         }
375         if (remain <= 0)
376             break;
377     }
378     ioq->cc -= nbytes;
379     return nbytes;
380 }
381
382 #if defined(_WIN32)
383 /*
384  * Make an (output) buffer up to the
385  * maximum size of the buffer.
386  *
387  * We don't free the bytes...
388  */
389 int
390 ioq_makebuf(struct ioqueue *ioq, char *pBuf, int nBufLen)
391 {
392     struct io *io;
393     struct emp_qelem *qp;
394     struct emp_qelem *head;
395     int nbytes;
396     int nleft;
397     int ncopied;
398     s_char *offset;
399
400     ncopied = 0;
401     nleft = nBufLen;
402     offset = pBuf;
403     head = &ioq->list.queue;
404
405     for (qp = head->q_forw; (qp != head) && (nleft > 0); qp = qp->q_forw) {
406         io = (struct io *)qp;
407         nbytes = io->nbytes - io->offset;
408         if (nbytes < 0) {
409             /* Paranoid check for bad buffer. */
410             continue;
411         }
412
413         /* too many bytes, wait till next time. */
414         if (nbytes > nleft)
415             break;
416
417         bcopy(io->data + io->offset, offset, nbytes);
418         offset += nbytes;
419         nleft -= nbytes;
420         ncopied += nbytes;
421     }
422     return ncopied;
423 }
424 #endif /* _WIN32 */
425
426 #if defined(_WIN32)
427 /*
428  * Remove backspaces and DELs from the buffer.
429  *
430  * Why?  Because I got tired of telneting into the
431  * server and having to type perfectly...
432  */
433 static void
434 loc_StripDels(char *pBuf)
435 {
436     char *cp;
437     char *dp;
438     char *sp;
439
440     for (cp = pBuf; *cp;) {
441         /* Remove Backspace and DEL */
442         if (*cp == '\b' || *cp == '\x8F') {
443             if (cp == pBuf)
444                 dp = cp;
445             else
446                 dp = cp - 1;
447             sp = cp + 1;
448             while (*sp)
449                 *dp++ = *sp++;
450             *dp = 0;
451         } else
452             cp++;
453     }
454 }
455 #endif