]> jfr.im git - irc/rqf/shadowircd.git/blob - libcharybdis/linebuf.c
[svn] - the new plan:
[irc/rqf/shadowircd.git] / libcharybdis / 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 * USA
23 *
24 * $Id: linebuf.c 1110 2006-03-29 22:55:25Z nenolod $
25 */
26
27 #include "stdinc.h"
28 #include "tools.h"
29 #include "client.h"
30 #include "linebuf.h"
31 #include "memory.h"
32 #include "event.h"
33 #include "balloc.h"
34 #include "hook.h"
35 #include "commio.h"
36 #include "sprintf_irc.h"
37
38 #ifdef STRING_WITH_STRINGS
39 # include <string.h>
40 # include <strings.h>
41 #else
42 # ifdef HAVE_STRING_H
43 # include <string.h>
44 # else
45 # ifdef HAVE_STRINGS_H
46 # include <strings.h>
47 # endif
48 # endif
49 #endif
50
51 extern BlockHeap *linebuf_heap;
52
53 static int bufline_count = 0;
54
55
56 /*
57 * linebuf_init
58 *
59 * Initialise the linebuf mechanism
60 */
61
62
63 void
64 linebuf_init(void)
65 {
66 linebuf_heap = BlockHeapCreate(sizeof(buf_line_t), LINEBUF_HEAP_SIZE);
67 }
68
69 static buf_line_t *
70 linebuf_allocate(void)
71 {
72 buf_line_t *t;
73 t = BlockHeapAlloc(linebuf_heap);
74 t->refcount = 0;
75 return (t);
76
77 }
78
79 static void
80 linebuf_free(buf_line_t * p)
81 {
82 BlockHeapFree(linebuf_heap, p);
83 }
84
85 /*
86 * linebuf_new_line
87 *
88 * Create a new line, and link it to the given linebuf.
89 * It will be initially empty.
90 */
91 static buf_line_t *
92 linebuf_new_line(buf_head_t * bufhead)
93 {
94 buf_line_t *bufline;
95 dlink_node *node;
96
97 bufline = linebuf_allocate();
98 if(bufline == NULL)
99 return NULL;
100 ++bufline_count;
101
102
103 node = make_dlink_node();
104
105 bufline->len = 0;
106 bufline->terminated = 0;
107 bufline->flushing = 0;
108 bufline->raw = 0;
109
110 /* Stick it at the end of the buf list */
111 dlinkAddTail(bufline, node, &bufhead->list);
112 bufline->refcount++;
113
114 /* And finally, update the allocated size */
115 bufhead->alloclen++;
116 bufhead->numlines++;
117
118 return bufline;
119 }
120
121
122 /*
123 * linebuf_done_line
124 *
125 * We've finished with the given line, so deallocate it
126 */
127 static void
128 linebuf_done_line(buf_head_t * bufhead, buf_line_t * bufline, dlink_node * node)
129 {
130 /* Remove it from the linked list */
131 dlinkDestroy(node, &bufhead->list);
132
133 /* Update the allocated size */
134 bufhead->alloclen--;
135 bufhead->len -= bufline->len;
136 s_assert(bufhead->len >= 0);
137 bufhead->numlines--;
138
139 bufline->refcount--;
140 s_assert(bufline->refcount >= 0);
141
142 if(bufline->refcount == 0)
143 {
144 /* and finally, deallocate the buf */
145 --bufline_count;
146 s_assert(bufline_count >= 0);
147 linebuf_free(bufline);
148 }
149 }
150
151
152 /*
153 * skip to end of line or the crlfs, return the number of bytes ..
154 */
155 static inline int
156 linebuf_skip_crlf(char *ch, int len)
157 {
158 int orig_len = len;
159
160 /* First, skip until the first non-CRLF */
161 for (; len; len--, ch++)
162 {
163 if(*ch == '\r')
164 break;
165 else if(*ch == '\n')
166 break;
167 }
168
169 /* Then, skip until the last CRLF */
170 for (; len; len--, ch++)
171 {
172 if((*ch != '\r') && (*ch != '\n'))
173 break;
174 }
175 s_assert(orig_len > len);
176 return (orig_len - len);
177 }
178
179
180
181 /*
182 * linebuf_newbuf
183 *
184 * Initialise the new buffer
185 */
186 void
187 linebuf_newbuf(buf_head_t * bufhead)
188 {
189 /* not much to do right now :) */
190 memset(bufhead, 0, sizeof(buf_head_t));
191 }
192
193 /*
194 * client_flush_input
195 *
196 * inputs - pointer to client
197 * output - none
198 * side effects - all input line bufs are flushed
199 */
200 void
201 client_flush_input(struct Client *client_p)
202 {
203 /* This way, it can be called for remote client as well */
204
205 if(client_p->localClient == NULL)
206 return;
207
208 linebuf_donebuf(&client_p->localClient->buf_recvq);
209 }
210
211
212 /*
213 * linebuf_donebuf
214 *
215 * Flush all the lines associated with this buffer
216 */
217 void
218 linebuf_donebuf(buf_head_t * bufhead)
219 {
220 while (bufhead->list.head != NULL)
221 {
222 linebuf_done_line(bufhead,
223 (buf_line_t *) bufhead->list.head->data, bufhead->list.head);
224 }
225 }
226
227 /*
228 * linebuf_copy_line
229 *
230 * Okay..this functions comments made absolutely no sense.
231 *
232 * Basically what we do is this. Find the first chunk of text
233 * and then scan for a CRLF. If we didn't find it, but we didn't
234 * overflow our buffer..we wait for some more data.
235 * If we found a CRLF, we replace them with a \0 character.
236 * If we overflowed, we copy the most our buffer can handle, terminate
237 * it with a \0 and return.
238 *
239 * The return value is the amount of data we consumed. This could
240 * be different than the size of the linebuffer, as when we discard
241 * the overflow, we don't want to process it again.
242 *
243 * This still sucks in my opinion, but it seems to work.
244 *
245 * -Aaron
246 */
247 static int
248 linebuf_copy_line(buf_head_t * bufhead, buf_line_t * bufline, char *data, int len)
249 {
250 int cpylen = 0; /* how many bytes we've copied */
251 char *ch = data; /* Pointer to where we are in the read data */
252 char *bufch = bufline->buf + bufline->len;
253 int clen = 0; /* how many bytes we've processed,
254 and don't ever want to see again.. */
255
256 /* If its full or terminated, ignore it */
257
258 bufline->raw = 0;
259 s_assert(bufline->len < BUF_DATA_SIZE);
260 if(bufline->terminated == 1)
261 return 0;
262
263 clen = cpylen = linebuf_skip_crlf(ch, len);
264 if(clen == -1)
265 return -1;
266
267 /* This is the ~overflow case..This doesn't happen often.. */
268 if(cpylen > (BUF_DATA_SIZE - bufline->len - 1))
269 {
270 memcpy(bufch, ch, (BUF_DATA_SIZE - bufline->len - 1));
271 bufline->buf[BUF_DATA_SIZE - 1] = '\0';
272 bufch = bufline->buf + BUF_DATA_SIZE - 2;
273 while (cpylen && (*bufch == '\r' || *bufch == '\n'))
274 {
275 *bufch = '\0';
276 cpylen--;
277 bufch--;
278 }
279 bufline->terminated = 1;
280 bufline->len = BUF_DATA_SIZE - 1;
281 bufhead->len += BUF_DATA_SIZE - 1;
282 return clen;
283 }
284
285 memcpy(bufch, ch, cpylen);
286 bufch += cpylen;
287 *bufch = '\0';
288 bufch--;
289
290 if(*bufch != '\r' && *bufch != '\n')
291 {
292 /* No linefeed, bail for the next time */
293 bufhead->len += cpylen;
294 bufline->len += cpylen;
295 bufline->terminated = 0;
296 return clen;
297 }
298
299 /* Yank the CRLF off this, replace with a \0 */
300 while (cpylen && (*bufch == '\r' || *bufch == '\n'))
301 {
302 *bufch = '\0';
303 cpylen--;
304 bufch--;
305 }
306
307 bufline->terminated = 1;
308 bufhead->len += cpylen;
309 bufline->len += cpylen;
310 return clen;
311 }
312
313 /*
314 * linebuf_copy_raw
315 *
316 * Copy as much data as possible directly into a linebuf,
317 * splitting at \r\n, but without altering any data.
318 *
319 */
320 static int
321 linebuf_copy_raw(buf_head_t * bufhead, buf_line_t * bufline, char *data, int len)
322 {
323 int cpylen = 0; /* how many bytes we've copied */
324 char *ch = data; /* Pointer to where we are in the read data */
325 char *bufch = bufline->buf + bufline->len;
326 int clen = 0; /* how many bytes we've processed,
327 and don't ever want to see again.. */
328
329 /* If its full or terminated, ignore it */
330
331 bufline->raw = 1;
332 s_assert(bufline->len < BUF_DATA_SIZE);
333 if(bufline->terminated == 1)
334 return 0;
335
336 clen = cpylen = linebuf_skip_crlf(ch, len);
337 if(clen == -1)
338 return -1;
339
340 /* This is the overflow case..This doesn't happen often.. */
341 if(cpylen > (BUF_DATA_SIZE - bufline->len - 1))
342 {
343 clen = BUF_DATA_SIZE - bufline->len - 1;
344 memcpy(bufch, ch, clen);
345 bufline->buf[BUF_DATA_SIZE - 1] = '\0';
346 bufch = bufline->buf + BUF_DATA_SIZE - 2;
347 bufline->terminated = 1;
348 bufline->len = BUF_DATA_SIZE - 1;
349 bufhead->len += BUF_DATA_SIZE - 1;
350 return clen;
351 }
352
353 memcpy(bufch, ch, cpylen);
354 bufch += cpylen;
355 *bufch = '\0';
356 bufch--;
357
358 if(*bufch != '\r' && *bufch != '\n')
359 {
360 /* No linefeed, bail for the next time */
361 bufhead->len += cpylen;
362 bufline->len += cpylen;
363 bufline->terminated = 0;
364 return clen;
365 }
366
367 bufline->terminated = 1;
368 bufhead->len += cpylen;
369 bufline->len += cpylen;
370 return clen;
371 }
372
373
374 /*
375 * linebuf_parse
376 *
377 * Take a given buffer and break out as many buffers as we can.
378 * If we find a CRLF, we terminate that buffer and create a new one.
379 * If we don't find a CRLF whilst parsing a buffer, we don't mark it
380 * 'finished', so the next loop through we can continue appending ..
381 *
382 * A few notes here, which you'll need to understand before continuing.
383 *
384 * - right now I'm only dealing with single sized buffers. Later on,
385 * I might consider chaining buffers together to get longer "lines"
386 * but seriously, I don't see the advantage right now.
387 *
388 * - This *is* designed to turn into a reference-counter-protected setup
389 * to dodge copious copies.
390 */
391 int
392 linebuf_parse(buf_head_t * bufhead, char *data, int len, int raw)
393 {
394 buf_line_t *bufline;
395 int cpylen;
396 int linecnt = 0;
397
398 /* First, if we have a partial buffer, try to squeze data into it */
399 if(bufhead->list.tail != NULL)
400 {
401 /* Check we're doing the partial buffer thing */
402 bufline = bufhead->list.tail->data;
403 s_assert(!bufline->flushing);
404 /* just try, the worst it could do is *reject* us .. */
405 if(!raw)
406 cpylen = linebuf_copy_line(bufhead, bufline, data, len);
407 else
408 cpylen = linebuf_copy_raw(bufhead, bufline, data, len);
409
410 if(cpylen == -1)
411 return -1;
412
413 linecnt++;
414 /* If we've copied the same as what we've got, quit now */
415 if(cpylen == len)
416 return linecnt; /* all the data done so soon? */
417
418 /* Skip the data and update len .. */
419 len -= cpylen;
420 s_assert(len >= 0);
421 data += cpylen;
422 }
423
424 /* Next, the loop */
425 while (len > 0)
426 {
427 /* We obviously need a new buffer, so .. */
428 bufline = linebuf_new_line(bufhead);
429
430 /* And parse */
431 if(!raw)
432 cpylen = linebuf_copy_line(bufhead, bufline, data, len);
433 else
434 cpylen = linebuf_copy_raw(bufhead, bufline, data, len);
435
436 if(cpylen == -1)
437 return -1;
438
439 len -= cpylen;
440 s_assert(len >= 0);
441 data += cpylen;
442 linecnt++;
443 }
444 return linecnt;
445 }
446
447
448 /*
449 * linebuf_get
450 *
451 * get the next buffer from our line. For the time being it will copy
452 * data into the given buffer and free the underlying linebuf.
453 */
454 int
455 linebuf_get(buf_head_t * bufhead, char *buf, int buflen, int partial, int raw)
456 {
457 buf_line_t *bufline;
458 int cpylen;
459 char *start, *ch;
460
461 /* make sure we have a line */
462 if(bufhead->list.head == NULL)
463 return 0; /* Obviously not.. hrm. */
464
465 bufline = bufhead->list.head->data;
466
467 /* make sure that the buffer was actually *terminated */
468 if(!(partial || bufline->terminated))
469 return 0; /* Wait for more data! */
470
471 /* make sure we've got the space, including the NULL */
472 cpylen = bufline->len;
473 s_assert(cpylen + 1 <= buflen);
474
475 /* Copy it */
476 start = bufline->buf;
477
478 /* if we left extraneous '\r\n' characters in the string,
479 * and we don't want to read the raw data, clean up the string.
480 */
481 if(bufline->raw && !raw)
482 {
483 /* skip leading EOL characters */
484 while (cpylen && (*start == '\r' || *start == '\n'))
485 {
486 start++;
487 cpylen--;
488 }
489 /* skip trailing EOL characters */
490 ch = &start[cpylen - 1];
491 while (cpylen && (*ch == '\r' || *ch == '\n'))
492 {
493 ch--;
494 cpylen--;
495 }
496 }
497 memcpy(buf, start, cpylen + 1);
498
499 /* convert CR/LF to NULL */
500 if(bufline->raw && !raw)
501 buf[cpylen] = '\0';
502
503 s_assert(cpylen >= 0);
504
505 /* Deallocate the line */
506 linebuf_done_line(bufhead, bufline, bufhead->list.head);
507
508 /* return how much we copied */
509 return cpylen;
510 }
511
512 /*
513 * linebuf_attach
514 *
515 * attach the lines in a buf_head_t to another buf_head_t
516 * without copying the data (using refcounts).
517 */
518 void
519 linebuf_attach(buf_head_t * bufhead, buf_head_t * new)
520 {
521 dlink_node *ptr;
522 buf_line_t *line;
523
524 DLINK_FOREACH(ptr, new->list.head)
525 {
526 line = ptr->data;
527 dlinkAddTailAlloc(line, &bufhead->list);
528
529 /* Update the allocated size */
530 bufhead->alloclen++;
531 bufhead->len += line->len;
532 bufhead->numlines++;
533
534 line->refcount++;
535 }
536 }
537
538 /*
539 * linebuf_putmsg
540 *
541 * Similar to linebuf_put, but designed for use by send.c.
542 *
543 * prefixfmt is used as a format for the varargs, and is inserted first.
544 * Then format/va_args is appended to the buffer.
545 */
546 void
547 linebuf_putmsg(buf_head_t * bufhead, const char *format, va_list * va_args,
548 const char *prefixfmt, ...)
549 {
550 buf_line_t *bufline;
551 int len = 0;
552 va_list prefix_args;
553
554 /* make sure the previous line is terminated */
555 #ifndef NDEBUG
556 if(bufhead->list.tail)
557 {
558 bufline = bufhead->list.tail->data;
559 s_assert(bufline->terminated);
560 }
561 #endif
562 /* Create a new line */
563 bufline = linebuf_new_line(bufhead);
564
565 if(prefixfmt != NULL)
566 {
567 va_start(prefix_args, prefixfmt);
568 len = ircvsnprintf(bufline->buf, BUF_DATA_SIZE, prefixfmt, prefix_args);
569 va_end(prefix_args);
570 }
571
572 if(va_args != NULL)
573 {
574 len += ircvsnprintf((bufline->buf + len), (BUF_DATA_SIZE - len), format, *va_args);
575 }
576
577 bufline->terminated = 1;
578
579 /* Truncate the data if required */
580 if(len > 510)
581 {
582 len = 510;
583 bufline->buf[len++] = '\r';
584 bufline->buf[len++] = '\n';
585 }
586 else if(len == 0)
587 {
588 bufline->buf[len++] = '\r';
589 bufline->buf[len++] = '\n';
590 bufline->buf[len] = '\0';
591 }
592 else
593 {
594 /* Chop trailing CRLF's .. */
595 while ((bufline->buf[len] == '\r')
596 || (bufline->buf[len] == '\n') || (bufline->buf[len] == '\0'))
597 {
598 len--;
599 }
600
601 bufline->buf[++len] = '\r';
602 bufline->buf[++len] = '\n';
603 bufline->buf[++len] = '\0';
604 }
605
606 bufline->len = len;
607 bufhead->len += len;
608 }
609
610
611
612 /*
613 * linebuf_flush
614 *
615 * Flush data to the buffer. It tries to write as much data as possible
616 * to the given socket. Any return values are passed straight through.
617 * If there is no data in the socket, EWOULDBLOCK is set as an errno
618 * rather than returning 0 (which would map to an EOF..)
619 *
620 * Notes: XXX We *should* have a clue here when a non-full buffer is arrived.
621 * and tag it so that we don't re-schedule another write until
622 * we have a CRLF.
623 */
624
625 int
626 linebuf_flush(int fd, buf_head_t * bufhead)
627 {
628 buf_line_t *bufline;
629 int retval;
630 /* Check we actually have a first buffer */
631 if(bufhead->list.head == NULL)
632 {
633 /* nope, so we return none .. */
634 errno = EWOULDBLOCK;
635 return -1;
636 }
637
638 bufline = bufhead->list.head->data;
639
640 /* And that its actually full .. */
641 if(!bufline->terminated)
642 {
643 errno = EWOULDBLOCK;
644 return -1;
645 }
646
647 /* Check we're flushing the first buffer */
648 if(!bufline->flushing)
649 {
650 bufline->flushing = 1;
651 bufhead->writeofs = 0;
652 }
653
654 /* Now, try writing data */
655 retval = send(fd, bufline->buf + bufhead->writeofs, bufline->len - bufhead->writeofs, 0);
656
657 if(retval <= 0)
658 return retval;
659
660 /* we've got data, so update the write offset */
661 bufhead->writeofs += retval;
662
663 /* if we've written everything *and* the CRLF, deallocate and update
664 bufhead */
665 if(bufhead->writeofs == bufline->len)
666 {
667 bufhead->writeofs = 0;
668 s_assert(bufhead->len >= 0);
669 linebuf_done_line(bufhead, bufline, bufhead->list.head);
670 }
671
672 /* Return line length */
673 return retval;
674 }
675
676
677
678 /*
679 * count linebufs for stats z
680 */
681
682 void
683 count_linebuf_memory(size_t * count, size_t * linebuf_memory_used)
684 {
685 BlockHeapUsage(linebuf_heap, count, NULL, linebuf_memory_used);
686 }