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