1 /*
2  * hbuf.c       -- History buffer implementation
3  *
4  * Copyright (C) 2005-2010 Mikael Berthe <mikael@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, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <string.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24 
25 #include "hbuf.h"
26 #include "utils.h"
27 #include "utf8.h"
28 #include "screen.h"
29 
30 
31 /* This is a private structure type */
32 
33 typedef struct {
34   char *ptr;
35   char *ptr_end;        // beginning of the block
36   char *ptr_end_alloc;  // end of the current persistent block
37   guchar flags;
38 
39   // XXX This should certainly be a pointer, and be allocated only when needed
40   // (for ex. when HBB_FLAG_PERSISTENT is set).
41   struct { // hbuf_line_info
42     time_t timestamp;
43     unsigned mucnicklen;
44     guint  flags;
45     gpointer xep184;
46   } prefix;
47 } hbuf_block_t;
48 
49 
50 //  do_wrap(p_hbuf, first_hbuf_elt, width)
51 // Wrap hbuf lines with the specified width.
52 // '\n' are handled by this routine (they are removed and persistent lines
53 // are created).
54 // All hbuf elements are processed, starting from first_hbuf_elt.
do_wrap(GList ** p_hbuf,GList * first_hbuf_elt,unsigned int width)55 static inline void do_wrap(GList **p_hbuf, GList *first_hbuf_elt,
56                            unsigned int width)
57 {
58   GList *curr_elt = first_hbuf_elt;
59 
60   // Let's add non-persistent blocs if necessary
61   // - If there are '\n' in the string
62   // - If length > width (and width != 0)
63   while (curr_elt) {
64     hbuf_block_t *hbuf_b_curr, *hbuf_b_prev;
65     char *c, *end;
66     char *br = NULL; // break pointer
67     char *cr = NULL; // CR pointer
68     unsigned int cur_w = 0;
69 
70     // We want to break where we can find a space char or a CR
71 
72     hbuf_b_curr = (hbuf_block_t*)(curr_elt->data);
73     hbuf_b_prev = hbuf_b_curr;
74     c = hbuf_b_curr->ptr;
75 
76     while (*c && (!width || cur_w <= width)) {
77       if (*c == '\n') {
78         br = cr = c;
79         *c = 0;
80         break;
81       }
82       if (iswblank(get_char(c)))
83         br = c;
84       cur_w += get_char_width(c);
85       c = next_char(c);
86     }
87 
88     if (cr || (*c && cur_w > width)) {
89       if (!br || br == hbuf_b_curr->ptr)
90         br = c;
91       else
92         br = next_char(br);
93       end = hbuf_b_curr->ptr_end;
94       hbuf_b_curr->ptr_end = br;
95       // Create another block
96       hbuf_b_curr = g_new0(hbuf_block_t, 1);
97       // The block must be persistent after a CR
98       if (cr) {
99         hbuf_b_curr->ptr    = hbuf_b_prev->ptr_end + 1; // == cr+1
100         hbuf_b_curr->flags  = HBB_FLAG_PERSISTENT;
101       } else {
102         hbuf_b_curr->ptr    = hbuf_b_prev->ptr_end; // == br
103         hbuf_b_curr->flags    = 0;
104       }
105       hbuf_b_curr->ptr_end  = end;
106       hbuf_b_curr->ptr_end_alloc = hbuf_b_prev->ptr_end_alloc;
107       // This is OK because insert_before(NULL) == append():
108       *p_hbuf = g_list_insert_before(*p_hbuf, curr_elt->next, hbuf_b_curr);
109     }
110     curr_elt = g_list_next(curr_elt);
111   }
112 }
113 
114 //  hbuf_add_line(p_hbuf, text, prefix_flags, width, maxhbufblocks)
115 // Add a line to the given buffer.  If width is not null, then lines are
116 // wrapped at this length.
117 // maxhbufblocks is the maximum number of hbuf blocks we can allocate.  If
118 // null, there is no limit.  If non-null, it should be >= 2.
119 //
120 // Note 1: Splitting according to width won't work if there are tabs; they
121 //         should be expanded before.
122 // Note 2: width does not include the ending \0.
hbuf_add_line(GList ** p_hbuf,const char * text,time_t timestamp,guint prefix_flags,guint width,guint maxhbufblocks,unsigned mucnicklen,gpointer xep184)123 void hbuf_add_line(GList **p_hbuf, const char *text, time_t timestamp,
124         guint prefix_flags, guint width, guint maxhbufblocks,
125         unsigned mucnicklen, gpointer xep184)
126 {
127   GList *curr_elt;
128   char *line;
129   guint hbb_blocksize, textlen;
130   hbuf_block_t *hbuf_block_elt;
131 
132   if (!text) return;
133 
134   prefix_flags |= (xep184 ? HBB_PREFIX_RECEIPT : 0);
135 
136   textlen = strlen(text);
137   hbb_blocksize = MAX(textlen+1, HBB_BLOCKSIZE);
138 
139   hbuf_block_elt = g_new0(hbuf_block_t, 1);
140   hbuf_block_elt->prefix.timestamp  = timestamp;
141   hbuf_block_elt->prefix.flags      = prefix_flags;
142   hbuf_block_elt->prefix.mucnicklen = mucnicklen;
143   hbuf_block_elt->prefix.xep184     = xep184;
144   if (!*p_hbuf) {
145     hbuf_block_elt->ptr  = g_new(char, hbb_blocksize);
146     if (!hbuf_block_elt->ptr) {
147       g_free(hbuf_block_elt);
148       return;
149     }
150     hbuf_block_elt->flags  = HBB_FLAG_ALLOC | HBB_FLAG_PERSISTENT;
151     hbuf_block_elt->ptr_end_alloc = hbuf_block_elt->ptr + hbb_blocksize;
152   } else {
153     hbuf_block_t *hbuf_b_prev;
154     // Set p_hbuf to the end of the list, to speed up history loading
155     // (or CPU time will be used by g_list_last() for each line)
156     *p_hbuf = g_list_last(*p_hbuf);
157     hbuf_b_prev = (*p_hbuf)->data;
158     hbuf_block_elt->ptr    = hbuf_b_prev->ptr_end;
159     hbuf_block_elt->flags  = HBB_FLAG_PERSISTENT;
160     hbuf_block_elt->ptr_end_alloc = hbuf_b_prev->ptr_end_alloc;
161   }
162   *p_hbuf = g_list_append(*p_hbuf, hbuf_block_elt);
163 
164   if (hbuf_block_elt->ptr + textlen >= hbuf_block_elt->ptr_end_alloc) {
165     // Too long for the current allocated bloc, we need another one
166     if (!maxhbufblocks || textlen >= HBB_BLOCKSIZE) {
167       // No limit, let's allocate a new block
168       // If the message text is big, we won't bother to reuse an old block
169       // as well (it could be too small and cause a segfault).
170       hbuf_block_elt->ptr  = g_new0(char, hbb_blocksize);
171       hbuf_block_elt->ptr_end_alloc = hbuf_block_elt->ptr + hbb_blocksize;
172       // XXX We should check the return value.
173     } else {
174       GList *hbuf_head, *hbuf_elt;
175       hbuf_block_t *hbuf_b_elt;
176       guint n = 0;
177       hbuf_head = g_list_first(*p_hbuf);
178       // We need at least 2 allocated blocks
179       if (maxhbufblocks == 1)
180         maxhbufblocks = 2;
181       // Let's count the number of allocated areas
182       for (hbuf_elt = hbuf_head; hbuf_elt; hbuf_elt = g_list_next(hbuf_elt)) {
183         hbuf_b_elt = (hbuf_block_t*)(hbuf_elt->data);
184         if (hbuf_b_elt->flags & HBB_FLAG_ALLOC)
185           n++;
186       }
187       // If we can't allocate a new area, reuse the previous block(s)
188       if (n < maxhbufblocks) {
189         hbuf_block_elt->ptr  = g_new0(char, hbb_blocksize);
190         hbuf_block_elt->ptr_end_alloc = hbuf_block_elt->ptr + hbb_blocksize;
191       } else {
192         // Let's use an old block, and free the extra blocks if needed
193         char *allocated_block = NULL;
194         char *end_of_allocated_block = NULL;
195         while (n >= maxhbufblocks) {
196           int start_of_block = 1;
197           for (hbuf_elt = hbuf_head; hbuf_elt; hbuf_elt = hbuf_head) {
198             hbuf_b_elt = (hbuf_block_t*)(hbuf_elt->data);
199             if (hbuf_b_elt->flags & HBB_FLAG_ALLOC) {
200               if (start_of_block-- == 0)
201                 break;
202               if (n == maxhbufblocks) {
203                 allocated_block = hbuf_b_elt->ptr;
204                 end_of_allocated_block = hbuf_b_elt->ptr_end_alloc;
205               } else {
206                 g_free(hbuf_b_elt->ptr);
207               }
208             }
209             g_free(hbuf_b_elt);
210             hbuf_head = *p_hbuf = g_list_delete_link(hbuf_head, hbuf_elt);
211           }
212           n--;
213         }
214         memset(allocated_block, 0, end_of_allocated_block-allocated_block);
215         hbuf_block_elt->ptr = allocated_block;
216         hbuf_block_elt->ptr_end_alloc = end_of_allocated_block;
217       }
218     }
219     hbuf_block_elt->flags  = HBB_FLAG_ALLOC | HBB_FLAG_PERSISTENT;
220   }
221 
222   line = hbuf_block_elt->ptr;
223   // Ok, now we can copy the text..
224   strcpy(line, text);
225   hbuf_block_elt->ptr_end = line + textlen + 1;
226 
227   curr_elt = g_list_last(*p_hbuf);
228 
229   // Wrap lines and handle CRs ('\n')
230   do_wrap(p_hbuf, curr_elt, width);
231 }
232 
233 //  hbuf_free()
234 // Destroys all hbuf list.
hbuf_free(GList ** p_hbuf)235 void hbuf_free(GList **p_hbuf)
236 {
237   hbuf_block_t *hbuf_b_elt;
238   GList *hbuf_elt;
239   GList *first_elt = g_list_first(*p_hbuf);
240 
241   for (hbuf_elt = first_elt; hbuf_elt; hbuf_elt = g_list_next(hbuf_elt)) {
242     hbuf_b_elt = (hbuf_block_t*)(hbuf_elt->data);
243     if (hbuf_b_elt->flags & HBB_FLAG_ALLOC) {
244       g_free(hbuf_b_elt->ptr);
245     }
246     g_free(hbuf_b_elt);
247   }
248 
249   g_list_free(first_elt);
250   *p_hbuf = NULL;
251 }
252 
253 //  hbuf_rebuild()
254 // Rebuild all hbuf list, with the new width.
255 // If width == 0, lines are not wrapped.
hbuf_rebuild(GList ** p_hbuf,unsigned int width)256 void hbuf_rebuild(GList **p_hbuf, unsigned int width)
257 {
258   GList *first_elt, *curr_elt, *next_elt;
259   hbuf_block_t *hbuf_b_curr, *hbuf_b_next;
260 
261   // *p_hbuf needs to be the head of the list
262   first_elt = *p_hbuf = g_list_first(*p_hbuf);
263 
264   // #1 Remove non-persistent blocks (ptr_end should be updated!)
265   curr_elt = first_elt;
266   while (curr_elt) {
267     next_elt = g_list_next(curr_elt);
268     // Last element?
269     if (!next_elt)
270       break;
271     hbuf_b_curr = (hbuf_block_t*)(curr_elt->data);
272     hbuf_b_next = (hbuf_block_t*)(next_elt->data);
273     // Is next line not-persistent?
274     if (!(hbuf_b_next->flags & HBB_FLAG_PERSISTENT)) {
275       hbuf_b_curr->ptr_end = hbuf_b_next->ptr_end;
276       g_free(hbuf_b_next);
277       curr_elt = g_list_delete_link(curr_elt, next_elt);
278     } else
279       curr_elt = next_elt;
280   }
281   // #2 Go back to head and create non-persistent blocks when needed
282   if (width)
283     do_wrap(p_hbuf, first_elt, width);
284 }
285 
286 //  hbuf_previous_persistent()
287 // Returns the previous persistent block (line).  If the given line is
288 // persistent, then it is returned.
289 // This function is used for example when resizing a buffer.  If the top of the
290 // screen is on a non-persistent block, then a screen resize could destroy this
291 // line...
hbuf_previous_persistent(GList * l_line)292 GList *hbuf_previous_persistent(GList *l_line)
293 {
294   hbuf_block_t *hbuf_b_elt;
295 
296   while (l_line) {
297     hbuf_b_elt = (hbuf_block_t*)l_line->data;
298     if (hbuf_b_elt->flags & HBB_FLAG_PERSISTENT &&
299         (hbuf_b_elt->flags & ~HBB_PREFIX_READMARK))
300       return l_line;
301     l_line = g_list_previous(l_line);
302   }
303 
304   return NULL;
305 }
306 
307 //  hbuf_get_lines(hbuf, n)
308 // Returns an array of n hbb_line pointers
309 // (The first line will be the line currently pointed by hbuf)
310 // Note: The caller should free the array, the hbb_line pointers and the
311 // text pointers after use.
hbuf_get_lines(GList * hbuf,unsigned int n)312 hbb_line **hbuf_get_lines(GList *hbuf, unsigned int n)
313 {
314   unsigned int i;
315   hbuf_block_t *blk;
316   guint last_persist_prefixflags = 0;
317   GList *last_persist;  // last persistent flags
318   hbb_line **array, **array_elt;
319   hbb_line *prev_array_elt = NULL;
320 
321   // To be able to correctly highlight multi-line messages,
322   // we need to look at the last non-null prefix, which should be the first
323   // line of the message.  We also need to check if there's a readmark flag
324   // somewhere in the message.
325   last_persist = hbuf_previous_persistent(hbuf);
326   while (last_persist) {
327     blk = (hbuf_block_t*)last_persist->data;
328     if ((blk->flags & HBB_FLAG_PERSISTENT) && blk->prefix.flags) {
329       // This can be either the beginning of the message,
330       // or a persistent line with a readmark flag (or both).
331       if (blk->prefix.flags & ~HBB_PREFIX_READMARK) { // First message line
332         last_persist_prefixflags |= blk->prefix.flags;
333         break;
334       } else { // Not the first line, but we need to keep the readmark flag
335         last_persist_prefixflags = blk->prefix.flags;
336       }
337     }
338     last_persist = g_list_previous(last_persist);
339   }
340 
341   array = g_new0(hbb_line*, n);
342   array_elt = array;
343 
344   for (i = 0 ; i < n ; i++) {
345     if (hbuf) {
346       int maxlen;
347 
348       blk = (hbuf_block_t*)(hbuf->data);
349       maxlen = blk->ptr_end - blk->ptr;
350       *array_elt = (hbb_line*)g_new(hbb_line, 1);
351       (*array_elt)->timestamp  = blk->prefix.timestamp;
352       (*array_elt)->flags      = blk->prefix.flags;
353       (*array_elt)->mucnicklen = blk->prefix.mucnicklen;
354       (*array_elt)->text       = g_strndup(blk->ptr, maxlen);
355 
356       if ((blk->flags & HBB_FLAG_PERSISTENT) &&
357           (blk->prefix.flags & ~HBB_PREFIX_READMARK)) {
358         // This is a new message: persistent block flag and no prefix flag
359         // (except a possible readmark flag)
360         last_persist_prefixflags = blk->prefix.flags;
361       } else {
362         // Propagate highlighting flags
363         (*array_elt)->flags |= last_persist_prefixflags &
364                                (HBB_PREFIX_HLIGHT_OUT | HBB_PREFIX_HLIGHT |
365                                 HBB_PREFIX_INFO | HBB_PREFIX_IN |
366                                 HBB_PREFIX_READMARK);
367         // Continuation of a message - omit the prefix
368         (*array_elt)->flags |= HBB_PREFIX_CONT;
369         (*array_elt)->mucnicklen = 0; // The nick is in the first one
370 
371         // If there is a readmark on this line, update last_persist_prefixflags
372         if (blk->flags & HBB_FLAG_PERSISTENT)
373           last_persist_prefixflags |= blk->prefix.flags & HBB_PREFIX_READMARK;
374         // Remove readmark flag from the previous line
375         if (prev_array_elt && last_persist_prefixflags & HBB_PREFIX_READMARK)
376           prev_array_elt->flags &= ~HBB_PREFIX_READMARK;
377       }
378 
379       prev_array_elt = *array_elt;
380 
381       hbuf = g_list_next(hbuf);
382     } else
383       break;
384 
385     array_elt++;
386   }
387 
388   return array;
389 }
390 
391 //  hbuf_search(hbuf, direction, string)
392 // Look backward/forward for a line containing string in the history buffer
393 // Search starts at hbuf, and goes forward if direction == 1, backward if -1
hbuf_search(GList * hbuf,int direction,const char * string)394 GList *hbuf_search(GList *hbuf, int direction, const char *string)
395 {
396   hbuf_block_t *blk;
397 
398   for (;;) {
399     if (direction > 0)
400       hbuf = g_list_next(hbuf);
401     else
402       hbuf = g_list_previous(hbuf);
403 
404     if (!hbuf) break;
405 
406     blk = (hbuf_block_t*)(hbuf->data);
407     // XXX blk->ptr is (maybe) not really correct, because the match should
408     // not be after ptr_end.  We should check that...
409     if (strcasestr(blk->ptr, string))
410       break;
411   }
412 
413   return hbuf;
414 }
415 
416 //  hbuf_jump_date(hbuf, t)
417 // Return a pointer to the first line after date t in the history buffer
hbuf_jump_date(GList * hbuf,time_t t)418 GList *hbuf_jump_date(GList *hbuf, time_t t)
419 {
420   hbuf_block_t *blk;
421 
422   hbuf = g_list_first(hbuf);
423 
424   for ( ; hbuf && g_list_next(hbuf); hbuf = g_list_next(hbuf)) {
425     blk = (hbuf_block_t*)(hbuf->data);
426     if (blk->prefix.timestamp >= t) break;
427   }
428 
429   return hbuf;
430 }
431 
432 //  hbuf_jump_percent(hbuf, pc)
433 // Return a pointer to the line at % pc of the history buffer
hbuf_jump_percent(GList * hbuf,int pc)434 GList *hbuf_jump_percent(GList *hbuf, int pc)
435 {
436   guint hlen;
437 
438   hbuf = g_list_first(hbuf);
439   hlen = g_list_length(hbuf);
440 
441   return g_list_nth(hbuf, pc*hlen/100);
442 }
443 
444 //  hbuf_jump_readmark(hbuf)
445 // Return a pointer to the line following the readmark
446 // or NULL if no mark was found.
hbuf_jump_readmark(GList * hbuf)447 GList *hbuf_jump_readmark(GList *hbuf)
448 {
449   hbuf_block_t *blk;
450   GList *r = NULL;
451 
452   hbuf = g_list_last(hbuf);
453   for ( ; hbuf; hbuf = g_list_previous(hbuf)) {
454     blk = (hbuf_block_t*)(hbuf->data);
455     if (blk->prefix.flags & HBB_PREFIX_READMARK)
456       return r;
457     if ((blk->flags & HBB_FLAG_PERSISTENT) &&
458         (blk->prefix.flags & ~HBB_PREFIX_READMARK))
459       r = hbuf;
460   }
461 
462   return NULL;
463 }
464 
465 //  hbuf_dump_to_file(hbuf, filename)
466 // Save the buffer to a file.
hbuf_dump_to_file(GList * hbuf,const char * filename)467 void hbuf_dump_to_file(GList *hbuf, const char *filename)
468 {
469   hbuf_block_t *blk;
470   hbb_line line;
471   guint last_persist_prefixflags = 0;
472   guint prefixwidth;
473   char pref[96];
474   FILE *fp;
475   struct stat statbuf;
476 
477   if (!stat(filename, &statbuf)) {
478     scr_LogPrint(LPRINT_NORMAL, "The file already exists.");
479     return;
480   }
481   fp = fopen(filename, "w");
482   if (!fp) {
483     scr_LogPrint(LPRINT_NORMAL, "Unable to open the file.");
484     return;
485   }
486 
487   prefixwidth = scr_getprefixwidth();
488   prefixwidth = MIN(prefixwidth, sizeof pref);
489 
490   for (hbuf = g_list_first(hbuf); hbuf; hbuf = g_list_next(hbuf)) {
491     int maxlen;
492 
493     blk = (hbuf_block_t*)(hbuf->data);
494     maxlen = blk->ptr_end - blk->ptr;
495 
496     memset(&line, 0, sizeof(line));
497     line.timestamp  = blk->prefix.timestamp;
498     line.flags      = blk->prefix.flags;
499     line.mucnicklen = blk->prefix.mucnicklen;
500     line.text       = g_strndup(blk->ptr, maxlen);
501 
502     if ((blk->flags & HBB_FLAG_PERSISTENT) &&
503         (blk->prefix.flags & ~HBB_PREFIX_READMARK)) {
504       last_persist_prefixflags = blk->prefix.flags;
505     } else {
506       // Propagate necessary highlighting flags
507       line.flags |= last_persist_prefixflags &
508                     (HBB_PREFIX_HLIGHT_OUT | HBB_PREFIX_HLIGHT |
509                      HBB_PREFIX_INFO | HBB_PREFIX_IN);
510       // Continuation of a message - omit the prefix
511       line.flags |= HBB_PREFIX_CONT;
512       line.mucnicklen = 0; // The nick is in the first one
513     }
514 
515     scr_line_prefix(&line, pref, prefixwidth);
516     fprintf(fp, "%s%s\n", pref, line.text);
517   }
518 
519   fclose(fp);
520   return;
521 }
522 
523 //  hbuf_remove_receipt(hbuf, xep184)
524 // Remove the Receipt Flag for the message with the given xep184 id
525 // Returns TRUE if it was found and removed, otherwise FALSE
hbuf_remove_receipt(GList * hbuf,gconstpointer xep184)526 gboolean hbuf_remove_receipt(GList *hbuf, gconstpointer xep184)
527 {
528   hbuf_block_t *blk;
529 
530   hbuf = g_list_last(hbuf);
531 
532   for ( ; hbuf; hbuf = g_list_previous(hbuf)) {
533     blk = (hbuf_block_t*)(hbuf->data);
534     if (!g_strcmp0(blk->prefix.xep184, xep184)) {
535       g_free(blk->prefix.xep184);
536       blk->prefix.xep184 = NULL;
537       blk->prefix.flags ^= HBB_PREFIX_RECEIPT;
538       return TRUE;
539     }
540   }
541   return FALSE;
542 }
543 
544 //  hbuf_set_readmark(hbuf, action)
545 // Set/Reset the readmark Flag
546 // If action is TRUE, set a mark to the latest line,
547 // if action is FALSE, remove a previous readmark flag.
hbuf_set_readmark(GList * hbuf,gboolean action)548 void hbuf_set_readmark(GList *hbuf, gboolean action)
549 {
550   hbuf_block_t *blk;
551 
552   if (!hbuf) return;
553 
554   hbuf = hbuf_previous_persistent(g_list_last(hbuf));
555 
556   if (action) {
557     // Add a readmark flag
558     blk = (hbuf_block_t*)(hbuf->data);
559     blk->prefix.flags |= HBB_PREFIX_READMARK;
560 
561     // Shift hbuf in order to remove previous flags
562     // (maybe it can be optimized out, if there's no risk
563     //  we have several marks)
564     hbuf = g_list_previous(hbuf);
565   }
566 
567   // Remove old mark
568   for ( ; hbuf; hbuf = g_list_previous(hbuf)) {
569     blk = (hbuf_block_t*)(hbuf->data);
570     if (blk->prefix.flags & HBB_PREFIX_READMARK) {
571       blk->prefix.flags &= ~HBB_PREFIX_READMARK;
572       break;
573     }
574   }
575 }
576 
577 //  hbuf_remove_trailing_readmark(hbuf)
578 // Unset the buffer readmark if it is on the last line
hbuf_remove_trailing_readmark(GList * hbuf)579 void hbuf_remove_trailing_readmark(GList *hbuf)
580 {
581   hbuf_block_t *blk;
582 
583   if (!hbuf) return;
584 
585   hbuf = g_list_last(hbuf);
586   blk = (hbuf_block_t*)(hbuf->data);
587   blk->prefix.flags &= ~HBB_PREFIX_READMARK;
588 }
589 
590 //  hbuf_get_blocks_number()
591 // Returns the number of allocated hbuf_block_t's.
hbuf_get_blocks_number(GList * hbuf)592 guint hbuf_get_blocks_number(GList *hbuf)
593 {
594   hbuf_block_t *hbuf_b_elt;
595   guint count = 0U;
596 
597   for (hbuf = g_list_first(hbuf); hbuf; hbuf = g_list_next(hbuf)) {
598     hbuf_b_elt = (hbuf_block_t*)(hbuf->data);
599     if (hbuf_b_elt->flags & HBB_FLAG_ALLOC)
600       count++;
601   }
602   return count;
603 }
604 
605 /* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2:  For Vim users... */
606