Added SILC Thread Queue API
[crypto.git] / apps / irssi / src / core / line-split.c
1 /*
2  line-split.c : irssi
3
4     Copyright (C) 1999-2000 Timo Sirainen
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "module.h"
22 #include "misc.h"
23
24 /* Maximum line length - split to two lines if it's longer than this.
25
26    This is mostly to prevent excessive memory usage. Like if someone DCC
27    chats you, you both have very fast connections and the other side sends
28    you 100 megs of text without any line feeds -> irssi will (try to)
29    allocate 128M of memory for the line and will eventually crash when it
30    can't allocate any more memory. If the line is split at every 64k the
31    text buffer will free the old lines and the memory usage never gets
32    too high. */
33 #define MAX_CHARS_IN_LINE 65536
34
35 struct _LINEBUF_REC {
36         int len;
37         int alloc;
38         int remove;
39         char *str;
40 };
41
42 static void linebuf_append(LINEBUF_REC *rec, const char *data, int len)
43 {
44         if (rec->len+len > rec->alloc) {
45                 rec->alloc = nearest_power(rec->len+len);;
46                 rec->str = rec->str == NULL ? g_malloc(rec->alloc) :
47                         g_realloc(rec->str, rec->alloc);
48         }
49
50         memcpy(rec->str + rec->len, data, len);
51         rec->len += len;
52 }
53
54 static char *linebuf_find(LINEBUF_REC *rec, char chr)
55 {
56         int n;
57
58         for (n = 0; n < rec->len; n++)
59                 if (rec->str[n] == chr) return rec->str+n;
60
61         return NULL;
62 }
63
64 static int remove_newline(LINEBUF_REC *rec)
65 {
66         char *ptr;
67
68         ptr = linebuf_find(rec, '\n');
69         if (ptr == NULL) {
70                 /* LF wasn't found, wait for more data.. */
71                 if (rec->len < MAX_CHARS_IN_LINE)
72                         return 0;
73
74                 /* line buffer is too big - force a newline. */
75                 linebuf_append(rec, "\n", 1);
76                 ptr = rec->str+rec->len-1;
77         }
78
79         rec->remove = (int) (ptr-rec->str)+1;
80         if (ptr != rec->str && ptr[-1] == '\r') {
81                 /* remove CR too. */
82                 ptr--;
83         }
84
85         *ptr = '\0';
86         return 1;
87 }
88
89 /* line-split `data'. Initially `*buffer' should contain NULL. */
90 int line_split(const char *data, int len, char **output, LINEBUF_REC **buffer)
91 {
92         LINEBUF_REC *rec;
93         int ret;
94
95         g_return_val_if_fail(data != NULL, -1);
96         g_return_val_if_fail(output != NULL, -1);
97         g_return_val_if_fail(buffer != NULL, -1);
98
99         if (*buffer == NULL)
100                 *buffer = g_new0(LINEBUF_REC, 1);
101         rec = *buffer;
102
103         if (rec->remove > 0) {
104                 rec->len -= rec->remove;
105                 g_memmove(rec->str, rec->str+rec->remove, rec->len);
106                 rec->remove = 0;
107         }
108
109         if (len > 0)
110                 linebuf_append(rec, data, len);
111         else if (len < 0) {
112                 /* connection closed.. */
113                 if (rec->len == 0)
114                         return -1;
115
116                 /* no new data got but still something in buffer.. */
117                 len = 0;
118                 if (linebuf_find(rec, '\n') == NULL) {
119                         /* connection closed and last line is missing \n ..
120                            just add it so we can see if it had
121                            anything useful.. */
122                         linebuf_append(rec, "\n", 1);
123                 }
124         }
125
126         ret = remove_newline(rec);
127         *output = rec->str;
128         return ret;
129 }
130
131 void line_split_free(LINEBUF_REC *buffer)
132 {
133         if (buffer != NULL) {
134                 if (buffer->str != NULL) g_free(buffer->str);
135                 g_free(buffer);
136         }
137 }
138
139 /* Return 1 if there is no data in the buffer */
140 int line_split_is_empty(LINEBUF_REC *buffer)
141 {
142         return buffer->len == 0;
143 }