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