comparison mcabber/src/hbuf.c @ 71:1e9d4949bcfd

[/trunk] Changeset 85 by mikael * New history buffer implementation
author mikael
date Sat, 16 Apr 2005 10:14:24 +0000
parents
children ff119bb11563
comparison
equal deleted inserted replaced
70:5b1249ce812d 71:1e9d4949bcfd
1 /*
2 * hbuf.c -- History buffer implementation
3 *
4 * Copyright (C) 2005 Mikael Berthe <bmikael@lists.lilotux.net>
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 (at
9 * your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * 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
19 * USA
20 */
21
22 #include <string.h>
23
24 #include "hbuf.h"
25
26
27 /* This is a private structure type */
28
29 typedef struct {
30 char *ptr;
31 char *ptr_end;
32 guchar flags;
33
34 // XXX This should certainly be a pointer, and be allocated only when needed
35 // (for ex. when HBB_FLAG_PERSISTENT is set).
36 struct { // hbuf_line_info
37 char *ptr_end_alloc;
38 char prefix[32];
39 } persist;
40 } hbuf_block;
41
42
43 // hbuf_add_line(p_hbuf, text, width)
44 // Add a line to the given buffer. If width is not null, then lines are
45 // wrapped at this length.
46 //
47 // Note 1: Splitting according to width won't work if there are tabs; they
48 // should be expanded before.
49 // Note 2: width does not include the ending \0.
50 void hbuf_add_line(GList **p_hbuf, char *text, unsigned int width)
51 {
52 GList *hbuf = *p_hbuf;
53 char *line, *cr, *end;
54
55 if (!text) return;
56
57 hbuf_block *hbuf_block_elt = g_new0(hbuf_block, 1);
58 if (!hbuf) {
59 hbuf_block_elt->ptr = g_new(char, HBB_BLOCKSIZE);
60 hbuf_block_elt->flags = HBB_FLAG_ALLOC | HBB_FLAG_PERSISTENT;
61 hbuf_block_elt->persist.ptr_end_alloc = hbuf_block_elt->ptr + HBB_BLOCKSIZE;
62 *p_hbuf = g_list_append(*p_hbuf, hbuf_block_elt);
63 } else {
64 hbuf_block *hbuf_b_prev = g_list_last(hbuf)->data;
65 hbuf_block_elt->ptr = hbuf_b_prev->ptr_end;
66 hbuf_block_elt->flags = HBB_FLAG_PERSISTENT;
67 hbuf_block_elt->persist.ptr_end_alloc = hbuf_b_prev->persist.ptr_end_alloc;
68 *p_hbuf = g_list_append(*p_hbuf, hbuf_block_elt);
69 }
70
71 if (strlen(text) >= HBB_BLOCKSIZE) {
72 // Too long
73 text = "[ERR:LINE_TOO_LONG]";
74 }
75 if (hbuf_block_elt->ptr + strlen(text) >= hbuf_block_elt->persist.ptr_end_alloc) {
76 // Too long for the current allocated bloc, we need another one
77 hbuf_block_elt->ptr = g_new0(char, HBB_BLOCKSIZE);
78 hbuf_block_elt->flags = HBB_FLAG_ALLOC | HBB_FLAG_PERSISTENT;
79 hbuf_block_elt->persist.ptr_end_alloc = hbuf_block_elt->ptr + HBB_BLOCKSIZE;
80 }
81
82 line = hbuf_block_elt->ptr;
83 // Ok, now we can copy the text..
84 strcpy(line, text);
85 hbuf_block_elt->ptr_end = line + strlen(line) + 1;
86 end = hbuf_block_elt->ptr_end;
87
88 // Let's add non-persistent blocs if necessary
89 // - If there are '\n' in the string
90 // - If length > width (and width != 0)
91 cr = strchr(line, '\n');
92 while (cr || (width && strlen(line) > width)) {
93 hbuf_block *hbuf_b_prev = hbuf_block_elt;
94
95 if (!width || (cr && (cr - line <= (int)width))) {
96 // Carriage return
97 *cr = 0;
98 hbuf_block_elt->ptr_end = cr;
99 // Create another persistent block
100 hbuf_block_elt = g_new0(hbuf_block, 1);
101 hbuf_block_elt->ptr = hbuf_b_prev->ptr_end + 1; // == cr+1
102 hbuf_block_elt->ptr_end = end;
103 hbuf_block_elt->flags = HBB_FLAG_PERSISTENT;
104 hbuf_block_elt->persist.ptr_end_alloc = hbuf_b_prev->persist.ptr_end_alloc;
105 *p_hbuf = g_list_append(*p_hbuf, hbuf_block_elt);
106 line = hbuf_block_elt->ptr;
107 } else {
108 // We need to break where we can find a space char
109 char *br; // break pointer
110 for (br = line + width; br > line && *br != 32 && *br != 9; br--)
111 ;
112 if (br <= line)
113 br = line + width;
114 else
115 br++;
116 hbuf_block_elt->ptr_end = br;
117 // Create another block, non-persistent
118 hbuf_block_elt = g_new0(hbuf_block, 1);
119 hbuf_block_elt->ptr = hbuf_b_prev->ptr_end; // == br
120 hbuf_block_elt->ptr_end = end;
121 hbuf_block_elt->flags = 0;
122 hbuf_block_elt->persist.ptr_end_alloc = hbuf_b_prev->persist.ptr_end_alloc;
123 *p_hbuf = g_list_append(*p_hbuf, hbuf_block_elt);
124 line = hbuf_block_elt->ptr;
125 }
126 cr = strchr(line, '\n');
127 }
128 }
129
130 // hbuf_free()
131 // Destroys all hbuf list.
132 void hbuf_free(GList **p_hbuf)
133 {
134 hbuf_block *hbuf_b_elt;
135 GList *hbuf_elt;
136 GList *first_elt = g_list_first(*p_hbuf);
137
138 for (hbuf_elt = first_elt; hbuf_elt; hbuf_elt = g_list_next(hbuf_elt)) {
139 hbuf_b_elt = (hbuf_block*)(hbuf_elt->data);
140 if (hbuf_b_elt->flags & HBB_FLAG_ALLOC) {
141 g_free(hbuf_b_elt->ptr);
142 }
143 }
144
145 g_list_free(*p_hbuf);
146 *p_hbuf = NULL;
147 }
148
149 // hbuf_rebuild()
150 // Rebuild all hbuf list, with the new width.
151 // If width == 0, lines are not wrapped.
152 void hbuf_rebuild(GList **p_hbuf, unsigned int width)
153 {
154 GList *first_elt, *curr_elt, *next_elt;
155 hbuf_block *hbuf_b_curr, *hbuf_b_next;
156
157 first_elt = g_list_first(*p_hbuf);
158
159 // #1 Remove non-persistent blocks (ptr_end should be updated!)
160 curr_elt = first_elt;
161 while (curr_elt) {
162 next_elt = g_list_next(curr_elt);
163 // Last element?
164 if (!next_elt)
165 break;
166 hbuf_b_curr = (hbuf_block*)(curr_elt->data);
167 hbuf_b_next = (hbuf_block*)(next_elt->data);
168 // Is next line not-persistent?
169 if (!(hbuf_b_next->flags & HBB_FLAG_PERSISTENT)) {
170 hbuf_b_curr->ptr_end = hbuf_b_next->ptr_end;
171 g_list_delete_link(curr_elt, next_elt);
172 next_elt = g_list_next(curr_elt);
173 } else
174 curr_elt = next_elt;
175 }
176 // #2 Go back to head and create non-persistent blocks when needed
177 if (width) {
178 char *line, *end;
179 curr_elt = first_elt;
180
181 while (curr_elt) {
182 hbuf_b_curr = (hbuf_block*)(curr_elt->data);
183 line = hbuf_b_curr->ptr;
184 if (strlen(line) > width) {
185 hbuf_block *hbuf_b_prev = hbuf_b_curr;
186
187 // We need to break where we can find a space char
188 char *br; // break pointer
189 for (br = line + width; br > line && *br != 32 && *br != 9; br--)
190 ;
191 if (br <= line)
192 br = line + width;
193 else
194 br++;
195 end = hbuf_b_curr->ptr_end;
196 hbuf_b_curr->ptr_end = br;
197 // Create another block, non-persistent
198 hbuf_b_curr = g_new0(hbuf_block, 1);
199 hbuf_b_curr->ptr = hbuf_b_prev->ptr_end; // == br
200 hbuf_b_curr->ptr_end = end;
201 hbuf_b_curr->flags = 0;
202 hbuf_b_curr->persist.ptr_end_alloc = hbuf_b_prev->persist.ptr_end_alloc;
203 /*
204 // Is there a better way?
205 if (g_list_next(curr_elt))
206 g_list_insert_before(*p_hbuf, curr_elt->next, hbuf_b_curr);
207 else
208 *p_hbuf = g_list_append(*p_hbuf, hbuf_b_curr);
209 */
210 // This is OK because insert_before(NULL) <==> append()
211 g_list_insert_before(*p_hbuf, curr_elt->next, hbuf_b_curr);
212 }
213 curr_elt = g_list_next(curr_elt);
214 }
215 }
216 }
217
218 // hbuf_get_lines(hbuf, n, where)
219 // Returns an array of n pointers (for n lines from hbuf)
220 // (The first line will be the line currently pointed by hbuf)
221 // Note:The caller should free the array after use.
222 char **hbuf_get_lines(GList *hbuf, unsigned int n)
223 {
224 unsigned int i;
225
226 char **array = g_new0(char*, n);
227 char **array_elt = array;
228
229 for (i=0 ; i < n ; i++) {
230 if (hbuf) {
231 hbuf_block *blk = (hbuf_block*)(hbuf->data);
232 int maxlen;
233 maxlen = blk->ptr_end - blk->ptr;
234 *array_elt++ = g_strndup(blk->ptr, maxlen);
235 hbuf = g_list_next(hbuf);
236 } else
237 *array_elt++ = NULL;
238 }
239
240 return array;
241 }
242