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