2 * Code for reading and writing the filters file.
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
12 #define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL
13 #include "filter_files.h"
19 #include <wsutil/file_util.h>
20 #include <wsutil/filesystem.h>
21 #include <wsutil/report_message.h>
22 #include <wsutil/wslog.h>
23 #include <wsutil/ws_assert.h>
26 * Read in a list of filters.
28 * On error, report the error via the UI.
31 #define INIT_BUF_SIZE 128
34 add_filter_entry(GList
*fl
, const char *filt_name
, const char *filt_expr
)
38 filt
= g_new(filter_def
, 1);
39 filt
->name
= g_strdup(filt_name
);
40 filt
->strval
= g_strdup(filt_expr
);
41 return g_list_prepend(fl
, filt
);
45 free_filter_entry(void * data
)
47 filter_def
*filt
= (filter_def
*)data
;
53 void ws_filter_list_free(filter_list_t
*fl
)
55 g_list_free_full(fl
->list
, free_filter_entry
);
60 remove_filter_entry(GList
*fl
, GList
*fl_entry
)
64 filt
= (filter_def
*) fl_entry
->data
;
68 return g_list_remove_link(fl
, fl_entry
);
72 skip_whitespace(FILE *ff
)
76 while ((c
= getc(ff
)) != EOF
&& c
!= '\n' && g_ascii_isspace(c
))
88 /* Treat CR-LF at the end of a line like LF, so that if we're reading
89 * a Windows-format file on UN*X, we handle it the same way we'd handle
90 * a UN*X-format file. */
92 if (c
!= EOF
&& c
!= '\n') {
93 /* Put back the character after the CR, and process the CR normally. */
102 ws_filter_list_read(filter_list_type_t list_type
)
104 const char *ff_name
, *ff_description
;
109 char *filt_name
, *filt_expr
;
110 int filt_name_len
, filt_expr_len
;
111 int filt_name_index
, filt_expr_index
;
114 filter_list_t
*list
= g_new(filter_list_t
, 1);
115 list
->type
= list_type
;
121 ff_name
= CFILTER_FILE_NAME
;
122 ff_description
= "capture filter";
126 ff_name
= DFILTER_FILE_NAME
;
127 ff_description
= "display filter";
131 ff_name
= DMACROS_FILE_NAME
;
132 ff_description
= "display filter macro";
136 ws_assert_not_reached();
139 /* try to open personal "cfilters"/"dfilters" file */
140 ff_path
= get_persconffile_path(ff_name
, true);
141 if ((ff
= ws_fopen(ff_path
, "r")) == NULL
) {
143 * Did that fail because the file didn't exist?
145 if (errno
!= ENOENT
) {
149 report_warning("Could not open your %s file\n\"%s\": %s.",
150 ff_description
, ff_path
, g_strerror(errno
));
156 * Yes. Try to open the global "cfilters/dfilters" file.
159 ff_path
= get_datafile_path(ff_name
);
160 if ((ff
= ws_fopen(ff_path
, "r")) == NULL
) {
162 * Well, that didn't work, either. Just give up.
163 * Report an error if the file existed but we couldn't open it.
165 if (errno
!= ENOENT
) {
166 report_warning("Could not open your %s file\n\"%s\": %s.",
167 ff_description
, ff_path
, g_strerror(errno
));
174 /* Allocate the filter name buffer. */
175 filt_name_len
= INIT_BUF_SIZE
;
176 filt_name
= (char *)g_malloc(filt_name_len
+ 1);
177 filt_expr_len
= INIT_BUF_SIZE
;
178 filt_expr
= (char *)g_malloc(filt_expr_len
+ 1);
180 for (line
= 1; ; line
++) {
181 /* Lines in a filter file are of the form
185 where "name" is a name, in quotes - backslashes in the name
186 escape the next character, so quotes and backslashes can appear
187 in the name - and "expression" is a filter expression, not in
188 quotes, running to the end of the line. */
190 /* Skip over leading white space, if any. */
191 c
= skip_whitespace(ff
);
194 break; /* Nothing more to read */
196 continue; /* Blank line. */
200 c
= getc(ff
); /* skip to the end of the line */
204 /* "c" is the first non-white-space character.
205 If it's not a quote, it's an error. */
207 ws_warning("'%s' line %d doesn't have a quoted filter name.", ff_path
,
210 c
= getc(ff
); /* skip to the end of the line */
214 /* Get the name of the filter. */
218 if (c
== EOF
|| c
== '\n')
219 break; /* End of line - or end of file */
222 if (filt_name_index
>= filt_name_len
) {
223 /* Filter name buffer isn't long enough; double its length. */
225 filt_name
= (char *)g_realloc(filt_name
, filt_name_len
+ 1);
227 filt_name
[filt_name_index
] = '\0';
231 /* Next character is escaped */
233 if (c
== EOF
|| c
== '\n')
234 break; /* End of line - or end of file */
236 /* Add this character to the filter name string. */
237 if (filt_name_index
>= filt_name_len
) {
238 /* Filter name buffer isn't long enough; double its length. */
240 filt_name
= (char *)g_realloc(filt_name
, filt_name_len
+ 1);
242 filt_name
[filt_name_index
] = c
;
248 /* EOF, not error; no newline seen before EOF */
249 ws_warning("'%s' line %d doesn't have a newline.", ff_path
,
252 break; /* nothing more to read */
256 /* No newline seen before end-of-line */
257 ws_warning("'%s' line %d doesn't have a closing quote.", ff_path
,
262 /* Skip over separating white space, if any. */
263 c
= skip_whitespace(ff
);
267 /* EOF, not error; no newline seen before EOF */
268 ws_warning("'%s' line %d doesn't have a newline.", ff_path
,
271 break; /* nothing more to read */
275 /* No filter expression */
276 ws_warning("'%s' line %d doesn't have a filter expression.", ff_path
,
281 /* "c" is the first non-white-space character; it's the first
282 character of the filter expression. */
285 /* Add this character to the filter expression string. */
286 if (filt_expr_index
>= filt_expr_len
) {
287 /* Filter expression buffer isn't long enough; double its length. */
289 filt_expr
= (char *)g_realloc(filt_expr
, filt_expr_len
+ 1);
291 filt_expr
[filt_expr_index
] = c
;
294 /* Get the next character. */
296 if (c
== EOF
|| c
== '\n')
302 /* EOF, not error; no newline seen before EOF */
303 ws_warning("'%s' line %d doesn't have a newline.", ff_path
,
306 break; /* nothing more to read */
309 /* We saw the ending newline; terminate the filter expression string */
310 if (filt_expr_index
>= filt_expr_len
) {
311 /* Filter expression buffer isn't long enough; double its length. */
313 filt_expr
= (char *)g_realloc(filt_expr
, filt_expr_len
+ 1);
315 filt_expr
[filt_expr_index
] = '\0';
317 /* Add the new filter to the list of filters */
318 flp
= add_filter_entry(flp
, filt_name
, filt_expr
);
321 report_warning("Error reading your %s file\n\"%s\": %s.",
322 ff_description
, ff_path
, g_strerror(errno
));
333 * Add a new filter to the end of a list.
336 ws_filter_list_add(filter_list_t
*fl
, const char *name
,
337 const char *expression
)
339 fl
->list
= add_filter_entry(fl
->list
, name
, expression
);
343 compare_def(const void *def
, const void *name
)
345 return g_strcmp0(((filter_def
*)def
)->name
, name
);
348 GList
*ws_filter_list_find(filter_list_t
*list
, const char *name
)
350 return g_list_find_custom(list
->list
, name
, compare_def
);
354 * Remove a filter from a list.
357 ws_filter_list_remove(filter_list_t
*list
, const char *name
)
361 p
= g_list_find_custom(list
->list
, name
, compare_def
);
364 list
->list
= remove_filter_entry(list
->list
, p
);
369 * Write out a list of filters.
371 * On error, report the error via the UI.
374 ws_filter_list_write(filter_list_t
*list
)
377 const char *ff_name
, *ff_description
;
378 char *ff_path
, *ff_path_new
;
385 switch (list
->type
) {
388 ff_name
= CFILTER_FILE_NAME
;
389 ff_description
= "capture filter";
393 ff_name
= DFILTER_FILE_NAME
;
394 ff_description
= "display filter";
398 ff_name
= DMACROS_FILE_NAME
;
399 ff_description
= "display filter macro";
403 ws_assert_not_reached();
408 /* Create the directory that holds personal configuration files,
410 if (create_persconffile_dir(&pf_dir_path
) == -1) {
411 report_failure("Can't create directory\n\"%s\"\nfor filter files: %s.",
412 pf_dir_path
, g_strerror(errno
));
417 ff_path
= get_persconffile_path(ff_name
, true);
419 /* Write to "XXX.new", and rename if that succeeds.
420 That means we don't trash the file if we fail to write it out
422 ff_path_new
= ws_strdup_printf("%s.new", ff_path
);
424 if ((ff
= ws_fopen(ff_path_new
, "w")) == NULL
) {
425 /* We had an error saving the filter. */
426 report_failure("Error saving your %s file\nCouldn't open \"%s\": %s.",
427 ff_description
, ff_path_new
, g_strerror(errno
));
432 flpp
= g_list_first(fl
);
434 filt
= (filter_def
*) flpp
->data
;
436 /* Write out the filter name as a quoted string; escape any quotes
439 for (p
= (unsigned char *)filt
->name
; (c
= *p
) != '\0'; p
++) {
440 if (c
== '"' || c
== '\\')
446 /* Separate the filter name and value with a space. */
449 /* Write out the filter expression and a newline. */
450 fprintf(ff
, "%s\n", filt
->strval
);
452 report_failure("Error saving your %s file\nWrite to \"%s\" failed: %s.",
453 ff_description
, ff_path_new
, g_strerror(errno
));
455 ws_unlink(ff_path_new
);
462 if (fclose(ff
) == EOF
) {
463 report_failure("Error saving your %s file\nWrite to \"%s\" failed: %s.",
464 ff_description
, ff_path_new
, g_strerror(errno
));
465 ws_unlink(ff_path_new
);
472 /* ANSI C doesn't say whether "rename()" removes the target if it
473 exists; the Win32 call to rename files doesn't do so, which I
474 infer is the reason why the MSVC++ "rename()" doesn't do so.
475 We must therefore remove the target file first, on Windows.
477 XXX - ws_rename() should be ws_stdio_rename() on Windows,
478 and ws_stdio_rename() uses MoveFileEx() with MOVEFILE_REPLACE_EXISTING,
479 so it should remove the target if it exists, so this stuff
480 shouldn't be necessary. Perhaps it dates back to when we were
481 calling rename(), with that being a wrapper around Microsoft's
482 _rename(), which didn't remove the target. */
483 if (ws_remove(ff_path
) < 0 && errno
!= ENOENT
) {
484 /* It failed for some reason other than "it's not there"; if
485 it's not there, we don't need to remove it, so we just
487 report_failure("Error saving your %s file\nCouldn't remove \"%s\": %s.",
488 ff_description
, ff_path
, g_strerror(errno
));
489 ws_unlink(ff_path_new
);
496 if (ws_rename(ff_path_new
, ff_path
) < 0) {
497 report_failure("Error saving your %s file\nCouldn't rename \"%s\" to \"%s\": %s.",
498 ff_description
, ff_path_new
, ff_path
, g_strerror(errno
));
499 ws_unlink(ff_path_new
);