]> jfr.im git - solanum.git/blame - libratbox/src/linebuf.c
WHOIS: Make hide_opers_in_whois not affect opers doing whois.
[solanum.git] / libratbox / src / linebuf.c
CommitLineData
db137867
AC
1/*
2 * ircd-ratbox: A slightly useful ircd.
3 * linebuf.c: Maintains linebuffers.
4 *
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
8 *
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.
13 *
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.
18 *
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
22 * USA
23 *
3202e249 24 * $Id: linebuf.c 26092 2008-09-19 15:13:52Z androsyn $
db137867
AC
25 */
26
27#include <libratbox_config.h>
28#include <ratbox_lib.h>
29#include <commio-int.h>
30
db137867 31static rb_bh *rb_linebuf_heap;
db137867
AC
32
33static int bufline_count = 0;
34
35#ifndef LINEBUF_HEAP_SIZE
36#define LINEBUF_HEAP_SIZE 2048
37#endif
38
39/*
40 * rb_linebuf_init
41 *
42 * Initialise the linebuf mechanism
43 */
44
45void
46rb_linebuf_init(size_t heap_size)
47{
db137867 48 rb_linebuf_heap = rb_bh_create(sizeof(buf_line_t), heap_size, "librb_linebuf_heap");
db137867
AC
49}
50
51static buf_line_t *
52rb_linebuf_allocate(void)
53{
54 buf_line_t *t;
db137867 55 t = rb_bh_alloc(rb_linebuf_heap);
db137867
AC
56 return (t);
57
58}
59
60static void
61rb_linebuf_free(buf_line_t * p)
62{
db137867 63 rb_bh_free(rb_linebuf_heap, p);
db137867
AC
64}
65
66/*
67 * rb_linebuf_new_line
68 *
69 * Create a new line, and link it to the given linebuf.
70 * It will be initially empty.
71 */
72static buf_line_t *
73rb_linebuf_new_line(buf_head_t * bufhead)
74{
75 buf_line_t *bufline;
76 rb_dlink_node *node;
77
78 bufline = rb_linebuf_allocate();
79 if(bufline == NULL)
80 return NULL;
81 ++bufline_count;
82
83
84 node = rb_make_rb_dlink_node();
85
86 /* Stick it at the end of the buf list */
87 rb_dlinkAddTail(bufline, node, &bufhead->list);
88 bufline->refcount++;
89
90 /* And finally, update the allocated size */
91 bufhead->alloclen++;
92 bufhead->numlines++;
93
94 return bufline;
95}
96
97
98/*
99 * rb_linebuf_done_line
100 *
101 * We've finished with the given line, so deallocate it
102 */
103static void
3202e249 104rb_linebuf_done_line(buf_head_t * bufhead, buf_line_t * bufline, rb_dlink_node *node)
db137867
AC
105{
106 /* Remove it from the linked list */
107 rb_dlinkDestroy(node, &bufhead->list);
108
109 /* Update the allocated size */
110 bufhead->alloclen--;
111 bufhead->len -= bufline->len;
112 lrb_assert(bufhead->len >= 0);
113 bufhead->numlines--;
114
115 bufline->refcount--;
116 lrb_assert(bufline->refcount >= 0);
117
118 if(bufline->refcount == 0)
119 {
120 /* and finally, deallocate the buf */
121 --bufline_count;
122 lrb_assert(bufline_count >= 0);
123 rb_linebuf_free(bufline);
124 }
125}
126
127
128/*
129 * skip to end of line or the crlfs, return the number of bytes ..
130 */
131static inline int
132rb_linebuf_skip_crlf(char *ch, int len)
133{
134 int orig_len = len;
135
136 /* First, skip until the first non-CRLF */
137 for(; len; len--, ch++)
138 {
139 if(*ch == '\r')
140 break;
141 else if(*ch == '\n')
142 break;
143 }
144
145 /* Then, skip until the last CRLF */
146 for(; len; len--, ch++)
147 {
148 if((*ch != '\r') && (*ch != '\n'))
149 break;
150 }
151 lrb_assert(orig_len > len);
152 return (orig_len - len);
153}
154
155
156
157/*
158 * rb_linebuf_newbuf
159 *
160 * Initialise the new buffer
161 */
162void
163rb_linebuf_newbuf(buf_head_t * bufhead)
164{
165 /* not much to do right now :) */
166 memset(bufhead, 0, sizeof(buf_head_t));
167}
168
169/*
170 * rb_linebuf_donebuf
171 *
172 * Flush all the lines associated with this buffer
173 */
174void
175rb_linebuf_donebuf(buf_head_t * bufhead)
176{
177 while(bufhead->list.head != NULL)
178 {
3202e249
VY
179 rb_linebuf_done_line(bufhead, (buf_line_t *) bufhead->list.head->data,
180 bufhead->list.head);
db137867
AC
181 }
182}
183
184/*
185 * rb_linebuf_copy_line
186 *
187 * Okay..this functions comments made absolutely no sense.
55abcbb2 188 *
db137867
AC
189 * Basically what we do is this. Find the first chunk of text
190 * and then scan for a CRLF. If we didn't find it, but we didn't
191 * overflow our buffer..we wait for some more data.
192 * If we found a CRLF, we replace them with a \0 character.
193 * If we overflowed, we copy the most our buffer can handle, terminate
194 * it with a \0 and return.
195 *
196 * The return value is the amount of data we consumed. This could
197 * be different than the size of the linebuffer, as when we discard
198 * the overflow, we don't want to process it again.
199 *
200 * This still sucks in my opinion, but it seems to work.
201 *
202 * -Aaron
203 */
204static int
205rb_linebuf_copy_line(buf_head_t * bufhead, buf_line_t * bufline, char *data, int len)
206{
207 int cpylen = 0; /* how many bytes we've copied */
208 char *ch = data; /* Pointer to where we are in the read data */
209 char *bufch = bufline->buf + bufline->len;
210 int clen = 0; /* how many bytes we've processed,
211 and don't ever want to see again.. */
212
213 /* If its full or terminated, ignore it */
214
215 bufline->raw = 0;
216 lrb_assert(bufline->len < BUF_DATA_SIZE);
217 if(bufline->terminated == 1)
218 return 0;
219
220 clen = cpylen = rb_linebuf_skip_crlf(ch, len);
221 if(clen == -1)
222 return -1;
223
224 /* This is the ~overflow case..This doesn't happen often.. */
225 if(cpylen > (BUF_DATA_SIZE - bufline->len - 1))
226 {
1c864688
JT
227 cpylen = BUF_DATA_SIZE - bufline->len - 1;
228 memcpy(bufch, ch, cpylen);
db137867
AC
229 bufline->buf[BUF_DATA_SIZE - 1] = '\0';
230 bufch = bufline->buf + BUF_DATA_SIZE - 2;
231 while(cpylen && (*bufch == '\r' || *bufch == '\n'))
232 {
233 *bufch = '\0';
234 cpylen--;
235 bufch--;
236 }
237 bufline->terminated = 1;
238 bufline->len = BUF_DATA_SIZE - 1;
239 bufhead->len += BUF_DATA_SIZE - 1;
240 return clen;
241 }
242
243 memcpy(bufch, ch, cpylen);
244 bufch += cpylen;
245 *bufch = '\0';
246 bufch--;
247
248 if(*bufch != '\r' && *bufch != '\n')
249 {
250 /* No linefeed, bail for the next time */
251 bufhead->len += cpylen;
252 bufline->len += cpylen;
253 bufline->terminated = 0;
254 return clen;
255 }
256
257 /* Yank the CRLF off this, replace with a \0 */
258 while(cpylen && (*bufch == '\r' || *bufch == '\n'))
259 {
260 *bufch = '\0';
261 cpylen--;
262 bufch--;
263 }
264
265 bufline->terminated = 1;
266 bufhead->len += cpylen;
267 bufline->len += cpylen;
268 return clen;
269}
270
271/*
272 * rb_linebuf_copy_raw
273 *
274 * Copy as much data as possible directly into a linebuf,
275 * splitting at \r\n, but without altering any data.
276 *
277 */
278static int
279rb_linebuf_copy_raw(buf_head_t * bufhead, buf_line_t * bufline, char *data, int len)
280{
281 int cpylen = 0; /* how many bytes we've copied */
282 char *ch = data; /* Pointer to where we are in the read data */
283 char *bufch = bufline->buf + bufline->len;
284 int clen = 0; /* how many bytes we've processed,
285 and don't ever want to see again.. */
286
287 /* If its full or terminated, ignore it */
288
289 bufline->raw = 1;
290 lrb_assert(bufline->len < BUF_DATA_SIZE);
291 if(bufline->terminated == 1)
292 return 0;
293
294 clen = cpylen = rb_linebuf_skip_crlf(ch, len);
295 if(clen == -1)
296 return -1;
297
298 /* This is the overflow case..This doesn't happen often.. */
299 if(cpylen > (BUF_DATA_SIZE - bufline->len - 1))
300 {
301 clen = BUF_DATA_SIZE - bufline->len - 1;
302 memcpy(bufch, ch, clen);
303 bufline->buf[BUF_DATA_SIZE - 1] = '\0';
304 bufch = bufline->buf + BUF_DATA_SIZE - 2;
305 bufline->terminated = 1;
306 bufline->len = BUF_DATA_SIZE - 1;
307 bufhead->len += BUF_DATA_SIZE - 1;
308 return clen;
309 }
310
311 memcpy(bufch, ch, cpylen);
312 bufch += cpylen;
313 *bufch = '\0';
314 bufch--;
315
316 if(*bufch != '\r' && *bufch != '\n')
317 {
318 /* No linefeed, bail for the next time */
319 bufhead->len += cpylen;
320 bufline->len += cpylen;
321 bufline->terminated = 0;
322 return clen;
323 }
324
325 bufline->terminated = 1;
326 bufhead->len += cpylen;
327 bufline->len += cpylen;
328 return clen;
329}
330
331
332/*
333 * rb_linebuf_parse
334 *
335 * Take a given buffer and break out as many buffers as we can.
336 * If we find a CRLF, we terminate that buffer and create a new one.
337 * If we don't find a CRLF whilst parsing a buffer, we don't mark it
338 * 'finished', so the next loop through we can continue appending ..
339 *
340 * A few notes here, which you'll need to understand before continuing.
341 *
342 * - right now I'm only dealing with single sized buffers. Later on,
343 * I might consider chaining buffers together to get longer "lines"
344 * but seriously, I don't see the advantage right now.
345 *
346 * - This *is* designed to turn into a reference-counter-protected setup
347 * to dodge copious copies.
348 */
349int
350rb_linebuf_parse(buf_head_t * bufhead, char *data, int len, int raw)
351{
352 buf_line_t *bufline;
353 int cpylen;
354 int linecnt = 0;
355
356 /* First, if we have a partial buffer, try to squeze data into it */
357 if(bufhead->list.tail != NULL)
358 {
359 /* Check we're doing the partial buffer thing */
360 bufline = bufhead->list.tail->data;
db137867
AC
361 /* just try, the worst it could do is *reject* us .. */
362 if(!raw)
363 cpylen = rb_linebuf_copy_line(bufhead, bufline, data, len);
364 else
365 cpylen = rb_linebuf_copy_raw(bufhead, bufline, data, len);
366
367 if(cpylen == -1)
368 return -1;
369
370 linecnt++;
371 /* If we've copied the same as what we've got, quit now */
372 if(cpylen == len)
373 return linecnt; /* all the data done so soon? */
374
375 /* Skip the data and update len .. */
376 len -= cpylen;
377 lrb_assert(len >= 0);
378 data += cpylen;
379 }
380
381 /* Next, the loop */
382 while(len > 0)
383 {
384 /* We obviously need a new buffer, so .. */
385 bufline = rb_linebuf_new_line(bufhead);
386
387 /* And parse */
388 if(!raw)
389 cpylen = rb_linebuf_copy_line(bufhead, bufline, data, len);
390 else
391 cpylen = rb_linebuf_copy_raw(bufhead, bufline, data, len);
392
393 if(cpylen == -1)
394 return -1;
395
396 len -= cpylen;
397 lrb_assert(len >= 0);
398 data += cpylen;
399 linecnt++;
400 }
401 return linecnt;
402}
403
404
405/*
406 * rb_linebuf_get
407 *
408 * get the next buffer from our line. For the time being it will copy
409 * data into the given buffer and free the underlying linebuf.
410 */
411int
412rb_linebuf_get(buf_head_t * bufhead, char *buf, int buflen, int partial, int raw)
413{
414 buf_line_t *bufline;
415 int cpylen;
416 char *start, *ch;
417
418 /* make sure we have a line */
419 if(bufhead->list.head == NULL)
420 return 0; /* Obviously not.. hrm. */
421
422 bufline = bufhead->list.head->data;
423
424 /* make sure that the buffer was actually *terminated */
425 if(!(partial || bufline->terminated))
426 return 0; /* Wait for more data! */
427
428 if(buflen < bufline->len)
429 cpylen = buflen - 1;
430 else
431 cpylen = bufline->len;
432
433 /* Copy it */
434 start = bufline->buf;
435
436 /* if we left extraneous '\r\n' characters in the string,
437 * and we don't want to read the raw data, clean up the string.
438 */
439 if(bufline->raw && !raw)
440 {
441 /* skip leading EOL characters */
442 while(cpylen && (*start == '\r' || *start == '\n'))
443 {
444 start++;
445 cpylen--;
446 }
447 /* skip trailing EOL characters */
448 ch = &start[cpylen - 1];
449 while(cpylen && (*ch == '\r' || *ch == '\n'))
450 {
451 ch--;
452 cpylen--;
453 }
454 }
455
456 memcpy(buf, start, cpylen);
457
458 /* convert CR/LF to NULL */
459 if(!raw)
460 buf[cpylen] = '\0';
461
462 lrb_assert(cpylen >= 0);
463
464 /* Deallocate the line */
465 rb_linebuf_done_line(bufhead, bufline, bufhead->list.head);
466
467 /* return how much we copied */
468 return cpylen;
469}
470
471/*
472 * rb_linebuf_attach
473 *
474 * attach the lines in a buf_head_t to another buf_head_t
475 * without copying the data (using refcounts).
476 */
477void
478rb_linebuf_attach(buf_head_t * bufhead, buf_head_t * new)
479{
480 rb_dlink_node *ptr;
481 buf_line_t *line;
482
483 RB_DLINK_FOREACH(ptr, new->list.head)
484 {
485 line = ptr->data;
486 rb_dlinkAddTailAlloc(line, &bufhead->list);
487
488 /* Update the allocated size */
489 bufhead->alloclen++;
490 bufhead->len += line->len;
491 bufhead->numlines++;
492
493 line->refcount++;
494 }
495}
496
497
498
499/*
500 * rb_linebuf_putmsg
501 *
502 * Similar to rb_linebuf_put, but designed for use by send.c.
503 *
504 * prefixfmt is used as a format for the varargs, and is inserted first.
505 * Then format/va_args is appended to the buffer.
506 */
507void
3202e249
VY
508rb_linebuf_putmsg(buf_head_t * bufhead, const char *format, va_list * va_args,
509 const char *prefixfmt, ...)
db137867
AC
510{
511 buf_line_t *bufline;
512 int len = 0;
513 va_list prefix_args;
514
515 /* make sure the previous line is terminated */
516#ifndef NDEBUG
517 if(bufhead->list.tail)
518 {
519 bufline = bufhead->list.tail->data;
520 lrb_assert(bufline->terminated);
521 }
522#endif
523 /* Create a new line */
524 bufline = rb_linebuf_new_line(bufhead);
525
526 if(prefixfmt != NULL)
527 {
528 va_start(prefix_args, prefixfmt);
529 len = rb_vsnprintf(bufline->buf, BUF_DATA_SIZE, prefixfmt, prefix_args);
530 va_end(prefix_args);
531 }
532
533 if(va_args != NULL)
534 {
535 len += rb_vsnprintf((bufline->buf + len), (BUF_DATA_SIZE - len), format, *va_args);
536 }
537
538 bufline->terminated = 1;
539
540 /* Truncate the data if required */
c2ac22cc 541 if(rb_unlikely(len > 510))
db137867
AC
542 {
543 len = 510;
544 bufline->buf[len++] = '\r';
545 bufline->buf[len++] = '\n';
546 }
c2ac22cc 547 else if(rb_unlikely(len == 0))
db137867
AC
548 {
549 bufline->buf[len++] = '\r';
550 bufline->buf[len++] = '\n';
551 bufline->buf[len] = '\0';
552 }
553 else
554 {
555 /* Chop trailing CRLF's .. */
3202e249
VY
556 while((bufline->buf[len] == '\r') || (bufline->buf[len] == '\n')
557 || (bufline->buf[len] == '\0'))
db137867
AC
558 {
559 len--;
560 }
561
562 bufline->buf[++len] = '\r';
563 bufline->buf[++len] = '\n';
564 bufline->buf[++len] = '\0';
565 }
566
567 bufline->len = len;
568 bufhead->len += len;
569}
570
571void
3202e249 572rb_linebuf_putbuf(buf_head_t * bufhead, const char *buffer)
db137867
AC
573{
574 buf_line_t *bufline;
575 int len = 0;
576
577 /* make sure the previous line is terminated */
578#ifndef NDEBUG
579 if(bufhead->list.tail)
580 {
581 bufline = bufhead->list.tail->data;
582 lrb_assert(bufline->terminated);
583 }
584#endif
585 /* Create a new line */
586 bufline = rb_linebuf_new_line(bufhead);
587
c2ac22cc 588 if(rb_unlikely(buffer != NULL))
db137867
AC
589 len = rb_strlcpy(bufline->buf, buffer, BUF_DATA_SIZE);
590
591 bufline->terminated = 1;
592
593 /* Truncate the data if required */
c2ac22cc 594 if(rb_unlikely(len > 510))
db137867
AC
595 {
596 len = 510;
597 bufline->buf[len++] = '\r';
598 bufline->buf[len++] = '\n';
599 }
c2ac22cc 600 else if(rb_unlikely(len == 0))
db137867
AC
601 {
602 bufline->buf[len++] = '\r';
603 bufline->buf[len++] = '\n';
604 bufline->buf[len] = '\0';
605 }
606 else
607 {
608 /* Chop trailing CRLF's .. */
3202e249
VY
609 while((bufline->buf[len] == '\r') || (bufline->buf[len] == '\n')
610 || (bufline->buf[len] == '\0'))
db137867
AC
611 {
612 len--;
613 }
614
615 bufline->buf[++len] = '\r';
616 bufline->buf[++len] = '\n';
617 bufline->buf[++len] = '\0';
618 }
619
620 bufline->len = len;
621 bufhead->len += len;
622
3202e249 623
db137867
AC
624}
625
626void
627rb_linebuf_put(buf_head_t * bufhead, const char *format, ...)
628{
629 buf_line_t *bufline;
630 int len = 0;
631 va_list args;
632
633 /* make sure the previous line is terminated */
634#ifndef NDEBUG
635 if(bufhead->list.tail)
636 {
637 bufline = bufhead->list.tail->data;
638 lrb_assert(bufline->terminated);
639 }
640#endif
641 /* Create a new line */
642 bufline = rb_linebuf_new_line(bufhead);
643
c2ac22cc 644 if(rb_unlikely(format != NULL))
db137867
AC
645 {
646 va_start(args, format);
647 len = rb_vsnprintf(bufline->buf, BUF_DATA_SIZE, format, args);
648 va_end(args);
649 }
650
651 bufline->terminated = 1;
652
653 /* Truncate the data if required */
c2ac22cc 654 if(rb_unlikely(len > 510))
db137867
AC
655 {
656 len = 510;
657 bufline->buf[len++] = '\r';
658 bufline->buf[len++] = '\n';
659 }
c2ac22cc 660 else if(rb_unlikely(len == 0))
db137867
AC
661 {
662 bufline->buf[len++] = '\r';
663 bufline->buf[len++] = '\n';
664 bufline->buf[len] = '\0';
665 }
666 else
667 {
668 /* Chop trailing CRLF's .. */
3202e249
VY
669 while((bufline->buf[len] == '\r') || (bufline->buf[len] == '\n')
670 || (bufline->buf[len] == '\0'))
db137867
AC
671 {
672 len--;
673 }
674
675 bufline->buf[++len] = '\r';
676 bufline->buf[++len] = '\n';
677 bufline->buf[++len] = '\0';
678 }
679
680 bufline->len = len;
681 bufhead->len += len;
682}
683
684
685
686/*
687 * rb_linebuf_flush
688 *
689 * Flush data to the buffer. It tries to write as much data as possible
690 * to the given socket. Any return values are passed straight through.
691 * If there is no data in the socket, EWOULDBLOCK is set as an errno
692 * rather than returning 0 (which would map to an EOF..)
693 *
694 * Notes: XXX We *should* have a clue here when a non-full buffer is arrived.
695 * and tag it so that we don't re-schedule another write until
696 * we have a CRLF.
697 */
698int
699rb_linebuf_flush(rb_fde_t *F, buf_head_t * bufhead)
700{
701 buf_line_t *bufline;
702 int retval;
703
55abcbb2
KB
704/*
705 * autoconf checks for this..but really just want to use it if we have a
db137867
AC
706 * native version even if libircd provides a fake version...
707 */
708#ifdef HAVE_WRITEV
709 if(!rb_fd_ssl(F))
710 {
711 rb_dlink_node *ptr;
712 int x = 0, y;
713 int xret;
714 static struct rb_iovec vec[RB_UIO_MAXIOV];
715
716 memset(vec, 0, sizeof(vec));
717 /* Check we actually have a first buffer */
718 if(bufhead->list.head == NULL)
719 {
720 /* nope, so we return none .. */
721 errno = EWOULDBLOCK;
722 return -1;
723 }
724
725 ptr = bufhead->list.head;
3202e249 726
db137867
AC
727 bufline = ptr->data;
728 if(!bufline->terminated)
729 {
730 errno = EWOULDBLOCK;
731 return -1;
732
733 }
734
39930c66
JT
735 vec[x].iov_base = bufline->buf + bufhead->writeofs;
736 vec[x++].iov_len = bufline->len - bufhead->writeofs;
737 ptr = ptr->next;
db137867
AC
738
739 do
740 {
741 if(ptr == NULL)
742 break;
743
744 bufline = ptr->data;
745 if(!bufline->terminated)
746 break;
3202e249 747
db137867
AC
748 vec[x].iov_base = bufline->buf;
749 vec[x].iov_len = bufline->len;
750 ptr = ptr->next;
751
3202e249
VY
752 }
753 while(++x < RB_UIO_MAXIOV);
db137867
AC
754
755 if(x == 0)
756 {
757 errno = EWOULDBLOCK;
758 return -1;
759 }
760
761 xret = retval = rb_writev(F, vec, x);
762 if(retval <= 0)
763 return retval;
764
765 ptr = bufhead->list.head;
766
767 for(y = 0; y < x; y++)
768 {
769 bufline = ptr->data;
770
39930c66 771 if(xret >= bufline->len - bufhead->writeofs)
db137867 772 {
39930c66 773 xret -= bufline->len - bufhead->writeofs;
db137867
AC
774 ptr = ptr->next;
775 rb_linebuf_done_line(bufhead, bufline, bufhead->list.head);
39930c66 776 bufhead->writeofs = 0;
db137867 777 }
3202e249 778 else
db137867 779 {
39930c66 780 bufhead->writeofs += xret;
db137867
AC
781 break;
782 }
db137867
AC
783 }
784
785 return retval;
786 }
3202e249
VY
787#endif
788
789 /* this is the non-writev case */
db137867 790
db137867
AC
791 /* Check we actually have a first buffer */
792 if(bufhead->list.head == NULL)
793 {
794 /* nope, so we return none .. */
795 errno = EWOULDBLOCK;
796 return -1;
797 }
798
799 bufline = bufhead->list.head->data;
800
801 /* And that its actually full .. */
802 if(!bufline->terminated)
803 {
804 errno = EWOULDBLOCK;
805 return -1;
806 }
807
db137867
AC
808 /* Now, try writing data */
809 retval = rb_write(F, bufline->buf + bufhead->writeofs, bufline->len - bufhead->writeofs);
810
811 if(retval <= 0)
812 return retval;
813
814 /* we've got data, so update the write offset */
815 bufhead->writeofs += retval;
816
817 /* if we've written everything *and* the CRLF, deallocate and update
818 bufhead */
819 if(bufhead->writeofs == bufline->len)
820 {
821 bufhead->writeofs = 0;
822 lrb_assert(bufhead->len >= 0);
823 rb_linebuf_done_line(bufhead, bufline, bufhead->list.head);
824 }
825
826 /* Return line length */
827 return retval;
828}
829
830
831
832/*
833 * count linebufs for stats z
834 */
835
836void
3202e249 837rb_count_rb_linebuf_memory(size_t *count, size_t *rb_linebuf_memory_used)
db137867 838{
db137867 839 rb_bh_usage(rb_linebuf_heap, count, NULL, rb_linebuf_memory_used, NULL);
db137867 840}