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
24 * $Id: linebuf.c 20772 2005-09-17 23:30:22Z androsyn $
36 #include "sprintf_irc.h"
38 #ifdef STRING_WITH_STRINGS
45 # ifdef HAVE_STRINGS_H
51 static BlockHeap
*linebuf_heap
;
53 static int bufline_count
= 0;
59 * Initialise the linebuf mechanism
66 linebuf_heap
= BlockHeapCreate(sizeof(buf_line_t
), LINEBUF_HEAP_SIZE
);
70 linebuf_allocate(void)
73 t
= BlockHeapAlloc(linebuf_heap
);
80 linebuf_free(buf_line_t
* p
)
82 BlockHeapFree(linebuf_heap
, p
);
88 * Create a new line, and link it to the given linebuf.
89 * It will be initially empty.
92 linebuf_new_line(buf_head_t
* bufhead
)
97 bufline
= linebuf_allocate();
103 node
= make_dlink_node();
106 bufline
->terminated
= 0;
107 bufline
->flushing
= 0;
110 /* Stick it at the end of the buf list */
111 dlinkAddTail(bufline
, node
, &bufhead
->list
);
114 /* And finally, update the allocated size */
125 * We've finished with the given line, so deallocate it
128 linebuf_done_line(buf_head_t
* bufhead
, buf_line_t
* bufline
, dlink_node
* node
)
130 /* Remove it from the linked list */
131 dlinkDestroy(node
, &bufhead
->list
);
133 /* Update the allocated size */
135 bufhead
->len
-= bufline
->len
;
136 s_assert(bufhead
->len
>= 0);
140 s_assert(bufline
->refcount
>= 0);
142 if(bufline
->refcount
== 0)
144 /* and finally, deallocate the buf */
146 s_assert(bufline_count
>= 0);
147 linebuf_free(bufline
);
153 * skip to end of line or the crlfs, return the number of bytes ..
156 linebuf_skip_crlf(char *ch
, int len
)
160 /* First, skip until the first non-CRLF */
161 for (; len
; len
--, ch
++)
169 /* Then, skip until the last CRLF */
170 for (; len
; len
--, ch
++)
172 if((*ch
!= '\r') && (*ch
!= '\n'))
175 s_assert(orig_len
> len
);
176 return (orig_len
- len
);
184 * Initialise the new buffer
187 linebuf_newbuf(buf_head_t
* bufhead
)
189 /* not much to do right now :) */
190 memset(bufhead
, 0, sizeof(buf_head_t
));
196 * inputs - pointer to client
198 * side effects - all input line bufs are flushed
201 client_flush_input(struct Client
*client_p
)
203 /* This way, it can be called for remote client as well */
205 if(client_p
->localClient
== NULL
)
208 linebuf_donebuf(&client_p
->localClient
->buf_recvq
);
215 * Flush all the lines associated with this buffer
218 linebuf_donebuf(buf_head_t
* bufhead
)
220 while (bufhead
->list
.head
!= NULL
)
222 linebuf_done_line(bufhead
,
223 (buf_line_t
*) bufhead
->list
.head
->data
, bufhead
->list
.head
);
230 * Okay..this functions comments made absolutely no sense.
232 * Basically what we do is this. Find the first chunk of text
233 * and then scan for a CRLF. If we didn't find it, but we didn't
234 * overflow our buffer..we wait for some more data.
235 * If we found a CRLF, we replace them with a \0 character.
236 * If we overflowed, we copy the most our buffer can handle, terminate
237 * it with a \0 and return.
239 * The return value is the amount of data we consumed. This could
240 * be different than the size of the linebuffer, as when we discard
241 * the overflow, we don't want to process it again.
243 * This still sucks in my opinion, but it seems to work.
248 linebuf_copy_line(buf_head_t
* bufhead
, buf_line_t
* bufline
, char *data
, int len
)
250 int cpylen
= 0; /* how many bytes we've copied */
251 char *ch
= data
; /* Pointer to where we are in the read data */
252 char *bufch
= bufline
->buf
+ bufline
->len
;
253 int clen
= 0; /* how many bytes we've processed,
254 and don't ever want to see again.. */
256 /* If its full or terminated, ignore it */
259 s_assert(bufline
->len
< BUF_DATA_SIZE
);
260 if(bufline
->terminated
== 1)
263 clen
= cpylen
= linebuf_skip_crlf(ch
, len
);
267 /* This is the ~overflow case..This doesn't happen often.. */
268 if(cpylen
> (BUF_DATA_SIZE
- bufline
->len
- 1))
270 memcpy(bufch
, ch
, (BUF_DATA_SIZE
- bufline
->len
- 1));
271 bufline
->buf
[BUF_DATA_SIZE
- 1] = '\0';
272 bufch
= bufline
->buf
+ BUF_DATA_SIZE
- 2;
273 while (cpylen
&& (*bufch
== '\r' || *bufch
== '\n'))
279 bufline
->terminated
= 1;
280 bufline
->len
= BUF_DATA_SIZE
- 1;
281 bufhead
->len
+= BUF_DATA_SIZE
- 1;
285 memcpy(bufch
, ch
, cpylen
);
290 if(*bufch
!= '\r' && *bufch
!= '\n')
292 /* No linefeed, bail for the next time */
293 bufhead
->len
+= cpylen
;
294 bufline
->len
+= cpylen
;
295 bufline
->terminated
= 0;
299 /* Yank the CRLF off this, replace with a \0 */
300 while (cpylen
&& (*bufch
== '\r' || *bufch
== '\n'))
307 bufline
->terminated
= 1;
308 bufhead
->len
+= cpylen
;
309 bufline
->len
+= cpylen
;
316 * Copy as much data as possible directly into a linebuf,
317 * splitting at \r\n, but without altering any data.
321 linebuf_copy_raw(buf_head_t
* bufhead
, buf_line_t
* bufline
, char *data
, int len
)
323 int cpylen
= 0; /* how many bytes we've copied */
324 char *ch
= data
; /* Pointer to where we are in the read data */
325 char *bufch
= bufline
->buf
+ bufline
->len
;
326 int clen
= 0; /* how many bytes we've processed,
327 and don't ever want to see again.. */
329 /* If its full or terminated, ignore it */
332 s_assert(bufline
->len
< BUF_DATA_SIZE
);
333 if(bufline
->terminated
== 1)
336 clen
= cpylen
= linebuf_skip_crlf(ch
, len
);
340 /* This is the overflow case..This doesn't happen often.. */
341 if(cpylen
> (BUF_DATA_SIZE
- bufline
->len
- 1))
343 clen
= BUF_DATA_SIZE
- bufline
->len
- 1;
344 memcpy(bufch
, ch
, clen
);
345 bufline
->buf
[BUF_DATA_SIZE
- 1] = '\0';
346 bufch
= bufline
->buf
+ BUF_DATA_SIZE
- 2;
347 bufline
->terminated
= 1;
348 bufline
->len
= BUF_DATA_SIZE
- 1;
349 bufhead
->len
+= BUF_DATA_SIZE
- 1;
353 memcpy(bufch
, ch
, cpylen
);
358 if(*bufch
!= '\r' && *bufch
!= '\n')
360 /* No linefeed, bail for the next time */
361 bufhead
->len
+= cpylen
;
362 bufline
->len
+= cpylen
;
363 bufline
->terminated
= 0;
367 bufline
->terminated
= 1;
368 bufhead
->len
+= cpylen
;
369 bufline
->len
+= cpylen
;
377 * Take a given buffer and break out as many buffers as we can.
378 * If we find a CRLF, we terminate that buffer and create a new one.
379 * If we don't find a CRLF whilst parsing a buffer, we don't mark it
380 * 'finished', so the next loop through we can continue appending ..
382 * A few notes here, which you'll need to understand before continuing.
384 * - right now I'm only dealing with single sized buffers. Later on,
385 * I might consider chaining buffers together to get longer "lines"
386 * but seriously, I don't see the advantage right now.
388 * - This *is* designed to turn into a reference-counter-protected setup
389 * to dodge copious copies.
392 linebuf_parse(buf_head_t
* bufhead
, char *data
, int len
, int raw
)
398 /* First, if we have a partial buffer, try to squeze data into it */
399 if(bufhead
->list
.tail
!= NULL
)
401 /* Check we're doing the partial buffer thing */
402 bufline
= bufhead
->list
.tail
->data
;
403 s_assert(!bufline
->flushing
);
404 /* just try, the worst it could do is *reject* us .. */
406 cpylen
= linebuf_copy_line(bufhead
, bufline
, data
, len
);
408 cpylen
= linebuf_copy_raw(bufhead
, bufline
, data
, len
);
414 /* If we've copied the same as what we've got, quit now */
416 return linecnt
; /* all the data done so soon? */
418 /* Skip the data and update len .. */
427 /* We obviously need a new buffer, so .. */
428 bufline
= linebuf_new_line(bufhead
);
432 cpylen
= linebuf_copy_line(bufhead
, bufline
, data
, len
);
434 cpylen
= linebuf_copy_raw(bufhead
, bufline
, data
, len
);
451 * get the next buffer from our line. For the time being it will copy
452 * data into the given buffer and free the underlying linebuf.
455 linebuf_get(buf_head_t
* bufhead
, char *buf
, int buflen
, int partial
, int raw
)
461 /* make sure we have a line */
462 if(bufhead
->list
.head
== NULL
)
463 return 0; /* Obviously not.. hrm. */
465 bufline
= bufhead
->list
.head
->data
;
467 /* make sure that the buffer was actually *terminated */
468 if(!(partial
|| bufline
->terminated
))
469 return 0; /* Wait for more data! */
471 /* make sure we've got the space, including the NULL */
472 cpylen
= bufline
->len
;
473 s_assert(cpylen
+ 1 <= buflen
);
476 start
= bufline
->buf
;
478 /* if we left extraneous '\r\n' characters in the string,
479 * and we don't want to read the raw data, clean up the string.
481 if(bufline
->raw
&& !raw
)
483 /* skip leading EOL characters */
484 while (cpylen
&& (*start
== '\r' || *start
== '\n'))
489 /* skip trailing EOL characters */
490 ch
= &start
[cpylen
- 1];
491 while (cpylen
&& (*ch
== '\r' || *ch
== '\n'))
497 memcpy(buf
, start
, cpylen
+ 1);
499 /* convert CR/LF to NULL */
500 if(bufline
->raw
&& !raw
)
503 s_assert(cpylen
>= 0);
505 /* Deallocate the line */
506 linebuf_done_line(bufhead
, bufline
, bufhead
->list
.head
);
508 /* return how much we copied */
515 * attach the lines in a buf_head_t to another buf_head_t
516 * without copying the data (using refcounts).
519 linebuf_attach(buf_head_t
* bufhead
, buf_head_t
* new)
524 DLINK_FOREACH(ptr
, new->list
.head
)
527 dlinkAddTailAlloc(line
, &bufhead
->list
);
529 /* Update the allocated size */
531 bufhead
->len
+= line
->len
;
541 * Similar to linebuf_put, but designed for use by send.c.
543 * prefixfmt is used as a format for the varargs, and is inserted first.
544 * Then format/va_args is appended to the buffer.
547 linebuf_putmsg(buf_head_t
* bufhead
, const char *format
, va_list * va_args
,
548 const char *prefixfmt
, ...)
554 /* make sure the previous line is terminated */
556 if(bufhead
->list
.tail
)
558 bufline
= bufhead
->list
.tail
->data
;
559 s_assert(bufline
->terminated
);
562 /* Create a new line */
563 bufline
= linebuf_new_line(bufhead
);
565 if(prefixfmt
!= NULL
)
567 va_start(prefix_args
, prefixfmt
);
568 len
= ircvsnprintf(bufline
->buf
, BUF_DATA_SIZE
, prefixfmt
, prefix_args
);
574 len
+= ircvsnprintf((bufline
->buf
+ len
), (BUF_DATA_SIZE
- len
), format
, *va_args
);
577 bufline
->terminated
= 1;
579 /* Truncate the data if required */
583 bufline
->buf
[len
++] = '\r';
584 bufline
->buf
[len
++] = '\n';
588 bufline
->buf
[len
++] = '\r';
589 bufline
->buf
[len
++] = '\n';
590 bufline
->buf
[len
] = '\0';
594 /* Chop trailing CRLF's .. */
595 while ((bufline
->buf
[len
] == '\r')
596 || (bufline
->buf
[len
] == '\n') || (bufline
->buf
[len
] == '\0'))
601 bufline
->buf
[++len
] = '\r';
602 bufline
->buf
[++len
] = '\n';
603 bufline
->buf
[++len
] = '\0';
615 * Flush data to the buffer. It tries to write as much data as possible
616 * to the given socket. Any return values are passed straight through.
617 * If there is no data in the socket, EWOULDBLOCK is set as an errno
618 * rather than returning 0 (which would map to an EOF..)
620 * Notes: XXX We *should* have a clue here when a non-full buffer is arrived.
621 * and tag it so that we don't re-schedule another write until
626 linebuf_flush(int fd
, buf_head_t
* bufhead
)
630 /* Check we actually have a first buffer */
631 if(bufhead
->list
.head
== NULL
)
633 /* nope, so we return none .. */
638 bufline
= bufhead
->list
.head
->data
;
640 /* And that its actually full .. */
641 if(!bufline
->terminated
)
647 /* Check we're flushing the first buffer */
648 if(!bufline
->flushing
)
650 bufline
->flushing
= 1;
651 bufhead
->writeofs
= 0;
654 /* Now, try writing data */
655 retval
= send(fd
, bufline
->buf
+ bufhead
->writeofs
, bufline
->len
- bufhead
->writeofs
, 0);
660 /* we've got data, so update the write offset */
661 bufhead
->writeofs
+= retval
;
663 /* if we've written everything *and* the CRLF, deallocate and update
665 if(bufhead
->writeofs
== bufline
->len
)
667 bufhead
->writeofs
= 0;
668 s_assert(bufhead
->len
>= 0);
669 linebuf_done_line(bufhead
, bufline
, bufhead
->list
.head
);
672 /* Return line length */
679 * count linebufs for stats z
683 count_linebuf_memory(size_t * count
, size_t * linebuf_memory_used
)
685 BlockHeapUsage(linebuf_heap
, count
, NULL
, linebuf_memory_used
);