Clean a bit - to be continued...
[seven-1.x.git] / libseven / linebuf.c
blobf8333995a0a663a8e24d330d2f16e8faf83c97ef
1 /*
2 * ircd-ratbox: A slightly useful ircd.
3 * linebuf.c: Maintains linebuffers.
5 * Copyright (C) 2001-2002 Adrian Chadd <adrian@creative.net.au>
6 * Copyright (C) 2002 Hybrid Development Team
7 * Copyright (C) 2002-2005 ircd-ratbox development team
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 * USA
26 #include "stdinc.h"
27 #include "tools.h"
28 #include "client.h"
29 #include "linebuf.h"
30 #include "memory.h"
31 #include "event.h"
32 #include "balloc.h"
33 #include "hook.h"
34 #include "commio.h"
35 #include "sprintf_irc.h"
37 #ifdef STRING_WITH_STRINGS
38 # include <string.h>
39 # include <strings.h>
40 #else
41 # ifdef HAVE_STRING_H
42 # include <string.h>
43 # else
44 # ifdef HAVE_STRINGS_H
45 # include <strings.h>
46 # endif
47 # endif
48 #endif
50 extern BlockHeap *linebuf_heap;
52 static int bufline_count = 0;
56 * linebuf_init
58 * Initialise the linebuf mechanism
62 void
63 linebuf_init(void)
65 linebuf_heap = BlockHeapCreate(sizeof(buf_line_t), LINEBUF_HEAP_SIZE);
68 static buf_line_t *
69 linebuf_allocate(void)
71 buf_line_t *t;
72 t = BlockHeapAlloc(linebuf_heap);
73 t->refcount = 0;
74 return (t);
78 static void
79 linebuf_free(buf_line_t * p)
81 BlockHeapFree(linebuf_heap, p);
85 * linebuf_new_line
87 * Create a new line, and link it to the given linebuf.
88 * It will be initially empty.
90 static buf_line_t *
91 linebuf_new_line(buf_head_t * bufhead)
93 buf_line_t *bufline;
94 dlink_node *node;
96 bufline = linebuf_allocate();
97 if(bufline == NULL)
98 return NULL;
99 ++bufline_count;
102 node = make_dlink_node();
104 bufline->len = 0;
105 bufline->terminated = 0;
106 bufline->flushing = 0;
107 bufline->raw = 0;
109 /* Stick it at the end of the buf list */
110 dlinkAddTail(bufline, node, &bufhead->list);
111 bufline->refcount++;
113 /* And finally, update the allocated size */
114 bufhead->alloclen++;
115 bufhead->numlines++;
117 return bufline;
122 * linebuf_done_line
124 * We've finished with the given line, so deallocate it
126 static void
127 linebuf_done_line(buf_head_t * bufhead, buf_line_t * bufline, dlink_node * node)
129 /* Remove it from the linked list */
130 dlinkDestroy(node, &bufhead->list);
132 /* Update the allocated size */
133 bufhead->alloclen--;
134 bufhead->len -= bufline->len;
135 s_assert(bufhead->len >= 0);
136 bufhead->numlines--;
138 bufline->refcount--;
139 s_assert(bufline->refcount >= 0);
141 if(bufline->refcount == 0)
143 /* and finally, deallocate the buf */
144 --bufline_count;
145 s_assert(bufline_count >= 0);
146 linebuf_free(bufline);
152 * skip to end of line or the crlfs, return the number of bytes ..
154 static inline int
155 linebuf_skip_crlf(char *ch, int len)
157 int orig_len = len;
159 /* First, skip until the first non-CRLF */
160 for (; len; len--, ch++)
162 if(*ch == '\r')
163 break;
164 else if(*ch == '\n')
165 break;
168 /* Then, skip until the last CRLF */
169 for (; len; len--, ch++)
171 if((*ch != '\r') && (*ch != '\n'))
172 break;
174 s_assert(orig_len > len);
175 return (orig_len - len);
181 * linebuf_newbuf
183 * Initialise the new buffer
185 void
186 linebuf_newbuf(buf_head_t * bufhead)
188 /* not much to do right now :) */
189 memset(bufhead, 0, sizeof(buf_head_t));
193 * client_flush_input
195 * inputs - pointer to client
196 * output - none
197 * side effects - all input line bufs are flushed
199 void
200 client_flush_input(struct Client *client_p)
202 /* This way, it can be called for remote client as well */
204 if(client_p->localClient == NULL)
205 return;
207 linebuf_donebuf(&client_p->localClient->buf_recvq);
212 * linebuf_donebuf
214 * Flush all the lines associated with this buffer
216 void
217 linebuf_donebuf(buf_head_t * bufhead)
219 while (bufhead->list.head != NULL)
221 linebuf_done_line(bufhead,
222 (buf_line_t *) bufhead->list.head->data, bufhead->list.head);
227 * linebuf_copy_line
229 * Okay..this functions comments made absolutely no sense.
231 * Basically what we do is this. Find the first chunk of text
232 * and then scan for a CRLF. If we didn't find it, but we didn't
233 * overflow our buffer..we wait for some more data.
234 * If we found a CRLF, we replace them with a \0 character.
235 * If we overflowed, we copy the most our buffer can handle, terminate
236 * it with a \0 and return.
238 * The return value is the amount of data we consumed. This could
239 * be different than the size of the linebuffer, as when we discard
240 * the overflow, we don't want to process it again.
242 * This still sucks in my opinion, but it seems to work.
244 * -Aaron
246 static int
247 linebuf_copy_line(buf_head_t * bufhead, buf_line_t * bufline, char *data, int len)
249 int cpylen = 0; /* how many bytes we've copied */
250 char *ch = data; /* Pointer to where we are in the read data */
251 char *bufch = bufline->buf + bufline->len;
252 int clen = 0; /* how many bytes we've processed,
253 and don't ever want to see again.. */
255 /* If its full or terminated, ignore it */
257 bufline->raw = 0;
258 s_assert(bufline->len < BUF_DATA_SIZE);
259 if(bufline->terminated == 1)
260 return 0;
262 clen = cpylen = linebuf_skip_crlf(ch, len);
263 if(clen == -1)
264 return -1;
266 /* This is the ~overflow case..This doesn't happen often.. */
267 if(cpylen > (BUF_DATA_SIZE - bufline->len - 1))
269 memcpy(bufch, ch, (BUF_DATA_SIZE - bufline->len - 1));
270 bufline->buf[BUF_DATA_SIZE - 1] = '\0';
271 bufch = bufline->buf + BUF_DATA_SIZE - 2;
272 while (cpylen && (*bufch == '\r' || *bufch == '\n'))
274 *bufch = '\0';
275 cpylen--;
276 bufch--;
278 bufline->terminated = 1;
279 bufline->len = BUF_DATA_SIZE - 1;
280 bufhead->len += BUF_DATA_SIZE - 1;
281 return clen;
284 memcpy(bufch, ch, cpylen);
285 bufch += cpylen;
286 *bufch = '\0';
287 bufch--;
289 if(*bufch != '\r' && *bufch != '\n')
291 /* No linefeed, bail for the next time */
292 bufhead->len += cpylen;
293 bufline->len += cpylen;
294 bufline->terminated = 0;
295 return clen;
298 /* Yank the CRLF off this, replace with a \0 */
299 while (cpylen && (*bufch == '\r' || *bufch == '\n'))
301 *bufch = '\0';
302 cpylen--;
303 bufch--;
306 bufline->terminated = 1;
307 bufhead->len += cpylen;
308 bufline->len += cpylen;
309 return clen;
313 * linebuf_copy_raw
315 * Copy as much data as possible directly into a linebuf,
316 * splitting at \r\n, but without altering any data.
319 static int
320 linebuf_copy_raw(buf_head_t * bufhead, buf_line_t * bufline, char *data, int len)
322 int cpylen = 0; /* how many bytes we've copied */
323 char *ch = data; /* Pointer to where we are in the read data */
324 char *bufch = bufline->buf + bufline->len;
325 int clen = 0; /* how many bytes we've processed,
326 and don't ever want to see again.. */
328 /* If its full or terminated, ignore it */
330 bufline->raw = 1;
331 s_assert(bufline->len < BUF_DATA_SIZE);
332 if(bufline->terminated == 1)
333 return 0;
335 clen = cpylen = linebuf_skip_crlf(ch, len);
336 if(clen == -1)
337 return -1;
339 /* This is the overflow case..This doesn't happen often.. */
340 if(cpylen > (BUF_DATA_SIZE - bufline->len - 1))
342 clen = BUF_DATA_SIZE - bufline->len - 1;
343 memcpy(bufch, ch, clen);
344 bufline->buf[BUF_DATA_SIZE - 1] = '\0';
345 bufch = bufline->buf + BUF_DATA_SIZE - 2;
346 bufline->terminated = 1;
347 bufline->len = BUF_DATA_SIZE - 1;
348 bufhead->len += BUF_DATA_SIZE - 1;
349 return clen;
352 memcpy(bufch, ch, cpylen);
353 bufch += cpylen;
354 *bufch = '\0';
355 bufch--;
357 if(*bufch != '\r' && *bufch != '\n')
359 /* No linefeed, bail for the next time */
360 bufhead->len += cpylen;
361 bufline->len += cpylen;
362 bufline->terminated = 0;
363 return clen;
366 bufline->terminated = 1;
367 bufhead->len += cpylen;
368 bufline->len += cpylen;
369 return clen;
374 * linebuf_parse
376 * Take a given buffer and break out as many buffers as we can.
377 * If we find a CRLF, we terminate that buffer and create a new one.
378 * If we don't find a CRLF whilst parsing a buffer, we don't mark it
379 * 'finished', so the next loop through we can continue appending ..
381 * A few notes here, which you'll need to understand before continuing.
383 * - right now I'm only dealing with single sized buffers. Later on,
384 * I might consider chaining buffers together to get longer "lines"
385 * but seriously, I don't see the advantage right now.
387 * - This *is* designed to turn into a reference-counter-protected setup
388 * to dodge copious copies.
391 linebuf_parse(buf_head_t * bufhead, char *data, int len, int raw)
393 buf_line_t *bufline;
394 int cpylen;
395 int linecnt = 0;
397 /* First, if we have a partial buffer, try to squeze data into it */
398 if(bufhead->list.tail != NULL)
400 /* Check we're doing the partial buffer thing */
401 bufline = bufhead->list.tail->data;
402 s_assert(!bufline->flushing);
403 /* just try, the worst it could do is *reject* us .. */
404 if(!raw)
405 cpylen = linebuf_copy_line(bufhead, bufline, data, len);
406 else
407 cpylen = linebuf_copy_raw(bufhead, bufline, data, len);
409 if(cpylen == -1)
410 return -1;
412 linecnt++;
413 /* If we've copied the same as what we've got, quit now */
414 if(cpylen == len)
415 return linecnt; /* all the data done so soon? */
417 /* Skip the data and update len .. */
418 len -= cpylen;
419 s_assert(len >= 0);
420 data += cpylen;
423 /* Next, the loop */
424 while (len > 0)
426 /* We obviously need a new buffer, so .. */
427 bufline = linebuf_new_line(bufhead);
429 /* And parse */
430 if(!raw)
431 cpylen = linebuf_copy_line(bufhead, bufline, data, len);
432 else
433 cpylen = linebuf_copy_raw(bufhead, bufline, data, len);
435 if(cpylen == -1)
436 return -1;
438 len -= cpylen;
439 s_assert(len >= 0);
440 data += cpylen;
441 linecnt++;
443 return linecnt;
448 * linebuf_get
450 * get the next buffer from our line. For the time being it will copy
451 * data into the given buffer and free the underlying linebuf.
454 linebuf_get(buf_head_t * bufhead, char *buf, int buflen, int partial, int raw)
456 buf_line_t *bufline;
457 int cpylen;
458 char *start, *ch;
460 /* make sure we have a line */
461 if(bufhead->list.head == NULL)
462 return 0; /* Obviously not.. hrm. */
464 bufline = bufhead->list.head->data;
466 /* make sure that the buffer was actually *terminated */
467 if(!(partial || bufline->terminated))
468 return 0; /* Wait for more data! */
470 /* make sure we've got the space, including the NULL */
471 cpylen = bufline->len;
472 s_assert(cpylen + 1 <= buflen);
474 /* Copy it */
475 start = bufline->buf;
477 /* if we left extraneous '\r\n' characters in the string,
478 * and we don't want to read the raw data, clean up the string.
480 if(bufline->raw && !raw)
482 /* skip leading EOL characters */
483 while (cpylen && (*start == '\r' || *start == '\n'))
485 start++;
486 cpylen--;
488 /* skip trailing EOL characters */
489 ch = &start[cpylen - 1];
490 while (cpylen && (*ch == '\r' || *ch == '\n'))
492 ch--;
493 cpylen--;
496 memcpy(buf, start, cpylen + 1);
498 /* convert CR/LF to NULL */
499 if(bufline->raw && !raw)
500 buf[cpylen] = '\0';
502 s_assert(cpylen >= 0);
504 /* Deallocate the line */
505 linebuf_done_line(bufhead, bufline, bufhead->list.head);
507 /* return how much we copied */
508 return cpylen;
512 * linebuf_attach
514 * attach the lines in a buf_head_t to another buf_head_t
515 * without copying the data (using refcounts).
517 void
518 linebuf_attach(buf_head_t * bufhead, buf_head_t * new)
520 dlink_node *ptr;
521 buf_line_t *line;
523 DLINK_FOREACH(ptr, new->list.head)
525 line = ptr->data;
526 dlinkAddTailAlloc(line, &bufhead->list);
528 /* Update the allocated size */
529 bufhead->alloclen++;
530 bufhead->len += line->len;
531 bufhead->numlines++;
533 line->refcount++;
538 * linebuf_putmsg
540 * Similar to linebuf_put, but designed for use by send.c.
542 * prefixfmt is used as a format for the varargs, and is inserted first.
543 * Then format/va_args is appended to the buffer.
545 void
546 linebuf_putmsg(buf_head_t * bufhead, const char *format, va_list * va_args,
547 const char *prefixfmt, ...)
549 buf_line_t *bufline;
550 int len = 0;
551 va_list prefix_args;
553 /* make sure the previous line is terminated */
554 #ifndef NDEBUG
555 if(bufhead->list.tail)
557 bufline = bufhead->list.tail->data;
558 s_assert(bufline->terminated);
560 #endif
561 /* Create a new line */
562 bufline = linebuf_new_line(bufhead);
564 if(prefixfmt != NULL)
566 va_start(prefix_args, prefixfmt);
567 len = ircvsnprintf(bufline->buf, BUF_DATA_SIZE, prefixfmt, prefix_args);
568 va_end(prefix_args);
571 if(va_args != NULL)
573 len += ircvsnprintf((bufline->buf + len), (BUF_DATA_SIZE - len), format, *va_args);
576 bufline->terminated = 1;
578 /* Truncate the data if required */
579 if(len > 510)
581 len = 510;
582 bufline->buf[len++] = '\r';
583 bufline->buf[len++] = '\n';
585 else if(len == 0)
587 bufline->buf[len++] = '\r';
588 bufline->buf[len++] = '\n';
589 bufline->buf[len] = '\0';
591 else
593 /* Chop trailing CRLF's .. */
594 while ((bufline->buf[len] == '\r')
595 || (bufline->buf[len] == '\n') || (bufline->buf[len] == '\0'))
597 len--;
600 bufline->buf[++len] = '\r';
601 bufline->buf[++len] = '\n';
602 bufline->buf[++len] = '\0';
605 bufline->len = len;
606 bufhead->len += len;
612 * linebuf_flush
614 * Flush data to the buffer. It tries to write as much data as possible
615 * to the given socket. Any return values are passed straight through.
616 * If there is no data in the socket, EWOULDBLOCK is set as an errno
617 * rather than returning 0 (which would map to an EOF..)
619 * Notes: XXX We *should* have a clue here when a non-full buffer is arrived.
620 * and tag it so that we don't re-schedule another write until
621 * we have a CRLF.
625 linebuf_flush(int fd, buf_head_t * bufhead)
627 buf_line_t *bufline;
628 int retval;
629 /* Check we actually have a first buffer */
630 if(bufhead->list.head == NULL)
632 /* nope, so we return none .. */
633 errno = EWOULDBLOCK;
634 return -1;
637 bufline = bufhead->list.head->data;
639 /* And that its actually full .. */
640 if(!bufline->terminated)
642 errno = EWOULDBLOCK;
643 return -1;
646 /* Check we're flushing the first buffer */
647 if(!bufline->flushing)
649 bufline->flushing = 1;
650 bufhead->writeofs = 0;
653 /* Now, try writing data */
654 retval = send(fd, bufline->buf + bufhead->writeofs, bufline->len - bufhead->writeofs, 0);
656 if(retval <= 0)
657 return retval;
659 /* we've got data, so update the write offset */
660 bufhead->writeofs += retval;
662 /* if we've written everything *and* the CRLF, deallocate and update
663 bufhead */
664 if(bufhead->writeofs == bufline->len)
666 bufhead->writeofs = 0;
667 s_assert(bufhead->len >= 0);
668 linebuf_done_line(bufhead, bufline, bufhead->list.head);
671 /* Return line length */
672 return retval;
678 * count linebufs for stats z
681 void
682 count_linebuf_memory(size_t * count, size_t * linebuf_memory_used)
684 BlockHeapUsage(linebuf_heap, count, NULL, linebuf_memory_used);