Qt: Fix leak on CaptureFileDialog preview of file with errors
[wireshark.git] / dftest.c
blobe4dfa2c8da7c62ded94b9ba42f06877a03a1fd41
1 /* dftest.c
2 * Shows display filter byte-code, for debugging dfilter routines.
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_MAIN
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <locale.h>
17 #include <string.h>
18 #include <errno.h>
20 #include <glib.h>
22 #include <ws_exit_codes.h>
24 #include <epan/epan.h>
25 #include <epan/timestamp.h>
26 #include <epan/prefs.h>
27 #include <epan/dfilter/dfilter.h>
28 #include <epan/dfilter/dfilter-macro.h>
30 #ifdef HAVE_PLUGINS
31 #include <wsutil/plugins.h>
32 #endif
33 #include <wsutil/filesystem.h>
34 #include <wsutil/privileges.h>
35 #include <wsutil/report_message.h>
36 #include <wsutil/wslog.h>
37 #include <wsutil/ws_getopt.h>
38 #include <wsutil/utf8_entities.h>
40 #include <wiretap/wtap.h>
42 #include "ui/util.h"
43 #include "wsutil/cmdarg_err.h"
44 #include "ui/failure_message.h"
45 #include "wsutil/version_info.h"
47 static int opt_verbose;
48 static int opt_debug_level; /* currently up to 2 */
49 static int opt_flex;
50 static int opt_lemon;
51 static int opt_syntax_tree;
52 static int opt_return_vals;
53 static int opt_timer;
54 static long opt_optimize = 1;
55 static int opt_show_types;
56 static int opt_dump_refs;
57 static int opt_dump_macros;
59 static gint64 elapsed_expand;
60 static gint64 elapsed_compile;
63 * Report an error in command-line arguments.
65 static void
66 dftest_cmdarg_err(const char *fmt, va_list ap)
68 fprintf(stderr, "dftest: ");
69 vfprintf(stderr, fmt, ap);
70 fprintf(stderr, "\n");
74 * Report additional information for an error in command-line arguments.
76 static void
77 dftest_cmdarg_err_cont(const char *fmt, va_list ap)
79 vfprintf(stderr, fmt, ap);
80 fprintf(stderr, "\n");
83 static void
84 putloc(FILE *fp, df_loc_t loc)
86 for (long i = 0; i < loc.col_start; i++) {
87 fputc(' ', fp);
89 fputc('^', fp);
91 for (size_t l = loc.col_len; l > 1; l--) {
92 fputc('~', fp);
94 fputc('\n', fp);
97 WS_NORETURN static void
98 print_usage(int status)
100 FILE *fp = stdout;
101 fprintf(fp, "\n");
102 fprintf(fp, "Usage: dftest [OPTIONS] -- EXPRESSION\n");
103 fprintf(fp, "Options:\n");
104 fprintf(fp, " -V, --verbose enable verbose mode\n");
105 fprintf(fp, " -d, --debug[=N] increase or set debug level\n");
106 fprintf(fp, " -D set maximum debug level\n");
107 fprintf(fp, " -f, --flex enable Flex debug trace\n");
108 fprintf(fp, " -l, --lemon enable Lemon debug trace\n");
109 fprintf(fp, " -s, --syntax print syntax tree\n");
110 fprintf(fp, " -m --macros print saved macros\n");
111 fprintf(fp, " -t, --timer print elapsed compilation time\n");
112 fprintf(fp, " -r --return-vals return field values for the tree root\n");
113 fprintf(fp, " -0, --optimize=0 do not optimize (check syntax)\n");
114 fprintf(fp, " --types show field value types\n");
115 /* NOTE: References are loaded during runtime and dftest only does compilation.
116 * Unless some static reference data is hard-coded at compile time during
117 * development the --refs option to dftest is useless because it will just
118 * print empty reference vectors. */
119 fprintf(fp, " --refs dump some runtime data structures\n");
120 fprintf(fp, " -h, --help display this help and exit\n");
121 fprintf(fp, " -v, --version print version\n");
122 fprintf(fp, "\n");
123 ws_log_print_usage(fp);
124 exit(status);
127 static void
128 print_syntax_tree(dfilter_t *df)
130 printf("Syntax tree:\n%s\n\n", dfilter_syntax_tree(df));
133 static void
134 print_macros(void)
136 if (dfilter_macro_table_count() == 0) {
137 printf("Macros: (empty)\n\n");
138 return;
141 struct dfilter_macro_table_iter iter;
142 const char *name, *text;
144 dfilter_macro_table_iter_init(&iter);
145 printf("Macros:\n");
146 while (dfilter_macro_table_iter_next(&iter, &name, &text)) {
147 printf(" "UTF8_BULLET" %s:\n", name);
148 printf(" %s\n", text);
150 printf("\n");
153 static void
154 print_warnings(dfilter_t *df)
156 guint i;
157 GPtrArray *deprecated;
158 int count = 0;
160 for (GSList *l = dfilter_get_warnings(df); l != NULL; l = l->next) {
161 printf("\nWarning: %s.", (char *)l->data);
162 count++;
165 deprecated = dfilter_deprecated_tokens(df);
166 if (deprecated && deprecated->len) {
167 for (i = 0; i < deprecated->len; i++) {
168 const char *token = g_ptr_array_index(deprecated, i);
169 printf("\nWarning: Deprecated token \"%s\".", token);
170 count++;
174 if (count) {
175 printf("\n");
179 static void
180 print_elapsed(void)
182 printf("\nElapsed: %"PRId64" µs (%"PRId64" µs + %"PRId64" µs)\n",
183 elapsed_expand + elapsed_compile,
184 elapsed_expand,
185 elapsed_compile);
188 static char *
189 expand_filter(const char *text)
191 char *expanded = NULL;
192 df_error_t *err = NULL;
193 gint64 start;
195 start = g_get_monotonic_time();
196 expanded = dfilter_expand(text, &err);
197 if (expanded == NULL) {
198 fprintf(stderr, "Error: %s\n", err->msg);
199 df_error_free(&err);
201 elapsed_expand = g_get_monotonic_time() - start;
202 return expanded;
205 static gboolean
206 compile_filter(const char *text, dfilter_t **dfp)
208 unsigned df_flags = 0;
209 gboolean ok;
210 df_error_t *df_err = NULL;
211 gint64 start;
213 if (opt_optimize > 0)
214 df_flags |= DF_OPTIMIZE;
215 if (opt_syntax_tree)
216 df_flags |= DF_SAVE_TREE;
217 if (opt_flex)
218 df_flags |= DF_DEBUG_FLEX;
219 if (opt_lemon)
220 df_flags |= DF_DEBUG_LEMON;
221 if (opt_return_vals)
222 df_flags |= DF_RETURN_VALUES;
224 start = g_get_monotonic_time();
225 ok = dfilter_compile_full(text, dfp, &df_err, df_flags, "dftest");
226 if (!ok) {
227 fprintf(stderr, "Error: %s\n", df_err->msg);
228 if (df_err->loc.col_start >= 0) {
229 fprintf(stderr, " %s\n ", text);
230 putloc(stderr, df_err->loc);
232 df_error_free(&df_err);
234 elapsed_compile = g_get_monotonic_time() - start;
235 return ok;
238 static int
239 optarg_to_digit(const char *arg)
241 if (strlen(arg) > 1 || !g_ascii_isdigit(*arg)) {
242 printf("Error: \"%s\" is not a valid number 0-9\n", arg);
243 print_usage(WS_EXIT_INVALID_OPTION);
245 errno = 0;
246 int digit = (int)strtol(ws_optarg, NULL, 10);
247 if (errno) {
248 printf("Error: %s\n", g_strerror(errno));
249 print_usage(WS_EXIT_INVALID_OPTION);
251 return digit;
255 main(int argc, char **argv)
257 char *configuration_init_error;
258 char *text = NULL;
259 char *expanded_text = NULL;
260 dfilter_t *df = NULL;
261 int exit_status = EXIT_FAILURE;
264 * Set the C-language locale to the native environment and set the
265 * code page to UTF-8 on Windows.
267 #ifdef _WIN32
268 setlocale(LC_ALL, ".UTF-8");
269 #else
270 setlocale(LC_ALL, "");
271 #endif
273 cmdarg_err_init(dftest_cmdarg_err, dftest_cmdarg_err_cont);
275 /* Initialize log handler early for startup. */
276 ws_log_init("dftest", vcmdarg_err);
278 /* Early logging command-line initialization. */
279 ws_log_parse_args(&argc, argv, vcmdarg_err, 1);
281 ws_noisy("Finished log init and parsing command line log arguments");
283 ws_init_version_info("DFTest", NULL, NULL);
285 const char *optstring = "hvdDflsmrtV0";
286 static struct ws_option long_options[] = {
287 { "help", ws_no_argument, 0, 'h' },
288 { "version", ws_no_argument, 0, 'v' },
289 { "debug", ws_optional_argument, 0, 'd' },
290 { "flex", ws_no_argument, 0, 'f' },
291 { "lemon", ws_no_argument, 0, 'l' },
292 { "syntax", ws_no_argument, 0, 's' },
293 { "macros", ws_no_argument, 0, 'm' },
294 { "timer", ws_no_argument, 0, 't' },
295 { "verbose", ws_no_argument, 0, 'V' },
296 { "return-vals", ws_no_argument, 0, 'r' },
297 { "optimize", ws_required_argument, 0, 1000 },
298 { "types", ws_no_argument, 0, 2000 },
299 { "refs", ws_no_argument, 0, 3000 },
300 { NULL, 0, 0, 0 }
302 int opt;
304 for (;;) {
305 opt = ws_getopt_long(argc, argv, optstring, long_options, NULL);
306 if (opt == -1)
307 break;
309 switch (opt) {
310 case 'V':
311 opt_verbose = 1;
312 break;
313 case 'd':
314 if (ws_optarg) {
315 opt_debug_level = optarg_to_digit(ws_optarg);
317 else {
318 opt_debug_level++;
320 opt_show_types = 1;
321 break;
322 case 'D':
323 opt_debug_level = 9;
324 opt_lemon = 1;
325 opt_flex = 1;
326 opt_show_types = 1;
327 break;
328 case 'f':
329 opt_flex = 1;
330 break;
331 case 'l':
332 opt_lemon = 1;
333 break;
334 case 's':
335 opt_syntax_tree = 1;
336 break;
337 case 'm':
338 opt_dump_macros = 1;
339 break;
340 case 't':
341 opt_timer = 1;
342 break;
343 case 'r':
344 opt_return_vals = 1;
345 break;
346 case '0':
347 opt_optimize = 0;
348 break;
349 case 1000:
350 opt_optimize = optarg_to_digit(ws_optarg);
351 break;
352 case 2000:
353 opt_show_types = 1;
354 break;
355 case 3000:
356 opt_dump_refs = 1;
357 break;
358 case 'v':
359 show_version();
360 exit(EXIT_SUCCESS);
361 break;
362 case 'h':
363 show_help_header(NULL);
364 print_usage(EXIT_SUCCESS);
365 break;
366 case '?':
367 print_usage(EXIT_FAILURE);
368 default:
369 ws_assert_not_reached();
373 /* Check for filter on command line. */
374 if (argv[ws_optind] == NULL) {
375 /* If not printing macros we need a filter expression to compile. */
376 if (!opt_dump_macros) {
377 printf("Error: Missing argument.\n");
378 print_usage(EXIT_FAILURE);
382 /* Set dfilter domain logging. */
383 if (opt_debug_level > 1) {
384 ws_log_set_noisy_filter(LOG_DOMAIN_DFILTER);
386 else if (opt_debug_level > 0 || opt_flex || opt_lemon) {
387 /* Also enable some dfilter logs with flex/lemon traces for context. */
388 ws_log_set_debug_filter(LOG_DOMAIN_DFILTER);
392 * Get credential information for later use.
394 init_process_policies();
397 * Attempt to get the pathname of the directory containing the
398 * executable file.
400 configuration_init_error = configuration_init(argv[0], NULL);
401 if (configuration_init_error != NULL) {
402 fprintf(stderr, "Error: Can't get pathname of directory containing "
403 "the dftest program: %s.\n",
404 configuration_init_error);
405 g_free(configuration_init_error);
408 static const struct report_message_routines dftest_report_routines = {
409 failure_message,
410 failure_message,
411 open_failure_message,
412 read_failure_message,
413 write_failure_message,
414 cfile_open_failure_message,
415 cfile_dump_open_failure_message,
416 cfile_read_failure_message,
417 cfile_write_failure_message,
418 cfile_close_failure_message
421 init_report_message("dftest", &dftest_report_routines);
423 timestamp_set_type(TS_RELATIVE);
424 timestamp_set_seconds_type(TS_SECONDS_DEFAULT);
427 * Libwiretap must be initialized before libwireshark is, so that
428 * dissection-time handlers for file-type-dependent blocks can
429 * register using the file type/subtype value for the file type.
431 wtap_init(TRUE);
433 /* Register all dissectors; we must do this before checking for the
434 "-g" flag, as the "-g" flag dumps a list of fields registered
435 by the dissectors, and we must do it before we read the preferences,
436 in case any dissectors register preferences. */
437 if (!epan_init(NULL, NULL, TRUE))
438 goto out;
440 /* Load libwireshark settings from the current profile. */
441 epan_load_settings();
443 /* notify all registered modules that have had any of their preferences
444 changed either from one of the preferences file or from the command
445 line that its preferences have changed. */
446 prefs_apply_all();
448 if (opt_dump_macros) {
449 print_macros();
450 if (argv[ws_optind] == NULL) {
451 /* No filter expression, we're done. */
452 exit(EXIT_SUCCESS);
456 /* Check again for filter on command line */
457 if (argv[ws_optind] == NULL) {
458 printf("Error: Missing argument.\n");
459 print_usage(EXIT_FAILURE);
462 /* This is useful to prevent confusion with option parsing.
463 * Skips printing options and argv[0]. */
464 if (opt_verbose) {
465 for (int i = ws_optind; i < argc; i++) {
466 fprintf(stderr, "argv[%d]: %s\n", i, argv[i]);
468 fprintf(stderr, "\n");
471 /* Get filter text */
472 text = get_args_as_string(argc, argv, ws_optind);
474 printf("Filter:\n %s\n\n", text);
476 /* Expand macros. */
477 expanded_text = expand_filter(text);
478 if (expanded_text == NULL) {
479 exit_status = WS_EXIT_INVALID_FILTER;
480 goto out;
483 if (strcmp(text, expanded_text) != 0)
484 printf("Filter (after expansion):\n %s\n\n", expanded_text);
486 /* Compile it */
487 if (!compile_filter(expanded_text, &df)) {
488 exit_status = WS_EXIT_INVALID_FILTER;
489 goto out;
492 /* If logging is enabled add an empty line. */
493 if (opt_debug_level > 0) {
494 printf("\n");
497 if (df == NULL) {
498 printf("Filter is empty.\n");
499 exit_status = WS_EXIT_INVALID_FILTER;
500 goto out;
503 if (opt_syntax_tree)
504 print_syntax_tree(df);
506 uint16_t dump_flags = 0;
507 if (opt_show_types)
508 dump_flags |= DF_DUMP_SHOW_FTYPE;
509 if (opt_dump_refs)
510 dump_flags |= DF_DUMP_REFERENCES;
512 dfilter_dump(stdout, df, dump_flags);
514 print_warnings(df);
516 if (opt_timer)
517 print_elapsed();
519 exit_status = 0;
521 out:
522 epan_cleanup();
523 dfilter_free(df);
524 g_free(text);
525 g_free(expanded_text);
526 exit(exit_status);