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