Have the frame_tvbuff.c routines not use the global cfile.
[metze/wireshark/wip.git] / ui / gtk / packet_list_store.c
1 /* packet_list_store.c
2  * Routines to implement a custom GTK+ list model for Wireshark's packet list
3  * Copyright 2008-2009, Stephen Fisher (see AUTHORS file)
4  * * Co-authors Anders Broman and Kovarththanan Rajaratnam.
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
23  * USA.
24  */
25
26 /* This code was originally based on the GTK+ Tree View tutorial at
27  * http://scentric.net/tutorial */
28
29 #include "config.h"
30
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include <gtk/gtk.h>
35
36 #include "packet_list_store.h"
37
38 #include "ui/progress_dlg.h"
39 #include "ui/ws_ui_util.h"
40
41 #include "ui/gtk/old-gtk-compat.h"
42
43 #include <epan/epan_dissect.h>
44 #include <epan/column.h>
45
46 #include <epan/color_filters.h>
47 #include "frame_tvbuff.h"
48
49 #include "globals.h"
50
51 /* #define PACKET_PARANOID_CHECKS */
52
53 /** PacketListRecord: represents a row */
54 typedef struct _PacketListRecord {
55         /** The column text for some columns */
56         const gchar **col_text;
57         /**< The length of the column text strings in 'col_text' */
58         gushort *col_text_len;
59
60         frame_data *fdata;
61
62         /* admin stuff used by the custom list model */
63 #ifdef PACKET_PARANOID_CHECKS
64         /** position within the physical array */
65         guint physical_pos;
66 #endif
67         /** position within the visible array */
68         gint visible_pos;
69
70         /** Has this record been colorized? */
71         guint colorized : 1;
72
73 } PacketListRecord;
74
75 static void packet_list_init(PacketList *pkg_tree);
76 static void packet_list_class_init(PacketListClass *klass);
77 static void packet_list_tree_model_init(GtkTreeModelIface *iface);
78 static void packet_list_finalize(GObject *object);
79 static GtkTreeModelFlags packet_list_get_flags(GtkTreeModel *tree_model);
80 static gint packet_list_get_n_columns(GtkTreeModel *tree_model);
81 static GType packet_list_get_column_type(GtkTreeModel *tree_model, gint idx);
82 static gboolean packet_list_get_iter(GtkTreeModel *tree_model,
83                                          GtkTreeIter *iter, GtkTreePath *path);
84 static GtkTreePath *packet_list_get_path(GtkTreeModel *tree_model,
85                                          GtkTreeIter *iter);
86 static void packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter,
87                                   gint column, GValue *value);
88 static gboolean packet_list_iter_next(GtkTreeModel *tree_model,
89                                           GtkTreeIter *iter);
90 static gboolean packet_list_iter_children(GtkTreeModel *tree_model,
91                                           GtkTreeIter *iter,
92                                           GtkTreeIter *parent);
93 static gboolean packet_list_iter_has_child(GtkTreeModel *tree_model _U_,
94                                            GtkTreeIter *iter _U_);
95 static gint packet_list_iter_n_children(GtkTreeModel *tree_model,
96                                         GtkTreeIter *iter);
97 static gboolean packet_list_iter_nth_child(GtkTreeModel *tree_model,
98                                            GtkTreeIter *iter,
99                                            GtkTreeIter *parent,
100                                            gint n);
101 static gboolean packet_list_iter_parent(GtkTreeModel *tree_model _U_,
102                                         GtkTreeIter *iter _U_,
103                                         GtkTreeIter *child _U_);
104
105 static gboolean packet_list_sortable_get_sort_column_id(GtkTreeSortable
106                                                         *sortable,
107                                                         gint *sort_col_id,
108                                                         GtkSortType *order);
109 static void packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
110                                                         gint sort_col_id,
111                                                         GtkSortType order);
112 static void packet_list_sortable_set_sort_func(GtkTreeSortable *sortable,
113                                                    gint sort_col_id,
114                                                    GtkTreeIterCompareFunc sort_func,
115                                                    gpointer user_data,
116                                                    GDestroyNotify destroy_func);
117 static void packet_list_sortable_set_default_sort_func(GtkTreeSortable
118                                                            *sortable,
119                                                            GtkTreeIterCompareFunc
120                                                            sort_func,
121                                                            gpointer user_data,
122                                                            GDestroyNotify
123                                                            destroy_func);
124 static gboolean packet_list_sortable_has_default_sort_func(GtkTreeSortable
125                                                            *sortable);
126 static void packet_list_sortable_init(GtkTreeSortableIface *iface);
127 static void packet_list_resort(PacketList *packet_list);
128 static void packet_list_dissect_and_cache_record(PacketList *packet_list, PacketListRecord *record, gboolean dissect_color );
129
130 static GObjectClass *parent_class = NULL;
131
132
133 GType
134 packet_list_get_type(void)
135 {
136         static GType packet_list_type = 0;
137
138         if(packet_list_type == 0) {
139                 static const GTypeInfo packet_list_info = {
140                         sizeof(PacketListClass),
141                         NULL, /* base_init */
142                         NULL, /* base_finalize */
143                         (GClassInitFunc) packet_list_class_init,
144                         NULL, /* class finalize */
145                         NULL, /* class_data */
146                         sizeof(PacketList),
147                         0, /* n_preallocs */
148                         (GInstanceInitFunc) packet_list_init,
149                         NULL /* value_table */
150                 };
151
152                 static const GInterfaceInfo tree_model_info = {
153                         (GInterfaceInitFunc) packet_list_tree_model_init,
154                         NULL,
155                         NULL
156                 };
157
158                 static const GInterfaceInfo tree_sortable_info = {
159                                 (GInterfaceInitFunc) packet_list_sortable_init,
160                                 NULL,
161                                 NULL
162                 };
163
164                 /* Register the new derived type with the GObject type system */
165                 packet_list_type = g_type_register_static(G_TYPE_OBJECT,
166                                                           "PacketList",
167                                                           &packet_list_info,
168                                                           (GTypeFlags)0);
169
170                 g_type_add_interface_static(packet_list_type,
171                                                 GTK_TYPE_TREE_MODEL,
172                                                 &tree_model_info);
173
174
175                 /* Register our GtkTreeModel interface with the type system */
176                 g_type_add_interface_static(packet_list_type,
177                                                 GTK_TYPE_TREE_SORTABLE,
178                                                 &tree_sortable_info);
179         }
180
181         return packet_list_type;
182 }
183
184 static void
185 packet_list_sortable_init(GtkTreeSortableIface *iface)
186 {
187         iface->get_sort_column_id = packet_list_sortable_get_sort_column_id;
188         iface->set_sort_column_id = packet_list_sortable_set_sort_column_id;
189         /* The following three functions are not implemented */
190         iface->set_sort_func = packet_list_sortable_set_sort_func;
191         iface->set_default_sort_func =
192                 packet_list_sortable_set_default_sort_func;
193         iface->has_default_sort_func =
194                 packet_list_sortable_has_default_sort_func;
195 }
196
197 static void
198 packet_list_class_init(PacketListClass *klass)
199 {
200         GObjectClass *object_class;
201
202         parent_class = (GObjectClass*) g_type_class_peek_parent(klass);
203         object_class = (GObjectClass*) klass;
204
205         object_class->finalize = packet_list_finalize;
206
207 #if !GTK_CHECK_VERSION(3,0,0)
208         /* XXX this seems to affect TreeView Application wide
209          * Move to main.c ??? as it's not a bad thing(tm)
210          */
211         gtk_rc_parse_string (
212                 "style \"PacketList-style\"\n"
213                 "{\n"
214                 "  GtkTreeView::horizontal-separator = 0\n"
215                 "  GtkTreeView::vertical-separator = 1\n"
216                 "} widget_class \"*TreeView*\""
217                 " style \"PacketList-style\"");
218 #endif
219
220 }
221
222 static void
223 packet_list_tree_model_init(GtkTreeModelIface *iface)
224 {
225         iface->get_flags = packet_list_get_flags;
226         iface->get_n_columns = packet_list_get_n_columns;
227         iface->get_column_type = packet_list_get_column_type;
228         iface->get_iter = packet_list_get_iter;
229         iface->get_path = packet_list_get_path;
230         iface->get_value = packet_list_get_value;
231         iface->iter_next = packet_list_iter_next;
232         iface->iter_children = packet_list_iter_children;
233         iface->iter_has_child = packet_list_iter_has_child;
234         iface->iter_n_children = packet_list_iter_n_children;
235         iface->iter_nth_child = packet_list_iter_nth_child;
236         iface->iter_parent = packet_list_iter_parent;
237 }
238
239 /* This is called every time a new packet list object instance is created in
240  * packet_list_new.  Initialize the list structure's fields here. */
241 static void
242 packet_list_init(PacketList *packet_list)
243 {
244         gint i, j;
245
246         /* To check whether an iter belongs to our model. */
247         packet_list->stamp = g_random_int();
248
249         packet_list->n_cols = cfile.cinfo.num_cols;
250         packet_list->physical_rows = g_ptr_array_new();
251         packet_list->visible_rows = g_ptr_array_new();
252
253         packet_list->columnized = FALSE;
254         packet_list->sort_id = 0; /* defaults to first column for now */
255         packet_list->sort_order = GTK_SORT_ASCENDING;
256
257         packet_list->col_to_text = g_new(int, packet_list->n_cols);
258         for (i = 0, j = 0; i < packet_list->n_cols; i++) {
259                 if (!col_based_on_frame_data(&cfile.cinfo, i)) {
260                         packet_list->col_to_text[i] = j;
261                         j++;
262                 } else
263                         packet_list->col_to_text[i] = -1;
264         }
265         packet_list->n_text_cols = j;
266
267 #ifdef PACKET_LIST_STATISTICS
268         packet_list->const_strings = 0;
269 #endif
270 }
271
272 /* This function is called just before a packet list is destroyed.      Free
273  * dynamically allocated memory here. */
274 static void
275 packet_list_finalize(GObject *object)
276 {
277         /* PacketList *packet_list = PACKET_LIST(object); */
278
279         /* XXX - Free all records and free all memory used by the list */
280
281         /* must chain up - finalize parent */
282         (* parent_class->finalize) (object);
283 }
284
285 static GtkTreeModelFlags
286 packet_list_get_flags(GtkTreeModel *tree_model)
287 {
288         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model),
289                                  (GtkTreeModelFlags)0);
290
291         return (GtkTreeModelFlags)(GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
292 }
293
294 static gint
295 packet_list_get_n_columns(GtkTreeModel *tree_model)
296 {
297         PacketList *packet_list;
298
299         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
300         packet_list = (PacketList *) tree_model;
301
302         /* Note: We need one extra column to store the entire frame_data */
303         return packet_list->n_cols + 1;
304 }
305
306 static GType
307 packet_list_get_column_type(GtkTreeModel *tree_model, gint idx)
308 {
309         PacketList *packet_list;
310
311         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), G_TYPE_INVALID);
312         packet_list = (PacketList *) tree_model;
313
314         /* Note: We use one extra column to store the entire frame_data */
315         g_return_val_if_fail(idx >= 0 && idx < packet_list->n_cols + 1, G_TYPE_INVALID);
316
317         if (idx >= 0 && idx < packet_list->n_cols)
318                 return G_TYPE_STRING;
319         else if (idx == packet_list->n_cols)
320                 return G_TYPE_POINTER;
321         else
322                 return G_TYPE_INVALID;
323 }
324
325 static gboolean
326 packet_list_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter,
327                          GtkTreePath *path)
328 {
329         gint *indices, depth;
330
331         g_assert(PACKETLIST_IS_LIST(tree_model));
332
333         g_assert(path != NULL);
334
335         indices = gtk_tree_path_get_indices(path);
336         depth = gtk_tree_path_get_depth(path);
337
338         /* we do not allow children since it's just a list */
339         g_assert(depth == 1);
340
341         return packet_list_iter_nth_child(tree_model, iter, NULL, indices[0]);
342 }
343
344 static GtkTreePath *
345 packet_list_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter)
346 {
347         GtkTreePath *path;
348         PacketListRecord *record;
349         PacketList *packet_list;
350
351         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), NULL);
352         packet_list = (PacketList *) tree_model;
353
354         g_return_val_if_fail(iter != NULL, NULL);
355         g_return_val_if_fail(iter->stamp == packet_list->stamp, NULL);
356         g_return_val_if_fail(iter->user_data != NULL, NULL);
357
358         record = (PacketListRecord*) iter->user_data;
359
360         path = gtk_tree_path_new();
361         gtk_tree_path_append_index(path, record->visible_pos);
362
363         return path;
364 }
365
366 static void
367 packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column,
368                           GValue *value)
369 {
370         PacketListRecord *record;
371         PacketList *packet_list;
372
373         g_return_if_fail(PACKETLIST_IS_LIST(tree_model));
374         packet_list = (PacketList *) tree_model;
375
376         g_return_if_fail(iter != NULL);
377         g_return_if_fail(iter->stamp == packet_list->stamp);
378         g_return_if_fail(iter->user_data != NULL);
379
380         /* Note: We use one extra column to store the entire frame_data */
381         g_return_if_fail(column >= 0 && column < packet_list->n_cols + 1);
382
383         record = (PacketListRecord*) iter->user_data;
384
385 #ifdef PACKET_PARANOID_CHECKS
386         g_return_if_fail(PACKET_LIST_RECORD_INDEX_VALID(packet_list->physical_rows, record->physical_pos));
387 #endif
388         g_return_if_fail(PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, record->visible_pos));
389
390         if (column >= 0 && column < packet_list->n_cols) {
391                 int text_column;
392
393                 g_value_init(value, G_TYPE_STRING);
394
395                 if (record->col_text == NULL || !record->colorized)
396                         packet_list_dissect_and_cache_record(packet_list, record, !record->colorized);
397
398                 text_column = packet_list->col_to_text[column];
399                 if (text_column == -1) { /* column based on frame_data */
400                         col_fill_in_frame_data(record->fdata, &cfile.cinfo, column, FALSE);
401                         g_value_set_string(value, cfile.cinfo.columns[column].col_data);
402                 } else {
403                         g_return_if_fail(record->col_text);
404                         g_value_set_string(value, record->col_text[text_column]);
405                 }
406
407         } else if (column == packet_list->n_cols) {
408                 g_value_init(value, G_TYPE_POINTER);
409                 g_value_set_pointer(value, record->fdata);
410         }
411 }
412
413 static PacketListRecord *
414 packet_list_iter_next_visible(PacketList *packet_list, PacketListRecord *record)
415 {
416         PacketListRecord *nextrecord;
417         gint next_visible_pos;
418
419         g_assert(record->visible_pos >= 0);
420         next_visible_pos = record->visible_pos + 1;
421
422         /* Is this the last record in the list? */
423         if(!PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, next_visible_pos))
424                 return NULL;
425
426         nextrecord = PACKET_LIST_RECORD_GET(packet_list->visible_rows, next_visible_pos);
427
428         g_assert(nextrecord->visible_pos == (record->visible_pos + 1));
429 #ifdef PACKET_PARANOID_CHECKS
430         g_assert(nextrecord->physical_pos >= (record->physical_pos + 1));
431 #endif
432
433         return nextrecord;
434 }
435
436 /* Takes an iter structure and sets it to point to the next row. */
437 static gboolean
438 packet_list_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
439 {
440         PacketListRecord *record, *nextrecord;
441         PacketList *packet_list;
442
443         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
444         packet_list = (PacketList *) tree_model;
445
446         if(iter == NULL)
447                 return FALSE;
448
449         g_return_val_if_fail(iter->stamp == packet_list->stamp, FALSE);
450         g_return_val_if_fail(iter->user_data, FALSE);
451
452         record = (PacketListRecord*) iter->user_data;
453         nextrecord = packet_list_iter_next_visible(packet_list, record);
454
455         if (!nextrecord)
456                 return FALSE;
457
458         /* iter->stamp = packet_list->stamp; */
459         iter->user_data = nextrecord;
460
461         return TRUE;
462 }
463
464 static gboolean
465 packet_list_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter,
466                           GtkTreeIter *parent)
467 {
468         return packet_list_iter_nth_child(tree_model, iter, parent, 0);
469 }
470
471 static gboolean
472 packet_list_iter_has_child(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_)
473 {
474         return FALSE; /* Lists have no children */
475 }
476
477 static gint
478 packet_list_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
479 {
480         PacketList *packet_list;
481
482         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
483         packet_list = (PacketList *) tree_model;
484
485         if(!iter) {
486                 /* special case: if iter == NULL, return number of top-level rows */
487                 return PACKET_LIST_RECORD_COUNT(packet_list->visible_rows);
488         }
489         else {
490                 g_return_val_if_fail(iter->stamp == packet_list->stamp, 0);
491                 g_return_val_if_fail(iter->user_data, 0);
492                 /* Lists have zero children */
493                 return 0;
494         }
495 }
496
497 static gboolean
498 packet_list_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter,
499                            GtkTreeIter *parent, gint n)
500 {
501         PacketListRecord *record;
502         PacketList *packet_list;
503
504         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
505         packet_list = (PacketList *) tree_model;
506
507         /* A list only has top-level rows */
508         if(parent) {
509                 g_return_val_if_fail(parent->stamp == packet_list->stamp, FALSE);
510                 g_return_val_if_fail(parent->user_data, FALSE);
511                 return FALSE;
512         }
513
514         /* Special case: if parent == NULL, set iter to n-th top-level row. */
515         if(!PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, n))
516                 return FALSE;
517
518         record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, n);
519
520         g_assert(record->visible_pos == n);
521
522         iter->stamp = packet_list->stamp;
523         iter->user_data = record;
524
525         return TRUE;
526 }
527
528 static gboolean
529 packet_list_iter_parent(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_,
530                         GtkTreeIter *child _U_)
531 {
532         return FALSE; /* No parents since no children in a list */
533 }
534
535 PacketList *
536 packet_list_new(void)
537 {
538         PacketList *newpacketlist;
539
540         newpacketlist = (PacketList*) g_object_new(PACKETLIST_TYPE_LIST, NULL);
541
542         g_assert(newpacketlist != NULL);
543
544         return newpacketlist;
545 }
546
547 #if 0
548 static void
549 packet_list_row_deleted(PacketList *packet_list, guint pos)
550 {
551         GtkTreePath *path;
552
553         /* Inform the tree view and other interested objects (such as tree row
554          * references) that we have deleted a row */
555         path = gtk_tree_path_new();
556         gtk_tree_path_append_index(path, pos);
557
558         gtk_tree_model_row_deleted(GTK_TREE_MODEL(packet_list), path);
559
560         gtk_tree_path_free(path);
561 }
562 #endif
563
564 void
565 packet_list_store_clear(PacketList *packet_list)
566 {
567         g_return_if_fail(packet_list != NULL);
568         g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
569
570         /* Don't issue a row_deleted signal. We rely on our caller to have disconnected
571          * the model from the view.
572         for( ; packet_list->num_rows > 0; --packet_list->num_rows)
573                 packet_list_row_deleted(packet_list, packet_list->num_rows-1);
574         */
575
576         /* XXX - hold on to these rows and reuse them instead */
577         if(packet_list->physical_rows)
578                 g_ptr_array_free(packet_list->physical_rows, TRUE);
579         if(packet_list->visible_rows)
580                 g_ptr_array_free(packet_list->visible_rows, TRUE);
581         packet_list->physical_rows = g_ptr_array_new();
582         packet_list->visible_rows = g_ptr_array_new();
583
584         packet_list->columnized = FALSE;
585
586         /* Generate new number */
587         packet_list->stamp = g_random_int();
588
589         if (packet_list->string_pool) {
590                 g_string_chunk_clear(packet_list->string_pool);
591         }
592
593 #ifdef PACKET_LIST_STATISTICS
594         g_warning("Const strings: %u", packet_list->const_strings);
595         packet_list->const_strings = 0;
596 #endif
597 }
598
599 gint
600 packet_list_append_record(PacketList *packet_list, frame_data *fdata)
601 {
602         PacketListRecord *newrecord;
603
604         g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), -1);
605
606         newrecord = wmem_new(wmem_file_scope(), PacketListRecord);
607         newrecord->colorized    = FALSE;
608         newrecord->col_text_len = NULL;
609         newrecord->col_text     = NULL;
610         newrecord->fdata        = fdata;
611 #ifdef PACKET_PARANOID_CHECKS
612         newrecord->physical_pos = PACKET_LIST_RECORD_COUNT(packet_list->physical_rows);
613 #endif
614
615         if (fdata->flags.passed_dfilter || fdata->flags.ref_time) {
616                 newrecord->visible_pos = PACKET_LIST_RECORD_COUNT(packet_list->visible_rows);
617                 PACKET_LIST_RECORD_APPEND(packet_list->visible_rows, newrecord);
618         }
619         else
620                 newrecord->visible_pos = -1;
621
622         PACKET_LIST_RECORD_APPEND(packet_list->physical_rows, newrecord);
623
624         packet_list->columnized = FALSE;   /* XXX, dissect? */
625
626         /*
627          * Issue a row_inserted signal if the model is connected
628          * and the row is visible.
629          */
630         if (gtk_tree_view_get_model(GTK_TREE_VIEW(packet_list->view)) && newrecord->visible_pos != -1) {
631                 GtkTreeIter iter;
632                 GtkTreePath *path;
633
634                 path = gtk_tree_path_new();
635                 gtk_tree_path_append_index(path, newrecord->visible_pos);
636
637                 iter.stamp = packet_list->stamp;
638                 iter.user_data = newrecord;
639
640                 gtk_tree_model_row_inserted(GTK_TREE_MODEL(packet_list), path, &iter);
641                 gtk_tree_path_free(path);
642         }
643
644         /* XXXX If the model is connected and sort column != frame_num we should
645          * probably resort.
646          * Don't resort the list for every row, the list will be in packet order any way.
647          * packet_list_resort(packet_list);
648          */
649
650         return newrecord->visible_pos;
651 }
652
653 #define PACKET_STRING_CHUNK_SIZE (1 * 1024 * 1024)
654 static void
655 packet_list_change_record(PacketList *packet_list, PacketListRecord *record, gint col, column_info *cinfo)
656 {
657         gchar *str;
658         size_t col_text_len;
659         int text_col;
660         col_item_t* col_item;
661
662         text_col = packet_list->col_to_text[col];
663
664         /* Column based on frame_data or it already contains a value */
665         if (text_col == -1 || record->col_text[text_col] != NULL)
666                 return;
667
668         col_item = &cfile.cinfo.columns[col];
669         switch (col_item->col_fmt) {
670                 case COL_PROTOCOL:
671                 case COL_INFO:
672                 case COL_IF_DIR:
673                 case COL_DCE_CALL:
674                 case COL_8021Q_VLAN_ID:
675                 case COL_EXPERT:
676                 case COL_FREQ_CHAN:
677                         if (col_item->col_data && col_item->col_data != col_item->col_buf) {
678                                 col_text_len = strlen(col_item->col_data);
679                                 if (col_text_len > G_MAXUSHORT)
680                                         col_text_len = G_MAXUSHORT;
681
682                                 /* This is a constant string, so we don't have to copy it */
683                                 record->col_text[text_col] = (gchar *) col_item->col_data;
684                                 record->col_text_len[text_col] = (gushort) col_text_len;
685 #ifdef PACKET_LIST_STATISTICS
686                                 ++packet_list->const_strings;
687 #endif
688                                 break;
689                         }
690                 /* !! FALL-THROUGH!! */
691
692                 case COL_DEF_SRC:
693                 case COL_RES_SRC:       /* COL_DEF_SRC is currently just like COL_RES_SRC */
694                 case COL_UNRES_SRC:
695                 case COL_DEF_DL_SRC:
696                 case COL_RES_DL_SRC:
697                 case COL_UNRES_DL_SRC:
698                 case COL_DEF_NET_SRC:
699                 case COL_RES_NET_SRC:
700                 case COL_UNRES_NET_SRC:
701                 case COL_DEF_DST:
702                 case COL_RES_DST:       /* COL_DEF_DST is currently just like COL_RES_DST */
703                 case COL_UNRES_DST:
704                 case COL_DEF_DL_DST:
705                 case COL_RES_DL_DST:
706                 case COL_UNRES_DL_DST:
707                 case COL_DEF_NET_DST:
708                 case COL_RES_NET_DST:
709                 case COL_UNRES_NET_DST:
710                 default:
711                         if(col_item->col_data){
712                                 col_text_len = strlen(col_item->col_data);
713                                 if (col_text_len > G_MAXUSHORT)
714                                         col_text_len = G_MAXUSHORT;
715
716                                 record->col_text_len[text_col] = (gushort) col_text_len;
717                         }
718                         if (!record->col_text_len[text_col]) {
719                                 record->col_text[text_col] = "";
720 #ifdef PACKET_LIST_STATISTICS
721                                 ++packet_list->const_strings;
722 #endif
723                                 break;
724                         }
725
726                         if(!packet_list->string_pool)
727                                 packet_list->string_pool = g_string_chunk_new(PACKET_STRING_CHUNK_SIZE);
728                         if (!get_column_resolved (col) && cinfo->col_expr.col_expr_val[col]) {
729                                 /* Use the unresolved value in col_expr_val */
730                                 str = g_string_chunk_insert_const (packet_list->string_pool, (const gchar *)cinfo->col_expr.col_expr_val[col]);
731                         } else {
732                                 str = g_string_chunk_insert_const (packet_list->string_pool, (const gchar *)col_item->col_data);
733                         }
734                         record->col_text[text_col] = str;
735                         break;
736         }
737 }
738
739 static gboolean
740 packet_list_sortable_get_sort_column_id(GtkTreeSortable *sortable,
741                                         gint *sort_col_id,
742                                         GtkSortType *order)
743 {
744         PacketList *packet_list;
745
746         g_return_val_if_fail(sortable != NULL, FALSE);
747         g_return_val_if_fail(PACKETLIST_IS_LIST(sortable), FALSE);
748
749         packet_list = (PacketList *) sortable;
750
751         if(sort_col_id)
752                 *sort_col_id = packet_list->sort_id;
753
754         if(order)
755                 *order = packet_list->sort_order;
756
757         return TRUE;
758 }
759
760 static gboolean
761 packet_list_column_contains_values(PacketList *packet_list, gint sort_col_id)
762 {
763         if (packet_list->columnized || col_based_on_frame_data(&cfile.cinfo, sort_col_id))
764                 return TRUE;
765         else
766                 return FALSE;
767 }
768
769 /* packet_list_dissect_and_cache_all()
770  *  returns:
771  *   TRUE   if columnization completed;
772  *            packet_list->columnized set to TRUE;
773  *   FALSE: columnization did not complete (i.e., was stopped by the user);
774  *            packet_list->columnized unchanged (i.e., FALSE).
775  */
776
777 static gboolean
778 packet_list_dissect_and_cache_all(PacketList *packet_list)
779 {
780         PacketListRecord *record;
781
782         int             progbar_nextstep;
783         int             progbar_quantum;
784         gboolean        progbar_stop_flag;
785         GTimeVal        progbar_start_time;
786         float           progbar_val;
787         progdlg_t  *progbar = NULL;
788         gchar           progbar_status_str[100];
789         gint            progbar_loop_max;
790         gint            progbar_loop_var;
791         gint            progbar_updates = 100 /* 100% */;
792
793         g_assert(packet_list->columnized == FALSE);
794
795         progbar_loop_max = PACKET_LIST_RECORD_COUNT(packet_list->physical_rows);
796         /* Update the progress bar when it gets to this value. */
797         progbar_nextstep = 0;
798         /* When we reach the value that triggers a progress bar update,
799            bump that value by this amount. */
800         progbar_quantum = progbar_loop_max/progbar_updates;
801         /* Progress so far. */
802         progbar_val = 0.0f;
803
804         progbar_stop_flag = FALSE;
805         g_get_current_time(&progbar_start_time);
806
807         main_window_update();
808
809         for (progbar_loop_var = 0; progbar_loop_var < progbar_loop_max; ++progbar_loop_var) {
810                 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, progbar_loop_var);
811                 packet_list_dissect_and_cache_record(packet_list, record, FALSE);
812
813                 /* Create the progress bar if necessary.
814                    We check on every iteration of the loop, so that it takes no
815                    longer than the standard time to create it (otherwise, for a
816                    large file, we might take considerably longer than that standard
817                    time in order to get to the next progress bar step). */
818                 if (progbar == NULL)
819                         /* Note: The following may call gtk_main_iteration() which will */
820                         /*       allow certain "interupts" to happen during this code.  */
821                         /*       (Note that the progress_dlg window is set to "modal"   */
822                         /*        so that clicking on other windows is disabled).       */
823                         progbar = delayed_create_progress_dlg(gtk_widget_get_window(packet_list->view),
824                                                               "Construct", "Columns",
825                                                               TRUE, &progbar_stop_flag,
826                                                               &progbar_start_time, progbar_val);
827
828                 if (progbar_loop_var >= progbar_nextstep) {
829                         /* let's not divide by zero. We should never be started
830                          * with count == 0, so let's assert that */
831                         g_assert(progbar_loop_max > 0);
832
833                         progbar_val = (gfloat) progbar_loop_var / progbar_loop_max;
834
835                         if (progbar != NULL) {
836                                 g_snprintf(progbar_status_str, sizeof(progbar_status_str),
837                                            "%u of %u frames", progbar_loop_var+1, progbar_loop_max);
838                                 /* Note: See comment above re use of gtk_main_iteration() */
839                                 update_progress_dlg(progbar, progbar_val, progbar_status_str);
840                         }
841
842                         progbar_nextstep += progbar_quantum;
843                 }
844
845                 if (progbar_stop_flag) {
846                         /* Well, the user decided to abort ... */
847                         break;
848                 }
849         }
850
851         /* We're done; destroy the progress bar if it was created. */
852         if (progbar != NULL)
853                 destroy_progress_dlg(progbar);
854
855         if (progbar_stop_flag) {
856                 return FALSE; /* user aborted before columnization completed */
857         }
858
859         packet_list->columnized = TRUE;
860         return TRUE;
861 }
862
863 /* packet_list_do_packet_list_dissect_and_cache_all()
864  *  returns:
865  *    TRUE:  if columnization not needed or columnization completed;
866  *    FALSE: columnization did not complete (i.e., stopped by the user)
867  */
868 gboolean
869 packet_list_do_packet_list_dissect_and_cache_all(PacketList *packet_list, gint sort_col_id)
870 {
871         if (!packet_list_column_contains_values(packet_list, sort_col_id)) {
872                 return packet_list_dissect_and_cache_all(packet_list);
873         }
874         return TRUE;
875 }
876
877 static void
878 packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
879                                         gint sort_col_id,
880                                         GtkSortType order)
881 {
882         PacketList *packet_list;
883
884         g_return_if_fail(sortable != NULL);
885         g_return_if_fail(PACKETLIST_IS_LIST(sortable));
886
887         packet_list = (PacketList *) sortable;
888
889         if(packet_list->sort_id == sort_col_id &&
890            packet_list->sort_order == order)
891                 return;
892
893         packet_list->sort_id = sort_col_id;
894         packet_list->sort_order = order;
895
896         if(PACKET_LIST_RECORD_COUNT(packet_list->physical_rows) == 0)
897                 return;
898
899         packet_list_resort(packet_list);
900
901         /* emit "sort-column-changed" signal to tell any tree views
902          * that the sort column has changed (so the little arrow
903          * in the column header of the sort column is drawn
904          * in the right column) */
905
906         gtk_tree_sortable_sort_column_changed(sortable);
907 }
908
909 static void
910 packet_list_sortable_set_sort_func(GtkTreeSortable *sortable _U_,
911                                    gint sort_col_id _U_,
912                                    GtkTreeIterCompareFunc sort_func _U_,
913                                    gpointer user_data _U_,
914                                    GDestroyNotify destroy_func _U_)
915 {
916         g_warning(G_STRLOC ": is not supported by the PacketList model.\n");
917 }
918
919 static void
920 packet_list_sortable_set_default_sort_func(GtkTreeSortable *sortable _U_,
921                                            GtkTreeIterCompareFunc sort_func _U_,
922                                            gpointer user_data _U_,
923                                            GDestroyNotify destroy_func _U_)
924 {
925         g_warning(G_STRLOC ": is not supported by the PacketList model.\n");
926 }
927
928 static gboolean
929 packet_list_sortable_has_default_sort_func(GtkTreeSortable *sortable _U_)
930 {
931         return FALSE; /* Since packet_list_sortable_set_sort_func and
932                          set_default_sort_func are not implemented. */
933 }
934
935 static gint
936 packet_list_compare_custom(gint sort_id, gint text_sort_id, PacketListRecord *a, PacketListRecord *b)
937 {
938         header_field_info *hfi;
939
940         hfi = proto_registrar_get_byname(cfile.cinfo.columns[sort_id].col_custom_fields);
941
942         if (hfi == NULL) {
943                 return frame_data_compare(cfile.epan, a->fdata, b->fdata, COL_NUMBER);
944         } else if ((hfi->strings == NULL) &&
945                    (((IS_FT_INT(hfi->type) || IS_FT_UINT(hfi->type)) &&
946                      ((hfi->display == BASE_DEC) || (hfi->display == BASE_DEC_HEX) ||
947                       (hfi->display == BASE_OCT))) ||
948                     (hfi->type == FT_DOUBLE) || (hfi->type == FT_FLOAT) ||
949                     (hfi->type == FT_BOOLEAN) || (hfi->type == FT_FRAMENUM) ||
950                     (hfi->type == FT_RELATIVE_TIME)))
951           {
952                 /* Attempt to convert to numbers */
953                 double num_a = g_ascii_strtod(a->col_text[text_sort_id], NULL);
954                 double num_b = g_ascii_strtod(b->col_text[text_sort_id], NULL);
955
956                 if (num_a < num_b)
957                         return -1;
958                 else if (num_a > num_b)
959                         return 1;
960                 else
961                         return 0;
962           }
963
964         return strcmp(a->col_text[text_sort_id], b->col_text[text_sort_id]);
965 }
966
967 static gint
968 _packet_list_compare_records(gint sort_id, gint text_sort_id, PacketListRecord *a, PacketListRecord *b)
969 {
970         g_assert(a->col_text);
971         g_assert(b->col_text);
972         g_assert(a->col_text[text_sort_id]);
973         g_assert(b->col_text[text_sort_id]);
974
975         if(a->col_text[text_sort_id] == b->col_text[text_sort_id])
976                 return 0; /* no need to call strcmp() */
977
978         if (cfile.cinfo.columns[sort_id].col_fmt == COL_CUSTOM)
979                 return packet_list_compare_custom(sort_id, text_sort_id, a, b);
980
981         return strcmp(a->col_text[text_sort_id], b->col_text[text_sort_id]);
982 }
983
984 static gint
985 packet_list_compare_records(gint sort_id, gint text_sort_id, PacketListRecord *a, PacketListRecord *b)
986 {
987         gint ret;
988
989         if (text_sort_id == -1) /* based on frame_data ? */
990                 return frame_data_compare(cfile.epan, a->fdata, b->fdata, cfile.cinfo.columns[sort_id].col_fmt);
991
992         ret = _packet_list_compare_records(sort_id, text_sort_id, a, b);
993         if (ret == 0)
994                 ret = frame_data_compare(cfile.epan, a->fdata, b->fdata, COL_NUMBER);
995         return ret;
996 }
997
998 static gint
999 packet_list_qsort_physical_compare_func(PacketListRecord **a, PacketListRecord **b,
1000                                    PacketList *packet_list)
1001 {
1002         gint ret;
1003         gint sort_id = packet_list->sort_id;
1004
1005         g_assert((a) && (b) && (packet_list));
1006
1007         ret = packet_list_compare_records(sort_id, packet_list->col_to_text[sort_id], *a, *b);
1008
1009         /* Swap -1 and 1 if sort order is reverse */
1010         if(ret != 0 && packet_list->sort_order == GTK_SORT_DESCENDING)
1011                 ret = (ret < 0) ? 1 : -1;
1012
1013         return ret;
1014 }
1015
1016 static void
1017 packet_list_resort(PacketList *packet_list)
1018 {
1019         PacketListRecord *record;
1020         GtkTreePath *path;
1021         gint *neworder;
1022         guint phy_idx;
1023         guint vis_idx;
1024
1025         g_return_if_fail(packet_list != NULL);
1026         g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
1027
1028         if(PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
1029                 return;
1030
1031         /* resort physical rows according to sorting column */
1032         g_ptr_array_sort_with_data(packet_list->physical_rows,
1033                           (GCompareDataFunc) packet_list_qsort_physical_compare_func,
1034                           packet_list);
1035         g_return_if_fail(packet_list->visible_rows != NULL);
1036
1037         /* let other objects know about the new order */
1038         neworder = g_new0(gint, PACKET_LIST_RECORD_COUNT(packet_list->visible_rows));
1039         g_assert(neworder);
1040
1041         for(phy_idx = 0, vis_idx = 0; phy_idx < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++phy_idx) {
1042                 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, phy_idx);
1043 #ifdef PACKET_PARANOID_CHECKS
1044                 record->physical_pos = phy_idx;
1045 #endif
1046                 g_assert(record->visible_pos >= -1);
1047                 if (record->visible_pos >= 0) {
1048                         g_assert(record->fdata->flags.passed_dfilter || record->fdata->flags.ref_time);
1049                         neworder[vis_idx] = record->visible_pos;
1050                         PACKET_LIST_RECORD_SET(packet_list->visible_rows, vis_idx, record);
1051                         record->visible_pos = vis_idx;
1052                         ++vis_idx;
1053                 }
1054         }
1055
1056         g_assert(vis_idx == PACKET_LIST_RECORD_COUNT(packet_list->visible_rows));
1057
1058         path = gtk_tree_path_new();
1059
1060         gtk_tree_model_rows_reordered(GTK_TREE_MODEL(packet_list), path, NULL,
1061                                           neworder);
1062
1063         gtk_tree_path_free(path);
1064         g_free(neworder);
1065 }
1066
1067 guint
1068 packet_list_recreate_visible_rows_list(PacketList *packet_list)
1069 {
1070         guint phy_idx;
1071         guint vis_idx;
1072         PacketListRecord *record;
1073
1074         g_return_val_if_fail(packet_list != NULL, 0);
1075         g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), 0);
1076
1077         if(PACKET_LIST_RECORD_COUNT(packet_list->physical_rows) == 0)
1078                 return 0;
1079
1080         if(packet_list->visible_rows)
1081                 g_ptr_array_free(packet_list->visible_rows, TRUE);
1082
1083         packet_list->visible_rows = g_ptr_array_new();
1084
1085         for(phy_idx = 0, vis_idx = 0; phy_idx < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++phy_idx) {
1086                 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, phy_idx);
1087                 if (record->fdata->flags.passed_dfilter || record->fdata->flags.ref_time) {
1088                         record->visible_pos = vis_idx++;
1089                         PACKET_LIST_RECORD_APPEND(packet_list->visible_rows, record);
1090                 }
1091                 else
1092                         record->visible_pos = -1;
1093         }
1094
1095         return vis_idx;
1096 }
1097
1098 static void
1099 packet_list_dissect_and_cache_record(PacketList *packet_list, PacketListRecord *record, gboolean dissect_color)
1100 {
1101         epan_dissect_t edt;
1102         frame_data *fdata;
1103         column_info *cinfo;
1104         gint col;
1105         gboolean create_proto_tree;
1106         struct wtap_pkthdr phdr; /* Packet header */
1107         Buffer buf; /* Packet data */
1108         gboolean dissect_columns = (record->col_text == NULL);
1109
1110         g_return_if_fail(packet_list);
1111         g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
1112
1113         wtap_phdr_init(&phdr);
1114
1115         fdata = record->fdata;
1116
1117         if (dissect_columns) {
1118                 cinfo = &cfile.cinfo;
1119
1120                 record->col_text     = (const gchar **)wmem_alloc0(wmem_file_scope(), sizeof(*record->col_text) * packet_list->n_text_cols);
1121                 record->col_text_len = (gushort *)wmem_alloc0(wmem_file_scope(), sizeof(*record->col_text_len) * packet_list->n_text_cols);
1122         } else
1123                 cinfo = NULL;
1124
1125         ws_buffer_init(&buf, 1500);
1126         if (!cf_read_record_r(&cfile, fdata, &phdr, &buf)) {
1127                 /*
1128                  * Error reading the record.
1129                  *
1130                  * Don't set the color filter for now (we might want
1131                  * to colorize it in some fashion to warn that the
1132                  * row couldn't be filled in or colorized), and
1133                  * set the columns to placeholder values, except
1134                  * for the Info column, where we'll put in an
1135                  * error message.
1136                  */
1137                 if (dissect_columns) {
1138                         col_fill_in_error(cinfo, fdata, FALSE, FALSE /* fill_fd_columns */);
1139
1140                         for(col = 0; col < cinfo->num_cols; ++col)
1141                                 packet_list_change_record(packet_list, record, col, cinfo);
1142                 }
1143                 if (dissect_color) {
1144                         fdata->color_filter = NULL;
1145                         record->colorized = TRUE;
1146                 }
1147                 ws_buffer_free(&buf);
1148                 return; /* error reading the record */
1149         }
1150
1151         /*
1152          * Determine whether we need to create a protocol tree.
1153          * We do if:
1154          *
1155          *    we're going to apply a color filter to this packet;
1156          *
1157          *    we're need to fill in the columns and we have custom columns
1158          *    (which require field values, which currently requires that
1159          *    we build a protocol tree).
1160          */
1161         create_proto_tree = (dissect_color && color_filters_used()) ||
1162                                                 (dissect_columns && have_custom_cols(cinfo));
1163
1164         epan_dissect_init(&edt, cfile.epan,
1165                                           create_proto_tree,
1166                                           FALSE /* proto_tree_visible */);
1167
1168         if (dissect_color) {
1169                 color_filters_prime_edt(&edt);
1170                 fdata->flags.need_colorize = 1;
1171         }
1172         if (dissect_columns)
1173                 col_custom_prime_edt(&edt, cinfo);
1174
1175         /*
1176          * XXX - need to catch an OutOfMemoryError exception and
1177          * attempt to recover from it.
1178          */
1179         epan_dissect_run(&edt, cfile.cd_t, &phdr,
1180             frame_tvbuff_new_buffer(&cfile.provider, fdata, &buf),
1181             fdata, cinfo);
1182
1183         if (dissect_columns) {
1184                 /* "Stringify" non frame_data vals */
1185                 epan_dissect_fill_in_columns(&edt, FALSE, FALSE /* fill_fd_columns */);
1186
1187                 for(col = 0; col < cinfo->num_cols; ++col)
1188                         packet_list_change_record(packet_list, record, col, cinfo);
1189         }
1190
1191         if (dissect_color)
1192                 record->colorized = TRUE;
1193
1194         epan_dissect_cleanup(&edt);
1195         wtap_phdr_cleanup(&phdr);
1196         ws_buffer_free(&buf);
1197 }
1198
1199 void
1200 packet_list_reset_colorized(PacketList *packet_list)
1201 {
1202         PacketListRecord *record;
1203         guint i;
1204
1205         for(i = 0; i < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++i) {
1206                 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, i);
1207                 record->colorized = FALSE;
1208         }
1209 }
1210
1211 const char*
1212 packet_list_get_widest_column_string(PacketList *packet_list, gint col)
1213 {
1214         int text_col;
1215
1216         g_return_val_if_fail(packet_list != NULL, NULL);
1217         g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), NULL);
1218         /* We need real column here, so not packet_list->n_cols+1 */
1219         g_return_val_if_fail(col >= 0 && col < packet_list->n_cols, NULL);
1220
1221         if (PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
1222                 return "";
1223
1224         text_col = packet_list->col_to_text[col];
1225
1226         if (text_col == -1) {   /* column based on frame data */
1227                 PacketListRecord *record;
1228                 guint vis_idx;
1229
1230                 guint widest_packet = 0;
1231                 gint widest_column_len = -1;
1232
1233                 for(vis_idx = 0; vis_idx < PACKET_LIST_RECORD_COUNT(packet_list->visible_rows); ++vis_idx) {
1234                         gint column_len;
1235
1236                         record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, vis_idx);
1237
1238                         col_fill_in_frame_data(record->fdata, &cfile.cinfo, col, FALSE);
1239                         column_len = (gint) strlen(cfile.cinfo.columns[col].col_buf);
1240                         if (column_len > widest_column_len) {
1241                                 widest_column_len = column_len;
1242                                 widest_packet = vis_idx;
1243                         }
1244                 }
1245
1246                 if (widest_column_len != -1) {
1247                         record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, widest_packet);
1248                         col_fill_in_frame_data(record->fdata, &cfile.cinfo, col, FALSE);
1249
1250                         return cfile.cinfo.columns[col].col_buf;
1251                 } else
1252                         return "";
1253         }
1254         else {
1255                 PacketListRecord *record;
1256                 guint vis_idx;
1257
1258                 const gchar *widest_column_str = NULL;
1259                 guint widest_column_len = 0;
1260
1261                 if (!packet_list->columnized)
1262                         packet_list_dissect_and_cache_all(packet_list); /* XXX: need to handle case of "incomplete" ? */
1263
1264                 for(vis_idx = 0; vis_idx < PACKET_LIST_RECORD_COUNT(packet_list->visible_rows); ++vis_idx) {
1265                         record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, vis_idx);
1266                         if (record->col_text_len[text_col] > widest_column_len) {
1267                                 widest_column_str = record->col_text[text_col];
1268                                 widest_column_len = record->col_text_len[text_col];
1269                         }
1270                 }
1271
1272                 return widest_column_str;
1273         }
1274 }
1275
1276 /*
1277  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
1278  *
1279  * Local variables:
1280  * c-basic-offset: 8
1281  * tab-width: 8
1282  * indent-tabs-mode: t
1283  * End:
1284  *
1285  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1286  * :indentSize=8:tabSize=8:noTabs=false:
1287  */