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 along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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
linebuf_append(LINEBUF_REC * rec,const char * data,int len)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 = g_realloc(rec->str, rec->alloc);
47 }
48
49 memcpy(rec->str + rec->len, data, len);
50 rec->len += len;
51 }
52
linebuf_find(LINEBUF_REC * rec,char chr)53 static char *linebuf_find(LINEBUF_REC *rec, char chr)
54 {
55 return memchr(rec->str, chr, rec->len);
56 }
57
remove_newline(LINEBUF_REC * rec)58 static int remove_newline(LINEBUF_REC *rec)
59 {
60 char *ptr;
61
62 ptr = linebuf_find(rec, '\n');
63 if (ptr == NULL) {
64 /* LF wasn't found, wait for more data.. */
65 if (rec->len < MAX_CHARS_IN_LINE)
66 return 0;
67
68 /* line buffer is too big - force a newline. */
69 linebuf_append(rec, "\n", 1);
70 ptr = rec->str+rec->len-1;
71 }
72
73 rec->remove = (int) (ptr-rec->str)+1;
74 if (ptr != rec->str && ptr[-1] == '\r') {
75 /* remove CR too. */
76 ptr--;
77 }
78
79 *ptr = '\0';
80 return 1;
81 }
82
83 /* line-split `data'. Initially `*buffer' should contain NULL. */
line_split(const char * data,int len,char ** output,LINEBUF_REC ** buffer)84 int line_split(const char *data, int len, char **output, LINEBUF_REC **buffer)
85 {
86 LINEBUF_REC *rec;
87 int ret;
88
89 g_return_val_if_fail(data != NULL, -1);
90 g_return_val_if_fail(output != NULL, -1);
91 g_return_val_if_fail(buffer != NULL, -1);
92
93 if (*buffer == NULL)
94 *buffer = g_new0(LINEBUF_REC, 1);
95 rec = *buffer;
96
97 if (rec->remove > 0) {
98 rec->len -= rec->remove;
99 g_memmove(rec->str, rec->str+rec->remove, rec->len);
100 rec->remove = 0;
101 }
102
103 if (len > 0)
104 linebuf_append(rec, data, len);
105 else if (len < 0) {
106 /* connection closed.. */
107 if (rec->len == 0)
108 return -1;
109
110 /* no new data got but still something in buffer.. */
111 if (linebuf_find(rec, '\n') == NULL) {
112 /* connection closed and last line is missing \n ..
113 just add it so we can see if it had
114 anything useful.. */
115 linebuf_append(rec, "\n", 1);
116 }
117 }
118
119 ret = remove_newline(rec);
120 *output = rec->str;
121 return ret;
122 }
123
line_split_free(LINEBUF_REC * buffer)124 void line_split_free(LINEBUF_REC *buffer)
125 {
126 if (buffer != NULL) {
127 if (buffer->str != NULL) g_free(buffer->str);
128 g_free(buffer);
129 }
130 }
131
132 /* Return 1 if there is no data in the buffer */
line_split_is_empty(LINEBUF_REC * buffer)133 int line_split_is_empty(LINEBUF_REC *buffer)
134 {
135 return buffer->len == 0;
136 }
137