Rename "create_color()" to "initialize_color()", as that reflects a bit
[metze/wireshark/wip.git] / color_filters.c
1 /* color_filters.c
2  * Routines for color filters
3  *
4  * $Id$
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24 /*
25  * Updated 1 Dec 10 jjm
26  */
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <glib.h>
32 #include <ctype.h>
33 #include <string.h>
34
35 #include <epan/filesystem.h>
36
37 #include <epan/packet.h>
38 #include "color.h"
39 #include "color_filters.h"
40 #include "file.h"
41 #include <epan/dfilter/dfilter.h>
42 #include "simple_dialog.h"
43
44 static gboolean read_filters(void);
45 static gboolean read_global_filters(void);
46
47 /* Variables and routines defined in color.h */
48
49 GSList *filter_list = NULL;
50 GSList *removed_filter_list = NULL;
51
52 /* Remove the specified filter from the list of existing color filters,
53  * and add it to the list of removed color filters.
54  * This way, unmarking and marking a packet which matches a now removed
55  * color filter will still be colored correctly as the color filter is
56  * still reachable. */
57 void remove_color_filter(color_filter_t *colorf)
58 {
59         /* Remove colorf from the list of color filters */
60         filter_list = g_slist_remove(filter_list, colorf);
61         /* Add colorf to the list of removed color filters */
62         removed_filter_list = g_slist_prepend(removed_filter_list, colorf);
63 }
64
65 /* delete the specified filter */
66 static void
67 delete_color_filter(color_filter_t *colorf)
68 {
69         if (colorf->filter_name != NULL)
70                 g_free(colorf->filter_name);
71         if (colorf->filter_text != NULL)
72                 g_free(colorf->filter_text);
73         if (colorf->c_colorfilter != NULL)
74                 dfilter_free(colorf->c_colorfilter);
75         g_free(colorf);
76 }
77
78 /* delete the specified filter as an iterator*/
79 static void
80 delete_color_filter_it(gpointer filter_arg, gpointer unused _U_)
81 {
82         color_filter_t *colorf = filter_arg;
83         delete_color_filter(colorf);
84 }
85
86 /* delete all the filters */
87 static void
88 delete_all_color_filters (void)
89 {
90         g_slist_foreach(filter_list, delete_color_filter_it, NULL);
91         g_slist_free(filter_list);
92         filter_list = NULL;
93         g_slist_foreach(removed_filter_list, delete_color_filter_it, NULL);
94         g_slist_free(removed_filter_list);
95         removed_filter_list = NULL;
96 }
97
98 /* Initialize the filter structures (reading from file) for general running, including app startup */
99 void
100 colfilter_init(void)
101 {
102         delete_all_color_filters();
103         if (!read_filters())
104                 read_global_filters();
105 }
106
107 /* Create a new filter */
108 color_filter_t *
109 new_color_filter(gchar *name,          /* The name of the filter to create */
110                  gchar *filter_string, /* The string representing the filter */
111                  color_t *bg_color,    /* The background color */
112                  color_t *fg_color)    /* The foreground color */
113 {
114         color_filter_t *colorf;
115
116         colorf = g_malloc(sizeof (color_filter_t));
117         colorf->filter_name = g_strdup(name);
118         colorf->filter_text = g_strdup(filter_string);
119         colorf->bg_color = *bg_color;
120         colorf->fg_color = *fg_color;
121         colorf->c_colorfilter = NULL;
122         colorf->edit_dialog = NULL;
123         colorf->marked = FALSE;
124         filter_list = g_slist_append(filter_list, colorf);
125         return colorf;
126 }
127
128 static void
129 prime_edt(gpointer data, gpointer user_data)
130 {
131         color_filter_t  *colorf = data;
132         epan_dissect_t   *edt = user_data;
133
134         if (colorf->c_colorfilter != NULL)
135                 epan_dissect_prime_dfilter(edt, colorf->c_colorfilter);
136 }
137
138 /* Prime the epan_dissect_t with all the compiler
139  * color filters in 'filter_list'. */
140 void
141 filter_list_prime_edt(epan_dissect_t *edt)
142 {
143         g_slist_foreach(filter_list, prime_edt, edt);
144 }
145
146 /* read filters from the given file */
147
148 /* XXX - Would it make more sense to use GStrings here instead of reallocing
149    our buffers? */
150 static gboolean
151 read_filters_file(FILE *f, gpointer arg)
152 {
153 #define INIT_BUF_SIZE 128
154         gchar  *name = NULL;
155         gchar  *filter_exp = NULL;
156         guint32 name_len = INIT_BUF_SIZE;
157         guint32 filter_exp_len = INIT_BUF_SIZE;
158         guint32 i = 0;
159         gint32  c;
160         guint16 fg_r, fg_g, fg_b, bg_r, bg_g, bg_b;
161         gboolean skip_end_of_line = FALSE;
162
163         name = g_malloc(name_len + 1);
164         filter_exp = g_malloc(filter_exp_len + 1);
165
166         while (1) {
167
168                 if (skip_end_of_line) {
169                         do {
170                                 c = getc(f);
171                         } while (c != EOF && c != '\n');
172                         if (c == EOF)
173                                 break;
174                         skip_end_of_line = FALSE;
175                 }
176
177                 while ((c = getc(f)) != EOF && isspace(c)) {
178                         if (c == '\n') {
179                                 continue;
180                         }
181                 }
182
183                 if (c == EOF)
184                         break;
185
186                 /* skip # comments and invalid lines */
187                 if (c != '@') { 
188                         skip_end_of_line = TRUE;
189                         continue;
190                 }
191
192                 /* we get the @ delimiter.
193                  * Format is:
194                  * @name@filter expression@[background r,g,b][foreground r,g,b]
195                  */
196
197                 /* retrieve name */
198                 i = 0;
199                 while (1) {
200                         c = getc(f);
201                         if (c == EOF || c == '@')
202                                 break;
203                         if (i >= name_len) {
204                                 /* buffer isn't long enough; double its length.*/
205                                 name_len *= 2;
206                                 name = g_realloc(name, name_len + 1);
207                         }
208                         name[i++] = c;            
209                 }
210                 name[i] = '\0';
211
212                 if (c == EOF) {
213                         break;
214                 } else if (i == 0) {
215                         skip_end_of_line = TRUE;
216                         continue;
217                 }
218
219                 /* retrieve filter expression */
220                 i = 0;
221                 while (1) {
222                         c = getc(f);
223                         if (c == EOF || c == '@')
224                                 break;
225                         if (i >= filter_exp_len) {
226                                 /* buffer isn't long enough; double its length.*/
227                                 filter_exp_len *= 2;
228                                 filter_exp = g_realloc(filter_exp, filter_exp_len + 1);
229                         }
230                         filter_exp[i++] = c;
231                 }
232                 filter_exp[i] = '\0';
233
234                 if (c == EOF) {
235                         break;
236                 } else if (i == 0) {
237                         skip_end_of_line = TRUE;
238                         continue;
239                 }
240
241                 /* retrieve background and foreground colors */
242                 if (fscanf(f,"[%hu,%hu,%hu][%hu,%hu,%hu]",
243                         &bg_r, &bg_g, &bg_b, &fg_r, &fg_g, &fg_b) == 6) {
244
245                         /* we got a complete color filter */
246
247                         color_t bg_color, fg_color;
248                         color_filter_t *colorf;
249                         dfilter_t *temp_dfilter;
250
251                         if (!dfilter_compile(filter_exp, &temp_dfilter)) {
252                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
253                                 "Could not compile color filter %s from saved filters.\n%s",
254                                               name, dfilter_error_msg);
255                                 skip_end_of_line = TRUE;
256                                 continue;
257                         }
258
259                         if (!initialize_color(&fg_color, fg_r, fg_g, fg_b)) {
260                                 /* oops */
261                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
262                                     "Could not allocate foreground color "
263                                     "specified in input file for %s.", name);
264                                 dfilter_free(temp_dfilter);
265                                 skip_end_of_line = TRUE;
266                                 continue;
267                         }
268                         if (!initialize_color(&bg_color, bg_r, bg_g, bg_b)) {
269                                 /* oops */
270                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
271                                     "Could not allocate background color "
272                                     "specified in input file for %s.", name);
273                                 dfilter_free(temp_dfilter);
274                                 skip_end_of_line = TRUE;
275                                 continue;
276                         }
277
278                         colorf = new_color_filter(name, filter_exp, &bg_color,
279                             &fg_color);
280                         colorf->c_colorfilter = temp_dfilter;
281
282                         if (arg != NULL)
283                                 color_add_filter_cb (colorf, arg);
284                 }    /* if sscanf */
285
286                 skip_end_of_line = TRUE;
287         }
288
289         g_free(name);
290         g_free(filter_exp);
291         return TRUE;
292 }
293
294 /* read filters from the user's filter file */
295 static gboolean
296 read_filters(void)
297 {
298         gchar *path;
299         FILE *f;
300         gboolean ret;
301
302         /* decide what file to open (from dfilter code) */
303         path = get_persconffile_path("colorfilters", FALSE);
304         if ((f = fopen(path, "r")) == NULL) {
305                 if (errno != ENOENT) {
306                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
307                             "Could not open filter file\n\"%s\": %s.", path,
308                             strerror(errno));
309                 }
310                 g_free(path);
311                 return FALSE;
312         }
313         g_free(path);
314         path = NULL;
315
316         ret = read_filters_file(f, NULL);
317         fclose(f);
318         return ret;
319 }
320
321 /* read filters from the filter file */
322 static gboolean
323 read_global_filters(void)
324 {
325         gchar *path;
326         FILE *f;
327         gboolean ret;
328
329         /* decide what file to open (from dfilter code) */
330         path = get_datafile_path("colorfilters");
331         if ((f = fopen(path, "r")) == NULL) {
332                 if (errno != ENOENT) {
333                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
334                             "Could not open global filter file\n\"%s\": %s.", path,
335                             strerror(errno));
336                 }
337                 g_free(path);
338                 return FALSE;
339         }
340         g_free(path);
341         path = NULL;
342
343         ret = read_filters_file(f, NULL);
344         fclose(f);
345         return ret;
346 }
347
348 /* save filters in some other filter file */
349
350 gboolean
351 read_other_filters(gchar *path, gpointer arg)
352 {
353         FILE *f;
354         gboolean ret;
355
356         if ((f = fopen(path, "r")) == NULL) {
357                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
358                     "Could not open\n%s\nfor reading: %s.",
359                     path, strerror(errno));
360                 return FALSE;
361         }
362
363         ret = read_filters_file(f, arg);
364         fclose(f);
365         return ret;
366 }
367
368 struct write_filter_data
369 {
370   FILE * f;
371   gboolean only_marked;
372 };
373
374 static void
375 write_filter(gpointer filter_arg, gpointer data_arg)
376 {
377         struct write_filter_data *data = data_arg;
378         color_filter_t *colorf = filter_arg;
379         FILE *f = data->f;
380
381         if (colorf->marked || !data->only_marked) {
382                 fprintf(f,"@%s@%s@[%d,%d,%d][%d,%d,%d]\n",
383                     colorf->filter_name,
384                     colorf->filter_text,
385                     colorf->bg_color.red,
386                     colorf->bg_color.green,
387                     colorf->bg_color.blue,
388                     colorf->fg_color.red,
389                     colorf->fg_color.green,
390                     colorf->fg_color.blue);
391         }
392 }
393
394 /* save filters in a filter file */
395 gboolean
396 write_filters_file(FILE *f, gboolean only_marked)
397 {
398         struct write_filter_data data;
399
400         data.f = f;
401         data.only_marked = only_marked;
402   
403         fprintf(f,"# DO NOT EDIT THIS FILE!  It was created by Ethereal\n");
404         g_slist_foreach(filter_list, write_filter, &data);
405         return TRUE;
406 }
407
408 /* save filters in users filter file */
409
410 gboolean
411 write_filters(void)
412 {
413         gchar *pf_dir_path;
414         const gchar *path;
415         FILE *f;
416
417         /* Create the directory that holds personal configuration files,
418            if necessary.  */
419         if (create_persconffile_dir(&pf_dir_path) == -1) {
420                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
421                     "Can't create directory\n\"%s\"\nfor color files: %s.",
422                     pf_dir_path, strerror(errno));
423                 g_free(pf_dir_path);
424                 return FALSE;
425         }
426
427         path = get_persconffile_path("colorfilters", TRUE);
428         if ((f = fopen(path, "w+")) == NULL) {
429                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
430                     "Could not open\n%s\nfor writing: %s.",
431                     path, strerror(errno));
432                 return FALSE;
433         }
434         write_filters_file(f, FALSE);
435         fclose(f);
436         return TRUE;
437 }
438
439 /* delete users filter file and reload global filters*/
440
441 gboolean
442 revert_filters(void)
443 {
444         gchar *pf_dir_path;
445         const gchar *path;
446
447         /* Create the directory that holds personal configuration files,
448            if necessary.  */
449         if (create_persconffile_dir(&pf_dir_path) == -1) {
450                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
451                     "Can't create directory\n\"%s\"\nfor color files: %s.",
452                     pf_dir_path, strerror(errno));
453                 g_free(pf_dir_path);
454                 return FALSE;
455         }
456
457         path = get_persconffile_path("colorfilters", TRUE);
458         if (!deletefile(path)) {
459                 g_free((gchar *)path);
460                 return FALSE;
461         }
462
463         g_free((gchar *)path);
464
465         /* Reload the (global) filters - Note: this does not update the dialog. */
466         colfilter_init();
467         return TRUE;
468 }
469
470
471 /* save filters in some other filter file */
472
473 gboolean
474 write_other_filters(gchar *path, gboolean only_marked)
475 {
476         FILE *f;
477
478         if ((f = fopen(path, "w+")) == NULL) {
479                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
480                     "Could not open\n%s\nfor writing: %s.",
481                     path, strerror(errno));
482                 return FALSE;
483         }
484         write_filters_file(f, only_marked);
485         fclose(f);
486         return TRUE;
487 }