Change the filter dialog (capture and display filters), so it has a real Cancel butto...
[obnox/wireshark/wip.git] / filters.c
1 /* filters.c
2  * Code for reading and writing the filters file.
3  *
4  * $Id$
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <stdio.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <errno.h>
33
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37
38 #include <glib.h>
39
40 #include <epan/filesystem.h>
41
42 #include "filters.h"
43 #include "file_util.h"
44
45 /*
46  * Old filter file name.
47  */
48 #define FILTER_FILE_NAME        "filters"
49
50 /*
51  * Capture filter file name.
52  */
53 #define CFILTER_FILE_NAME       "cfilters"
54
55 /*
56  * Display filter file name.
57  */
58 #define DFILTER_FILE_NAME       "dfilters"
59
60 /*
61  * List of capture filters - saved.
62  */
63 static GList *capture_filters = NULL;
64
65 /*
66  * List of display filters - saved.
67  */
68 static GList *display_filters = NULL;
69
70 /*
71  * List of capture filters - currently edited.
72  */
73 static GList *capture_edited_filters = NULL;
74
75 /*
76  * List of display filters - currently edited.
77  */
78 static GList *display_edited_filters = NULL;
79
80 /*
81  * Read in a list of filters.
82  *
83  * On success, "*pref_path_return" is set to NULL.
84  * On error, "*pref_path_return" is set to point to the pathname of
85  * the file we tried to read - it should be freed by our caller -
86  * and "*errno_return" is set to the error.
87  */
88
89 #define INIT_BUF_SIZE   128
90
91 GList *
92 add_filter_entry(GList *fl, const char *filt_name, const char *filt_expr)
93 {
94     filter_def *filt;
95
96     filt         = (filter_def *) g_malloc(sizeof(filter_def));
97     filt->name   = g_strdup(filt_name);
98     filt->strval = g_strdup(filt_expr);
99     return g_list_append(fl, filt);
100 }
101
102 GList *
103 remove_filter_entry(GList *fl, GList *fl_entry)
104 {
105   filter_def *filt;
106
107   filt = (filter_def *) fl_entry->data;
108   g_free(filt->name);
109   g_free(filt->strval);
110   g_free(filt);
111   return g_list_remove_link(fl, fl_entry);
112 }
113
114 void
115 read_filter_list(filter_list_type_t list_type, char **pref_path_return,
116     int *errno_return)
117 {
118   const char *ff_name;
119   char       *ff_path;
120   FILE       *ff;
121   GList      **flpp;
122   int         c;
123   char       *filt_name, *filt_expr;
124   int         filt_name_len, filt_expr_len;
125   int         filt_name_index, filt_expr_index;
126   int         line = 1;
127
128   *pref_path_return = NULL;     /* assume no error */
129
130   switch (list_type) {
131
132   case CFILTER_LIST:
133     ff_name = CFILTER_FILE_NAME;
134     flpp = &capture_filters;
135     break;
136
137   case DFILTER_LIST:
138     ff_name = DFILTER_FILE_NAME;
139     flpp = &display_filters;
140     break;
141
142   default:
143     g_assert_not_reached();
144     return;
145   }
146
147   /* try to open personal "cfilters"/"dfilters" file */
148   ff_path = get_persconffile_path(ff_name, FALSE);
149   if ((ff = eth_fopen(ff_path, "r")) == NULL) {
150     /*
151      * Did that fail because the file didn't exist?
152      */
153     if (errno != ENOENT) {
154       /*
155        * No.  Just give up.
156        */
157       *pref_path_return = ff_path;
158       *errno_return = errno;
159       return;
160     }
161
162     /*
163      * Yes.  See if there's an "old style" personal "filters" file; if so, read it.
164      * This means that a user will start out with their capture and
165      * display filter lists being identical; each list may contain
166      * filters that don't belong in that list.  The user can edit
167      * the filter lists, and delete the ones that don't belong in
168      * a particular list.
169      */
170     g_free(ff_path);
171     ff_path = get_persconffile_path(FILTER_FILE_NAME, FALSE);
172     if ((ff = eth_fopen(ff_path, "r")) == NULL) {
173     /*
174      * Did that fail because the file didn't exist?
175      */
176       if (errno != ENOENT) {
177       /*
178        * No.  Just give up.
179        */
180         *pref_path_return = ff_path;
181         *errno_return = errno;
182     return;
183       }
184
185     /*
186      * Try to open the global "cfilters/dfilters" file */
187     ff_path = get_datafile_path(ff_name);
188     if ((ff = eth_fopen(ff_path, "r")) == NULL) {
189
190       /*
191        * Well, that didn't work, either.  Just give up.
192        * Return an error if the file existed but we couldn't open it.
193        */
194       if (errno != ENOENT) {
195         *pref_path_return = ff_path;
196         *errno_return = errno;
197     }
198     return;
199     }
200     }
201   }
202
203   /* If we already have a list of filters, discard it. */
204   /* this should never happen - this function is called only once for each list! */
205   while(*flpp) {
206     *flpp = remove_filter_entry(*flpp, g_list_first(*flpp));        
207   }
208
209   /* Allocate the filter name buffer. */
210   filt_name_len = INIT_BUF_SIZE;
211   filt_name = g_malloc(filt_name_len + 1);
212   filt_expr_len = INIT_BUF_SIZE;
213   filt_expr = g_malloc(filt_expr_len + 1);
214
215   for (line = 1; ; line++) {
216     /* Lines in a filter file are of the form
217
218         "name" expression
219
220        where "name" is a name, in quotes - backslashes in the name
221        escape the next character, so quotes and backslashes can appear
222        in the name - and "expression" is a filter expression, not in
223        quotes, running to the end of the line. */
224
225     /* Skip over leading white space, if any. */
226     while ((c = getc(ff)) != EOF && isspace(c)) {
227       if (c == '\n') {
228         /* Blank line. */
229         continue;
230       }
231     }
232
233     if (c == EOF)
234       break;    /* Nothing more to read */
235
236     /* "c" is the first non-white-space character.
237        If it's not a quote, it's an error. */
238     if (c != '"') {
239       g_warning("'%s' line %d doesn't have a quoted filter name.", ff_path,
240                 line);
241       while (c != '\n')
242         c = getc(ff);   /* skip to the end of the line */
243       continue;
244     }
245
246     /* Get the name of the filter. */
247     filt_name_index = 0;
248     for (;;) {
249       c = getc(ff);
250       if (c == EOF || c == '\n')
251         break;  /* End of line - or end of file */
252       if (c == '"') {
253         /* Closing quote. */
254         if (filt_name_index >= filt_name_len) {
255           /* Filter name buffer isn't long enough; double its length. */
256           filt_name_len *= 2;
257           filt_name = g_realloc(filt_name, filt_name_len + 1);
258         }
259         filt_name[filt_name_index] = '\0';
260         break;
261       }
262       if (c == '\\') {
263         /* Next character is escaped */
264         c = getc(ff);
265         if (c == EOF || c == '\n')
266           break;        /* End of line - or end of file */
267       }
268       /* Add this character to the filter name string. */
269       if (filt_name_index >= filt_name_len) {
270         /* Filter name buffer isn't long enough; double its length. */
271         filt_name_len *= 2;
272         filt_name = g_realloc(filt_name, filt_name_len + 1);
273       }
274       filt_name[filt_name_index] = c;
275       filt_name_index++;
276     }
277
278     if (c == EOF) {
279       if (!ferror(ff)) {
280         /* EOF, not error; no newline seen before EOF */
281         g_warning("'%s' line %d doesn't have a newline.", ff_path,
282                   line);
283       }
284       break;    /* nothing more to read */
285     }
286
287     if (c != '"') {
288       /* No newline seen before end-of-line */
289       g_warning("'%s' line %d doesn't have a closing quote.", ff_path,
290                 line);
291       continue;
292     }
293
294     /* Skip over separating white space, if any. */
295     while ((c = getc(ff)) != EOF && isspace(c)) {
296       if (c == '\n')
297         break;
298     }
299
300     if (c == EOF) {
301       if (!ferror(ff)) {
302         /* EOF, not error; no newline seen before EOF */
303         g_warning("'%s' line %d doesn't have a newline.", ff_path,
304                   line);
305       }
306       break;    /* nothing more to read */
307     }
308
309     if (c == '\n') {
310       /* No filter expression */
311       g_warning("'%s' line %d doesn't have a filter expression.", ff_path,
312                 line);
313       continue;
314     }
315
316     /* "c" is the first non-white-space character; it's the first
317        character of the filter expression. */
318     filt_expr_index = 0;
319     for (;;) {
320       /* Add this character to the filter expression string. */
321       if (filt_expr_index >= filt_expr_len) {
322         /* Filter expressioin buffer isn't long enough; double its length. */
323         filt_expr_len *= 2;
324         filt_expr = g_realloc(filt_expr, filt_expr_len + 1);
325       }
326       filt_expr[filt_expr_index] = c;
327       filt_expr_index++;
328
329       /* Get the next character. */
330       c = getc(ff);
331       if (c == EOF || c == '\n')
332         break;
333     }
334
335     if (c == EOF) {
336       if (!ferror(ff)) {
337         /* EOF, not error; no newline seen before EOF */
338         g_warning("'%s' line %d doesn't have a newline.", ff_path,
339                   line);
340       }
341       break;    /* nothing more to read */
342     }
343
344     /* We saw the ending newline; terminate the filter expression string */
345     if (filt_expr_index >= filt_expr_len) {
346       /* Filter expressioin buffer isn't long enough; double its length. */
347       filt_expr_len *= 2;
348       filt_expr = g_realloc(filt_expr, filt_expr_len + 1);
349     }
350     filt_expr[filt_expr_index] = '\0';
351
352     /* Add the new filter to the list of filters */
353     *flpp = add_filter_entry(*flpp, filt_name, filt_expr);
354   }
355   if (ferror(ff)) {
356     *pref_path_return = ff_path;
357     *errno_return = errno;
358   } else
359     g_free(ff_path);
360   fclose(ff);
361   g_free(filt_name);
362   g_free(filt_expr);
363   
364   /* init the corresponding edited list */
365   switch (list_type) {
366   case CFILTER_LIST:
367     copy_filter_list(CFILTER_EDITED_LIST, CFILTER_LIST);
368     break;
369   case DFILTER_LIST:
370     copy_filter_list(DFILTER_EDITED_LIST, DFILTER_LIST);
371     break;
372   default:
373     g_assert_not_reached();
374     return;
375   }
376 }
377
378 /*
379  * Get a pointer to a list of filters.
380  */
381 static GList **
382 get_filter_list(filter_list_type_t list_type)
383 {
384   GList **flpp;
385
386   switch (list_type) {
387
388   case CFILTER_LIST:
389     flpp = &capture_filters;
390     break;
391
392   case DFILTER_LIST:
393     flpp = &display_filters;
394     break;
395
396   case CFILTER_EDITED_LIST:
397     flpp = &capture_edited_filters;
398     break;
399
400   case DFILTER_EDITED_LIST:
401     flpp = &display_edited_filters;
402     break;
403
404   default:
405     g_assert_not_reached();
406     flpp = NULL;
407   }
408   return flpp;
409 }
410
411 /*
412  * Get a pointer to the first entry in a filter list.
413  */
414 GList *
415 get_filter_list_first(filter_list_type_t list_type)
416 {
417   GList      **flpp;
418
419   flpp = get_filter_list(list_type);
420   return g_list_first(*flpp);
421 }
422
423 /*
424  * Add a new filter to the end of a list.
425  * Returns a pointer to the newly-added entry.
426  */
427 GList *
428 add_to_filter_list(filter_list_type_t list_type, const char *name,
429     const char *expression)
430 {
431   GList      **flpp;
432
433   flpp = get_filter_list(list_type);
434   *flpp = add_filter_entry(*flpp, name, expression);
435
436   return g_list_last(*flpp);
437 }
438
439 /*
440  * Remove a filter from a list.
441  */
442 void
443 remove_from_filter_list(filter_list_type_t list_type, GList *fl_entry)
444 {
445   GList      **flpp;
446
447   flpp = get_filter_list(list_type);
448   *flpp = remove_filter_entry(*flpp, fl_entry);
449 }
450
451 /*
452  * Write out a list of filters.
453  *
454  * On success, "*pref_path_return" is set to NULL.
455  * On error, "*pref_path_return" is set to point to the pathname of
456  * the file we tried to read - it should be freed by our caller -
457  * and "*errno_return" is set to the error.
458  */
459 void
460 save_filter_list(filter_list_type_t list_type, char **pref_path_return,
461     int *errno_return)
462 {
463   const gchar *ff_name;
464   gchar      *ff_path, *ff_path_new;
465   GList      *fl;
466   GList      *flpp;
467   filter_def *filt;
468   FILE       *ff;
469   guchar     *p, c;
470
471   *pref_path_return = NULL;     /* assume no error */
472
473   switch (list_type) {
474
475   case CFILTER_LIST:
476     ff_name = CFILTER_FILE_NAME;
477     fl = capture_filters;
478     break;
479
480   case DFILTER_LIST:
481     ff_name = DFILTER_FILE_NAME;
482     fl = display_filters;
483     break;
484
485   default:
486     g_assert_not_reached();
487     return;
488   }
489
490   ff_path = get_persconffile_path(ff_name, TRUE);
491
492   /* Write to "XXX.new", and rename if that succeeds.
493      That means we don't trash the file if we fail to write it out
494      completely. */
495   ff_path_new = g_strdup_printf("%s.new", ff_path);
496
497   if ((ff = eth_fopen(ff_path_new, "w")) == NULL) {
498     *pref_path_return = ff_path;
499     *errno_return = errno;
500     g_free(ff_path_new);
501     return;
502   }
503   flpp = g_list_first(fl);
504   while (flpp) {
505     filt = (filter_def *) flpp->data;
506
507     /* Write out the filter name as a quoted string; escape any quotes
508        or backslashes. */
509     putc('"', ff);
510     for (p = (guchar *)filt->name; (c = *p) != '\0'; p++) {
511       if (c == '"' || c == '\\')
512         putc('\\', ff);
513       putc(c, ff);
514     }
515     putc('"', ff);
516
517     /* Separate the filter name and value with a space. */
518     putc(' ', ff);
519
520     /* Write out the filter expression and a newline. */
521     fprintf(ff, "%s\n", filt->strval);
522     if (ferror(ff)) {
523       *pref_path_return = ff_path;
524       *errno_return = errno;
525       fclose(ff);
526       eth_unlink(ff_path_new);
527       g_free(ff_path_new);
528       return;
529     }
530     flpp = flpp->next;
531   }
532   if (fclose(ff) == EOF) {
533     *pref_path_return = ff_path;
534     *errno_return = errno;
535     eth_unlink(ff_path_new);
536     g_free(ff_path_new);
537     return;
538   }
539
540 #ifdef _WIN32
541   /* ANSI C doesn't say whether "rename()" removes the target if it
542      exists; the Win32 call to rename files doesn't do so, which I
543      infer is the reason why the MSVC++ "rename()" doesn't do so.
544      We must therefore remove the target file first, on Windows. */
545   if (eth_remove(ff_path) < 0 && errno != ENOENT) {
546     /* It failed for some reason other than "it's not there"; if
547        it's not there, we don't need to remove it, so we just
548        drive on. */
549     *pref_path_return = ff_path;
550     *errno_return = errno;
551     eth_unlink(ff_path_new);
552     g_free(ff_path_new);
553     return;
554   }
555 #endif
556
557   if (eth_rename(ff_path_new, ff_path) < 0) {
558     *pref_path_return = ff_path;
559     *errno_return = errno;
560     eth_unlink(ff_path_new);
561     g_free(ff_path_new);
562     return;
563   }
564   g_free(ff_path_new);
565   g_free(ff_path);
566 }
567
568 /*
569  * Copy a filter list into another.
570  */
571 void copy_filter_list(filter_list_type_t dest_type, filter_list_type_t src_type)
572 {
573     GList      **flpp_dest;
574     GList      **flpp_src;
575     GList      *flp_src;
576     filter_def *filt;
577
578     g_assert(dest_type != src_type);
579
580     flpp_dest = get_filter_list(dest_type);
581     flpp_src = get_filter_list(src_type);
582     flp_src = *flpp_src;
583
584     /* throw away the "old" destination list - a NULL list is ok here */
585     while(*flpp_dest) {
586         *flpp_dest = remove_filter_entry(*flpp_dest, g_list_first(*flpp_dest));        
587     }
588     g_assert(g_list_length(*flpp_dest) == 0);
589
590     /* copy the list entries */
591     while(flp_src) {
592         filt = (flp_src)->data;
593
594         *flpp_dest = add_filter_entry(*flpp_dest, filt->name, filt->strval);
595         flp_src = g_list_next(flp_src);
596     }
597 }
598