]> jfr.im git - solanum.git/blob - libratbox/src/linebuf.c
Merge branch 'master' of github.com:charybdis-ircd/charybdis
[solanum.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 26092 2008-09-19 15:13:52Z androsyn $
25 */
26
27 #include <libratbox_config.h>
28 #include <ratbox_lib.h>
29 #include <commio-int.h>
30
31 static rb_bh *rb_linebuf_heap;
32
33 static 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
45 void
46 rb_linebuf_init(size_t heap_size)
47 {
48 rb_linebuf_heap = rb_bh_create(sizeof(buf_line_t), heap_size, "librb_linebuf_heap");
49 }
50
51 static buf_line_t *
52 rb_linebuf_allocate(void)
53 {
54 buf_line_t *t;
55 t = rb_bh_alloc(rb_linebuf_heap);
56 return (t);
57
58 }
59
60 static void
61 rb_linebuf_free(buf_line_t * p)
62 {
63 rb_bh_free(rb_linebuf_heap, p);
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 */
72 static buf_line_t *
73 rb_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 */
103 static void
104 rb_linebuf_done_line(buf_head_t * bufhead, buf_line_t * bufline, rb_dlink_node *node)
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 */
131 static inline int
132 rb_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 */
162 void
163 rb_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 */
174 void
175 rb_linebuf_donebuf(buf_head_t * bufhead)
176 {
177 while(bufhead->list.head != NULL)
178 {
179 rb_linebuf_done_line(bufhead, (buf_line_t *) bufhead->list.head->data,
180 bufhead->list.head);
181 }
182 }
183
184 /*
185 * rb_linebuf_copy_line
186 *
187 * Okay..this functions comments made absolutely no sense.
188 *
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 */
204 static int
205 rb_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 {
227 cpylen = BUF_DATA_SIZE - bufline->len - 1;
228 memcpy(bufch, ch, cpylen);
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 */
278 static int
279 rb_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 */
349 int
350 rb_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;
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 */
411 int
412 rb_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 */
477 void
478 rb_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 * rb_linebuf_putmsg
499 *
500 * Similar to rb_linebuf_put, but designed for use by send.c.
501 *
502 * prefixfmt is used as a format for the varargs, and is inserted first.
503 * Then format/va_args is appended to the buffer.
504 */
505 void
506 rb_linebuf_putmsg(buf_head_t * bufhead, const char *format, va_list * va_args,
507 const char *prefixfmt, ...)
508 {
509 buf_line_t *bufline;
510 int len = 0;
511 va_list prefix_args;
512
513 /* make sure the previous line is terminated */
514 #ifndef NDEBUG
515 if(bufhead->list.tail)
516 {
517 bufline = bufhead->list.tail->data;
518 lrb_assert(bufline->terminated);
519 }
520 #endif
521 /* Create a new line */
522 bufline = rb_linebuf_new_line(bufhead);
523
524 if(prefixfmt != NULL)
525 {
526 va_start(prefix_args, prefixfmt);
527 len = vsnprintf(bufline->buf, BUF_DATA_SIZE, prefixfmt, prefix_args);
528 va_end(prefix_args);
529 }
530
531 if(va_args != NULL)
532 {
533 len += vsnprintf((bufline->buf + len), (BUF_DATA_SIZE - len), format, *va_args);
534 }
535
536 bufline->terminated = 1;
537
538 /* Truncate the data if required */
539 if(rb_unlikely(len > 510))
540 {
541 len = 510;
542 bufline->buf[len++] = '\r';
543 bufline->buf[len++] = '\n';
544 }
545 else if(rb_unlikely(len == 0))
546 {
547 bufline->buf[len++] = '\r';
548 bufline->buf[len++] = '\n';
549 bufline->buf[len] = '\0';
550 }
551 else
552 {
553 /* Chop trailing CRLF's .. */
554 while((bufline->buf[len] == '\r') || (bufline->buf[len] == '\n')
555 || (bufline->buf[len] == '\0'))
556 {
557 len--;
558 }
559
560 bufline->buf[++len] = '\r';
561 bufline->buf[++len] = '\n';
562 bufline->buf[++len] = '\0';
563 }
564
565 bufline->len = len;
566 bufhead->len += len;
567 }
568
569 /*
570 * rb_linebuf_putprefix
571 *
572 * Similar to rb_linebuf_put, but designed for use by send.c.
573 *
574 * prefix is inserted first, then format/va_args is appended to the buffer.
575 */
576 void
577 rb_linebuf_putprefix(buf_head_t * bufhead, const char *format, va_list * va_args,
578 const char *prefix)
579 {
580 buf_line_t *bufline;
581 int len = 0;
582
583 /* make sure the previous line is terminated */
584 #ifndef NDEBUG
585 if(bufhead->list.tail)
586 {
587 bufline = bufhead->list.tail->data;
588 lrb_assert(bufline->terminated);
589 }
590 #endif
591 /* Create a new line */
592 bufline = rb_linebuf_new_line(bufhead);
593
594 if(prefix != NULL)
595 len = rb_strlcpy(bufline->buf, prefix, BUF_DATA_SIZE);
596
597 if(va_args != NULL)
598 {
599 len += vsnprintf((bufline->buf + len), (BUF_DATA_SIZE - len), format, *va_args);
600 }
601
602 bufline->terminated = 1;
603
604 /* Truncate the data if required */
605 if(rb_unlikely(len > 510))
606 {
607 len = 510;
608 bufline->buf[len++] = '\r';
609 bufline->buf[len++] = '\n';
610 }
611 else if(rb_unlikely(len == 0))
612 {
613 bufline->buf[len++] = '\r';
614 bufline->buf[len++] = '\n';
615 bufline->buf[len] = '\0';
616 }
617 else
618 {
619 /* Chop trailing CRLF's .. */
620 while((bufline->buf[len] == '\r') || (bufline->buf[len] == '\n')
621 || (bufline->buf[len] == '\0'))
622 {
623 len--;
624 }
625
626 bufline->buf[++len] = '\r';
627 bufline->buf[++len] = '\n';
628 bufline->buf[++len] = '\0';
629 }
630
631 bufline->len = len;
632 bufhead->len += len;
633 }
634
635 void
636 rb_linebuf_putbuf(buf_head_t * bufhead, const char *buffer)
637 {
638 buf_line_t *bufline;
639 int len = 0;
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(rb_unlikely(buffer != NULL))
653 len = rb_strlcpy(bufline->buf, buffer, BUF_DATA_SIZE);
654
655 bufline->terminated = 1;
656
657 /* Truncate the data if required */
658 if(rb_unlikely(len > 510))
659 {
660 len = 510;
661 bufline->buf[len++] = '\r';
662 bufline->buf[len++] = '\n';
663 }
664 else if(rb_unlikely(len == 0))
665 {
666 bufline->buf[len++] = '\r';
667 bufline->buf[len++] = '\n';
668 bufline->buf[len] = '\0';
669 }
670 else
671 {
672 /* Chop trailing CRLF's .. */
673 while((bufline->buf[len] == '\r') || (bufline->buf[len] == '\n')
674 || (bufline->buf[len] == '\0'))
675 {
676 len--;
677 }
678
679 bufline->buf[++len] = '\r';
680 bufline->buf[++len] = '\n';
681 bufline->buf[++len] = '\0';
682 }
683
684 bufline->len = len;
685 bufhead->len += len;
686
687
688 }
689
690 void
691 rb_linebuf_put(buf_head_t * bufhead, const char *format, ...)
692 {
693 buf_line_t *bufline;
694 int len = 0;
695 va_list args;
696
697 /* make sure the previous line is terminated */
698 #ifndef NDEBUG
699 if(bufhead->list.tail)
700 {
701 bufline = bufhead->list.tail->data;
702 lrb_assert(bufline->terminated);
703 }
704 #endif
705 /* Create a new line */
706 bufline = rb_linebuf_new_line(bufhead);
707
708 if(rb_unlikely(format != NULL))
709 {
710 va_start(args, format);
711 len = vsnprintf(bufline->buf, BUF_DATA_SIZE, format, args);
712 va_end(args);
713 }
714
715 bufline->terminated = 1;
716
717 /* Truncate the data if required */
718 if(rb_unlikely(len > 510))
719 {
720 len = 510;
721 bufline->buf[len++] = '\r';
722 bufline->buf[len++] = '\n';
723 }
724 else if(rb_unlikely(len == 0))
725 {
726 bufline->buf[len++] = '\r';
727 bufline->buf[len++] = '\n';
728 bufline->buf[len] = '\0';
729 }
730 else
731 {
732 /* Chop trailing CRLF's .. */
733 while((bufline->buf[len] == '\r') || (bufline->buf[len] == '\n')
734 || (bufline->buf[len] == '\0'))
735 {
736 len--;
737 }
738
739 bufline->buf[++len] = '\r';
740 bufline->buf[++len] = '\n';
741 bufline->buf[++len] = '\0';
742 }
743
744 bufline->len = len;
745 bufhead->len += len;
746 }
747
748
749
750 /*
751 * rb_linebuf_flush
752 *
753 * Flush data to the buffer. It tries to write as much data as possible
754 * to the given socket. Any return values are passed straight through.
755 * If there is no data in the socket, EWOULDBLOCK is set as an errno
756 * rather than returning 0 (which would map to an EOF..)
757 *
758 * Notes: XXX We *should* have a clue here when a non-full buffer is arrived.
759 * and tag it so that we don't re-schedule another write until
760 * we have a CRLF.
761 */
762 int
763 rb_linebuf_flush(rb_fde_t *F, buf_head_t * bufhead)
764 {
765 buf_line_t *bufline;
766 int retval;
767
768 /*
769 * autoconf checks for this..but really just want to use it if we have a
770 * native version even if libircd provides a fake version...
771 */
772 #ifdef HAVE_WRITEV
773 if(!rb_fd_ssl(F))
774 {
775 rb_dlink_node *ptr;
776 int x = 0, y;
777 int xret;
778 static struct rb_iovec vec[RB_UIO_MAXIOV];
779
780 memset(vec, 0, sizeof(vec));
781 /* Check we actually have a first buffer */
782 if(bufhead->list.head == NULL)
783 {
784 /* nope, so we return none .. */
785 errno = EWOULDBLOCK;
786 return -1;
787 }
788
789 ptr = bufhead->list.head;
790
791 bufline = ptr->data;
792 if(!bufline->terminated)
793 {
794 errno = EWOULDBLOCK;
795 return -1;
796
797 }
798
799 vec[x].iov_base = bufline->buf + bufhead->writeofs;
800 vec[x++].iov_len = bufline->len - bufhead->writeofs;
801 ptr = ptr->next;
802
803 do
804 {
805 if(ptr == NULL)
806 break;
807
808 bufline = ptr->data;
809 if(!bufline->terminated)
810 break;
811
812 vec[x].iov_base = bufline->buf;
813 vec[x].iov_len = bufline->len;
814 ptr = ptr->next;
815
816 }
817 while(++x < RB_UIO_MAXIOV);
818
819 if(x == 0)
820 {
821 errno = EWOULDBLOCK;
822 return -1;
823 }
824
825 xret = retval = rb_writev(F, vec, x);
826 if(retval <= 0)
827 return retval;
828
829 ptr = bufhead->list.head;
830
831 for(y = 0; y < x; y++)
832 {
833 bufline = ptr->data;
834
835 if(xret >= bufline->len - bufhead->writeofs)
836 {
837 xret -= bufline->len - bufhead->writeofs;
838 ptr = ptr->next;
839 rb_linebuf_done_line(bufhead, bufline, bufhead->list.head);
840 bufhead->writeofs = 0;
841 }
842 else
843 {
844 bufhead->writeofs += xret;
845 break;
846 }
847 }
848
849 return retval;
850 }
851 #endif
852
853 /* this is the non-writev case */
854
855 /* Check we actually have a first buffer */
856 if(bufhead->list.head == NULL)
857 {
858 /* nope, so we return none .. */
859 errno = EWOULDBLOCK;
860 return -1;
861 }
862
863 bufline = bufhead->list.head->data;
864
865 /* And that its actually full .. */
866 if(!bufline->terminated)
867 {
868 errno = EWOULDBLOCK;
869 return -1;
870 }
871
872 /* Now, try writing data */
873 retval = rb_write(F, bufline->buf + bufhead->writeofs, bufline->len - bufhead->writeofs);
874
875 if(retval <= 0)
876 return retval;
877
878 /* we've got data, so update the write offset */
879 bufhead->writeofs += retval;
880
881 /* if we've written everything *and* the CRLF, deallocate and update
882 bufhead */
883 if(bufhead->writeofs == bufline->len)
884 {
885 bufhead->writeofs = 0;
886 lrb_assert(bufhead->len >= 0);
887 rb_linebuf_done_line(bufhead, bufline, bufhead->list.head);
888 }
889
890 /* Return line length */
891 return retval;
892 }
893
894
895
896 /*
897 * count linebufs for stats z
898 */
899
900 void
901 rb_count_rb_linebuf_memory(size_t *count, size_t *rb_linebuf_memory_used)
902 {
903 rb_bh_usage(rb_linebuf_heap, count, NULL, rb_linebuf_memory_used, NULL);
904 }