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