update epan/dissectors/pidl/drsuapi/drsuapi.idl from samba
[wireshark-sm.git] / wsutil / filter_files.c
blob1c3c013fc6b4a4a8263069b8d20da6b1f044ffd5
1 /* filter_files.c
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
9 */
11 #include <config.h>
12 #define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL
13 #include "filter_files.h"
15 #include <stdio.h>
16 #include <string.h>
17 #include <errno.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
33 static GList *
34 add_filter_entry(GList *fl, const char *filt_name, const char *filt_expr)
36 filter_def *filt;
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);
44 static void
45 free_filter_entry(void * data)
47 filter_def *filt = (filter_def*)data;
48 g_free(filt->name);
49 g_free(filt->strval);
50 g_free(filt);
53 void ws_filter_list_free(filter_list_t *fl)
55 g_list_free_full(fl->list, free_filter_entry);
56 g_free(fl);
59 static GList *
60 remove_filter_entry(GList *fl, GList *fl_entry)
62 filter_def *filt;
64 filt = (filter_def *) fl_entry->data;
65 g_free(filt->name);
66 g_free(filt->strval);
67 g_free(filt);
68 return g_list_remove_link(fl, fl_entry);
71 static int
72 skip_whitespace(FILE *ff)
74 int c;
76 while ((c = getc(ff)) != EOF && c != '\n' && g_ascii_isspace(c))
78 return c;
81 static int
82 getc_crlf(FILE *ff)
84 int c;
86 c = getc(ff);
87 if (c == '\r') {
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. */
91 c = getc(ff);
92 if (c != EOF && c != '\n') {
93 /* Put back the character after the CR, and process the CR normally. */
94 ungetc(c, ff);
95 c = '\r';
98 return c;
101 filter_list_t *
102 ws_filter_list_read(filter_list_type_t list_type)
104 const char *ff_name, *ff_description;
105 char *ff_path;
106 FILE *ff;
107 GList *flp = NULL;
108 int c;
109 char *filt_name, *filt_expr;
110 int filt_name_len, filt_expr_len;
111 int filt_name_index, filt_expr_index;
112 int line = 1;
114 filter_list_t *list = g_new(filter_list_t, 1);
115 list->type = list_type;
116 list->list = NULL;
118 switch (list_type) {
120 case CFILTER_LIST:
121 ff_name = CFILTER_FILE_NAME;
122 ff_description = "capture";
123 break;
125 case DFILTER_LIST:
126 ff_name = DFILTER_FILE_NAME;
127 ff_description = "display";
128 break;
130 case DMACROS_LIST:
131 ff_name = DMACROS_FILE_NAME;
132 ff_description = "display filter macro";
133 break;
135 default:
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) {
147 * No. Just give up.
149 report_warning("Could not open your %s filter file\n\"%s\": %s.",
150 ff_description, ff_path, g_strerror(errno));
151 g_free(ff_path);
152 return list;
156 * Yes. Try to open the global "cfilters/dfilters" file.
158 g_free(ff_path);
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 filter file\n\"%s\": %s.",
167 ff_description, ff_path, g_strerror(errno));
169 g_free(ff_path);
170 return list;
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
183 "name" expression
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);
193 if (c == EOF)
194 break; /* Nothing more to read */
195 if (c == '\n')
196 continue; /* Blank line. */
197 if (c == '#') {
198 /* Comment. */
199 while (c != '\n')
200 c = getc(ff); /* skip to the end of the line */
201 continue;
204 /* "c" is the first non-white-space character.
205 If it's not a quote, it's an error. */
206 if (c != '"') {
207 ws_warning("'%s' line %d doesn't have a quoted filter name.", ff_path,
208 line);
209 while (c != '\n')
210 c = getc(ff); /* skip to the end of the line */
211 continue;
214 /* Get the name of the filter. */
215 filt_name_index = 0;
216 for (;;) {
217 c = getc_crlf(ff);
218 if (c == EOF || c == '\n')
219 break; /* End of line - or end of file */
220 if (c == '"') {
221 /* Closing quote. */
222 if (filt_name_index >= filt_name_len) {
223 /* Filter name buffer isn't long enough; double its length. */
224 filt_name_len *= 2;
225 filt_name = (char *)g_realloc(filt_name, filt_name_len + 1);
227 filt_name[filt_name_index] = '\0';
228 break;
230 if (c == '\\') {
231 /* Next character is escaped */
232 c = getc_crlf(ff);
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. */
239 filt_name_len *= 2;
240 filt_name = (char *)g_realloc(filt_name, filt_name_len + 1);
242 filt_name[filt_name_index] = c;
243 filt_name_index++;
246 if (c == EOF) {
247 if (!ferror(ff)) {
248 /* EOF, not error; no newline seen before EOF */
249 ws_warning("'%s' line %d doesn't have a newline.", ff_path,
250 line);
252 break; /* nothing more to read */
255 if (c != '"') {
256 /* No newline seen before end-of-line */
257 ws_warning("'%s' line %d doesn't have a closing quote.", ff_path,
258 line);
259 continue;
262 /* Skip over separating white space, if any. */
263 c = skip_whitespace(ff);
265 if (c == EOF) {
266 if (!ferror(ff)) {
267 /* EOF, not error; no newline seen before EOF */
268 ws_warning("'%s' line %d doesn't have a newline.", ff_path,
269 line);
271 break; /* nothing more to read */
274 if (c == '\n') {
275 /* No filter expression */
276 ws_warning("'%s' line %d doesn't have a filter expression.", ff_path,
277 line);
278 continue;
281 /* "c" is the first non-white-space character; it's the first
282 character of the filter expression. */
283 filt_expr_index = 0;
284 for (;;) {
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. */
288 filt_expr_len *= 2;
289 filt_expr = (char *)g_realloc(filt_expr, filt_expr_len + 1);
291 filt_expr[filt_expr_index] = c;
292 filt_expr_index++;
294 /* Get the next character. */
295 c = getc_crlf(ff);
296 if (c == EOF || c == '\n')
297 break;
300 if (c == EOF) {
301 if (!ferror(ff)) {
302 /* EOF, not error; no newline seen before EOF */
303 ws_warning("'%s' line %d doesn't have a newline.", ff_path,
304 line);
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. */
312 filt_expr_len *= 2;
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);
320 if (ferror(ff)) {
321 report_warning("Error reading your %s filter file\n\"%s\": %s.",
322 ff_description, ff_path, g_strerror(errno));
324 g_free(ff_path);
325 fclose(ff);
326 g_free(filt_name);
327 g_free(filt_expr);
328 list->list = flp;
329 return list;
333 * Add a new filter to the end of a list.
335 void
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);
342 static int
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.
356 bool
357 ws_filter_list_remove(filter_list_t *list, const char *name)
359 GList *p;
361 p = g_list_find_custom(list->list, name, compare_def);
362 if (p == NULL)
363 return false;
364 list->list = remove_filter_entry(list->list, p);
365 return true;
369 * Write out a list of filters.
371 * On error, report the error via the UI.
373 void
374 ws_filter_list_write(filter_list_t *list)
376 char *pf_dir_path;
377 const char *ff_name, *ff_description;
378 char *ff_path, *ff_path_new;
379 GList *fl;
380 GList *flpp;
381 filter_def *filt;
382 FILE *ff;
383 unsigned char *p, c;
385 switch (list->type) {
387 case CFILTER_LIST:
388 ff_name = CFILTER_FILE_NAME;
389 ff_description = "capture";
390 break;
392 case DFILTER_LIST:
393 ff_name = DFILTER_FILE_NAME;
394 ff_description = "display";
395 break;
397 case DMACROS_LIST:
398 ff_name = DMACROS_FILE_NAME;
399 ff_description = "display filter macros";
400 break;
402 default:
403 ws_assert_not_reached();
404 return;
406 fl = list->list;
408 /* Create the directory that holds personal configuration files,
409 if necessary. */
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));
413 g_free(pf_dir_path);
414 return;
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
421 completely. */
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 filter file\nCouldn't open \"%s\": %s.",
427 ff_description, ff_path_new, g_strerror(errno));
428 g_free(ff_path_new);
429 g_free(ff_path);
430 return;
432 flpp = g_list_first(fl);
433 while (flpp) {
434 filt = (filter_def *) flpp->data;
436 /* Write out the filter name as a quoted string; escape any quotes
437 or backslashes. */
438 putc('"', ff);
439 for (p = (unsigned char *)filt->name; (c = *p) != '\0'; p++) {
440 if (c == '"' || c == '\\')
441 putc('\\', ff);
442 putc(c, ff);
444 putc('"', ff);
446 /* Separate the filter name and value with a space. */
447 putc(' ', ff);
449 /* Write out the filter expression and a newline. */
450 fprintf(ff, "%s\n", filt->strval);
451 if (ferror(ff)) {
452 report_failure("Error saving your %s filter file\nWrite to \"%s\" failed: %s.",
453 ff_description, ff_path_new, g_strerror(errno));
454 fclose(ff);
455 ws_unlink(ff_path_new);
456 g_free(ff_path_new);
457 g_free(ff_path);
458 return;
460 flpp = flpp->next;
462 if (fclose(ff) == EOF) {
463 report_failure("Error saving your %s filter file\nWrite to \"%s\" failed: %s.",
464 ff_description, ff_path_new, g_strerror(errno));
465 ws_unlink(ff_path_new);
466 g_free(ff_path_new);
467 g_free(ff_path);
468 return;
471 #ifdef _WIN32
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
486 drive on. */
487 report_failure("Error saving your %s filter file\nCouldn't remove \"%s\": %s.",
488 ff_description, ff_path, g_strerror(errno));
489 ws_unlink(ff_path_new);
490 g_free(ff_path_new);
491 g_free(ff_path);
492 return;
494 #endif
496 if (ws_rename(ff_path_new, ff_path) < 0) {
497 report_failure("Error saving your %s filter 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);
500 g_free(ff_path_new);
501 g_free(ff_path);
502 return;
504 g_free(ff_path_new);
505 g_free(ff_path);