Mercurial > ~mikael > mcabber > hg
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 |