diff options
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | gtk/main.c | 78 | ||||
-rw-r--r-- | gtk/prefs_dlg.c | 297 | ||||
-rw-r--r-- | packet-ip.c | 11 | ||||
-rw-r--r-- | prefs-int.h | 79 | ||||
-rw-r--r-- | prefs.c | 585 | ||||
-rw-r--r-- | prefs.h | 93 | ||||
-rw-r--r-- | tethereal.c | 57 |
8 files changed, 1112 insertions, 91 deletions
diff --git a/Makefile.am b/Makefile.am index db337e1c29..50e175079a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,7 @@ # Makefile.am # Automake file for Ethereal # -# $Id: Makefile.am,v 1.206 2000/06/22 06:37:59 guy Exp $ +# $Id: Makefile.am,v 1.207 2000/07/05 09:40:37 guy Exp $ # # Ethereal - Network traffic analyzer # By Gerald Combs <gerald@zing.org> @@ -274,6 +274,7 @@ ETHEREAL_COMMON_SOURCES = \ ppptypes.h \ prefs.c \ prefs.h \ + prefs-int.h \ print.c \ print.h \ proto.c \ diff --git a/gtk/main.c b/gtk/main.c index 3622af20ff..f76a91ecd4 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -1,6 +1,6 @@ /* main.c * - * $Id: main.c,v 1.127 2000/07/05 02:04:16 guy Exp $ + * $Id: main.c,v 1.128 2000/07/05 09:41:04 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@zing.org> @@ -1144,15 +1144,16 @@ print_usage(void) { fprintf(stderr, "%s [ -vh ] [ -kQS ] [ -b <bold font> ] [ -B <byte view height> ]\n", PACKAGE); fprintf(stderr, "\t[ -c count ] [ -D ] [ -f <capture filter> ] [ -i interface ]\n"); - fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -P <packet list height> ] [ -r infile ]\n"); - fprintf(stderr, "\t[ -R <read filter> ] [ -s snaplen ] [ -t <time stamp format> ]\n"); - fprintf(stderr, "\t[ -T <tree view height> ] [ -w savefile ]\n"); + fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -o <preference setting> ] ...\n"); + fprintf(stderr, "\t[ -P <packet list height> ] [ -r infile ] [ -R <read filter> ]\n"); + fprintf(stderr, "\t[ -s snaplen ] [ -t <time stamp format> ] [ -T <tree view height> ]\n"); + fprintf(stderr, "\t[ -w savefile ]\n"); #else fprintf(stderr, "%s [ -vh ] [ -b <bold font> ] [ -B <byte view height> ]\n", PACKAGE); - fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -P <packet list height> ] [ -r infile ]\n"); - fprintf(stderr, "\t[ -R <read filter> ] [ -t <time stamp format> ]\n"); - fprintf(stderr, "\t[ -T <tree view height> ]\n"); + fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -o <preference setting ] ...\n"); + fprintf(stderr, "\t[ -P <packet list height> ] [ -r infile ] [ -R <read filter> ]\n"); + fprintf(stderr, "\t[ -t <time stamp format> ] [ -T <tree view height> ]\n"); #endif } @@ -1175,8 +1176,8 @@ main(int argc, char *argv[]) extern char pcap_version[]; #endif #endif - char *pf_path; - int pf_open_errno = 0; + char *gpf_path, *pf_path; + int gpf_open_errno, pf_open_errno; int err; #ifdef HAVE_LIBPCAP gboolean start_capture = FALSE; @@ -1201,6 +1202,16 @@ main(int argc, char *argv[]) capture_child = (strcmp(command_name, CHILD_NAME) == 0); #endif + /* Register all dissectors; we must do this before checking for the + "-G" flag, as the "-G" flag dumps a list of fields registered + by the dissectors, and we must do it before we read the preferences, + in case any dissectors register preferences. */ + dissect_init(); + + /* Now register the preferences for any non-dissector modules. + We must do that before we read the preferences as well. */ + prefs_register_modules(); + /* If invoked with the "-G" flag, we dump out a glossary of display filter symbols. @@ -1221,7 +1232,6 @@ main(int argc, char *argv[]) any arguments after the "-G" flag will not be used. */ if (argc >= 2 && strcmp(argv[1], "-G") == 0) { - dissect_init(); proto_registrar_dump(); exit(0); } @@ -1235,13 +1245,7 @@ main(int argc, char *argv[]) /* Let GTK get its args */ gtk_init (&argc, &argv); - prefs = read_prefs(&pf_path); - if (pf_path != NULL) { - /* The preferences file exists, but couldn't be opened; "pf_path" is - its pathname. Remember "errno", as that says why the attempt - failed. */ - pf_open_errno = errno; - } + prefs = read_prefs(&gpf_open_errno, &gpf_path, &pf_open_errno, &pf_path); /* Initialize the capture file struct */ cfile.plist = NULL; @@ -1312,7 +1316,7 @@ main(int argc, char *argv[]) ); /* Now get our args */ - while ((opt = getopt(argc, argv, "b:B:c:Df:hi:km:nP:Qr:R:Ss:t:T:w:W:vZ:")) != EOF) { + while ((opt = getopt(argc, argv, "b:B:c:Df:hi:km:no:P:Qr:R:Ss:t:T:w:W:vZ:")) != EOF) { switch (opt) { case 'b': /* Bold font */ bold_font = g_strdup(optarg); @@ -1367,6 +1371,21 @@ main(int argc, char *argv[]) case 'n': /* No name resolution */ g_resolving_actif = 0; break; + case 'o': /* Override preference from command line */ + switch (prefs_set_pref(optarg)) { + + case PREFS_SET_SYNTAX_ERR: + fprintf(stderr, "ethereal: Invalid -o flag \"%s\"\n", optarg); + exit(1); + break; + + case PREFS_SET_NO_SUCH_PREF: + fprintf(stderr, "ethereal: -o flag \"%s\" specifies unknown preference\n", + optarg); + exit(1); + break; + } + break; case 'P': /* Packet list pane height */ pl_size = atoi(optarg); break; @@ -1541,12 +1560,6 @@ main(int argc, char *argv[]) create_main_window(pl_size, tv_size, bv_size, prefs); -/* - Hmmm should we do it here -*/ - - dissect_init(); /* Init anything that needs initializing */ - #ifdef HAVE_LIBPCAP /* Is this a "child" ethereal, which is only supposed to pop up a capture box to let us stop the capture, and run a capture @@ -1611,12 +1624,21 @@ main(int argc, char *argv[]) } #endif - /* If we failed to open the preferences file, pop up an alert box; - we defer it until now, so that the alert box is more likely to - come up on top of the main window. */ + /* If the global preferences file exists but we failed to open it, + pop up an alert box; we defer that until now, so that the alert + box is more likely to come up on top of the main window. */ + if (gpf_path != NULL) { + simple_dialog(ESD_TYPE_WARN, NULL, + "Could not open global preferences file\n\"%s\": %s.", gpf_path, + strerror(gpf_open_errno)); + } + + /* If the user's preferences file exists but we failed to open it, + pop up an alert box; we defer that until now, so that the alert + box is more likely to come up on top of the main window. */ if (pf_path != NULL) { simple_dialog(ESD_TYPE_WARN, NULL, - "Could not open preferences file\n\"%s\": %s.", pf_path, + "Could not open your preferences file\n\"%s\": %s.", pf_path, strerror(pf_open_errno)); } diff --git a/gtk/prefs_dlg.c b/gtk/prefs_dlg.c index 76d09247e8..a107816ac5 100644 --- a/gtk/prefs_dlg.c +++ b/gtk/prefs_dlg.c @@ -1,7 +1,7 @@ /* prefs_dlg.c * Routines for handling preferences * - * $Id: prefs_dlg.c,v 1.13 2000/07/05 06:33:02 guy Exp $ + * $Id: prefs_dlg.c,v 1.14 2000/07/05 09:41:07 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@zing.org> @@ -59,6 +59,8 @@ #include "dlg_utils.h" #include "simple_dialog.h" +#include "prefs-int.h" + static void prefs_main_ok_cb(GtkWidget *, gpointer); static void prefs_main_save_cb(GtkWidget *, gpointer); static void prefs_main_cancel_cb(GtkWidget *, gpointer); @@ -78,6 +80,154 @@ static void prefs_main_destroy_cb(GtkWidget *, gpointer); */ static GtkWidget *prefs_w; +static void +pref_show(pref_t *pref, gpointer user_data) +{ + GtkWidget *main_tb = user_data; + const char *title; + char *label_string; + GtkWidget *label, *menu, *menu_item, *widget, *button; + GSList *rb_group; + char uint_str[10+1]; + const enum_val *enum_valp; + int menu_index, index; + + /* Give this preference a label which is its title, followed by a colon, + and left-align it. */ + title = pref->title; + label_string = g_malloc(strlen(title) + 2); + strcpy(label_string, title); + strcat(label_string, ":"); + label = gtk_label_new(label_string); + g_free(label_string); + gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); + + /* Attach it to the table. */ + gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, pref->ordinal, + pref->ordinal+1); + + /* Save the current value of the preference, so that we can revert it if + the user does "Apply" and then "Cancel", and create the control for + editing the preference. */ + switch (pref->type) { + + case PREF_UINT: + pref->saved_val.uint = *pref->varp.uint; + + /* XXX - there are no uint spinbuttons, so we can't use a spinbutton. + Even more annoyingly, even if there were, GLib doesn't define + G_MAXUINT - but I think ANSI C may define UINT_MAX, so we could + use that. */ + widget = gtk_entry_new(); + switch (pref->info.base) { + + case 10: + sprintf(uint_str, "%u", pref->saved_val.uint); + break; + + case 8: + sprintf(uint_str, "%o", pref->saved_val.uint); + break; + + case 16: + sprintf(uint_str, "%x", pref->saved_val.uint); + break; + } + gtk_entry_set_text(GTK_ENTRY(widget), uint_str); + pref->control = widget; + break; + + case PREF_BOOL: + pref->saved_val.bool = *pref->varp.bool; + widget = gtk_check_button_new(); + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(widget), pref->saved_val.bool); + pref->control = widget; + break; + + case PREF_ENUM: + pref->saved_val.enumval = *pref->varp.enump; + if (pref->info.enum_info.radio_buttons) { + /* Show it as radio buttons. */ + widget = gtk_hbox_new(FALSE, 0); + rb_group = NULL; + for (enum_valp = pref->info.enum_info.enumvals, index = 0; + enum_valp->name != NULL; enum_valp++, index++) { + button = gtk_radio_button_new_with_label(rb_group, enum_valp->name); + if (rb_group == NULL) + rb_group = gtk_radio_button_group(GTK_RADIO_BUTTON(button)); + gtk_box_pack_start(GTK_BOX(widget), button, FALSE, FALSE, 10); + if (enum_valp->value == pref->saved_val.enumval) + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE); + pref->control = button; + } + } else { + /* Show it as an option menu. */ + menu = gtk_menu_new(); + menu_index = -1; + for (enum_valp = pref->info.enum_info.enumvals, index = 0; + enum_valp->name != NULL; enum_valp++, index++) { + menu_item = gtk_menu_item_new_with_label(enum_valp->name); + gtk_menu_append(GTK_MENU(menu), menu_item); + if (enum_valp->value == pref->saved_val.enumval) + menu_index = index; + } + + /* Create the option menu from the option */ + widget = gtk_option_menu_new(); + gtk_option_menu_set_menu(GTK_OPTION_MENU(widget), menu); + + /* Set its current value to the variable's current value */ + if (menu_index != -1) + gtk_option_menu_set_history(GTK_OPTION_MENU(widget), menu_index); + pref->control = widget; + } + break; + + case PREF_STRING: + widget = gtk_entry_new(); + if (pref->saved_val.string != NULL) + g_free(pref->saved_val.string); + pref->saved_val.string = g_strdup(*pref->varp.string); + gtk_entry_set_text(GTK_ENTRY(widget), pref->saved_val.string); + pref->control = widget; + break; + + default: + g_assert_not_reached(); + widget = NULL; + break; + } + + gtk_table_attach_defaults(GTK_TABLE(main_tb), widget, 1, 2, pref->ordinal, + pref->ordinal+1); +} + +static void +module_prefs_show(module_t *module, gpointer user_data) +{ + GtkWidget *prefs_nb = user_data; + GtkWidget *main_vb, *main_tb, *label; + + /* Main vertical box */ + main_vb = gtk_vbox_new(FALSE, 5); + gtk_container_border_width(GTK_CONTAINER(main_vb), 5); + + /* Main table */ + main_tb = gtk_table_new(module->numprefs, 2, FALSE); + gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0); + gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10); + gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15); + + /* Add items for each of the preferences */ + prefs_pref_foreach(module, pref_show, main_tb); + + label = gtk_label_new(module->title); + gtk_notebook_append_page(GTK_NOTEBOOK(prefs_nb), main_vb, label); + + /* Show 'em what we got */ + gtk_widget_show_all(main_vb); +} + void prefs_cb(GtkWidget *w, gpointer dummy) { GtkWidget *main_vb, *top_hb, *bbox, *prefs_nb, @@ -136,6 +286,9 @@ prefs_cb(GtkWidget *w, gpointer dummy) { label = gtk_label_new ("GUI"); gtk_notebook_append_page (GTK_NOTEBOOK(prefs_nb), gui_pg, label); + /* Registered prefs */ + prefs_module_foreach(module_prefs_show, prefs_nb); + /* Button row: OK and cancel buttons */ bbox = gtk_hbutton_box_new(); gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END); @@ -174,12 +327,117 @@ prefs_cb(GtkWidget *w, gpointer dummy) { } static void +pref_fetch(pref_t *pref, gpointer user_data) +{ + GtkWidget *label; + char *label_string; + char *str_val; + char *p; + guint uval; + GSList *rb_entry; + GtkWidget *button; + + /* Fetch the value of the preference, and set the appropriate variable + to it. */ + switch (pref->type) { + + case PREF_UINT: + str_val = gtk_entry_get_text(GTK_ENTRY(pref->control)); + uval = strtoul(str_val, &p, pref->info.base); +#if 0 + if (p == value || *p != '\0') + return PREFS_SET_SYNTAX_ERR; /* number was bad */ +#endif + *pref->varp.uint = uval; + break; + + case PREF_BOOL: + *pref->varp.bool = GTK_TOGGLE_BUTTON(pref->control)->active; + break; + + case PREF_ENUM: + if (pref->info.enum_info.radio_buttons) { + /* Go through the list of of radio buttons in the group, and find + the first one that's active. */ + button = NULL; + for (rb_entry = gtk_radio_button_group(GTK_RADIO_BUTTON(pref->control)); + rb_entry != NULL; + rb_entry = g_slist_next(rb_entry)) { + button = rb_entry->data; + if (GTK_TOGGLE_BUTTON(button)->active) + break; + } + /* OK, now find that button's label. */ + label = GTK_BIN(button)->child; + } else { + /* Get the label for the currently active entry in the option menu. + Yes, this is how you do it. See FAQ 6.8 in the GTK+ FAQ. */ + label = GTK_BIN(pref->control)->child; + } + + /* Get the label, and translate it to a value. */ + gtk_label_get(GTK_LABEL(label), &label_string); + *pref->varp.enump = find_val_for_string(label_string, + pref->info.enum_info.enumvals, 1); + break; + + case PREF_STRING: + str_val = gtk_entry_get_text(GTK_ENTRY(pref->control)); + if (*pref->varp.string != NULL) + g_free(*pref->varp.string); + *pref->varp.string = g_strdup(str_val); + break; + } +} + +static void +module_prefs_fetch(module_t *module, gpointer user_data) +{ + /* For all preferences in this module, fetch its value from this + module's notebook page. */ + prefs_pref_foreach(module, pref_fetch, NULL); +} + +static void +pref_clean(pref_t *pref, gpointer user_data) +{ + switch (pref->type) { + + case PREF_UINT: + break; + + case PREF_BOOL: + break; + + case PREF_ENUM: + break; + + case PREF_STRING: + if (pref->saved_val.string != NULL) { + g_free(pref->saved_val.string); + pref->saved_val.string = NULL; + } + break; + } +} + +static void +module_prefs_clean(module_t *module, gpointer user_data) +{ + /* For all preferences in this module, clean up any cruft allocated for + use by the GUI code. */ + prefs_pref_foreach(module, pref_clean, NULL); +} + +static void prefs_main_ok_cb(GtkWidget *ok_bt, gpointer parent_w) { printer_prefs_ok(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY)); column_prefs_ok(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY)); stream_prefs_ok(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY)); gui_prefs_ok(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY)); + prefs_module_foreach(module_prefs_fetch, NULL); + prefs_module_foreach(module_prefs_clean, NULL); gtk_widget_destroy(GTK_WIDGET(parent_w)); } @@ -193,6 +451,7 @@ prefs_main_save_cb(GtkWidget *save_bt, gpointer parent_w) column_prefs_save(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY)); stream_prefs_save(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY)); gui_prefs_save(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY)); + prefs_module_foreach(module_prefs_fetch, NULL); err = write_prefs(&pf_path); if (err != 0) { simple_dialog(ESD_TYPE_WARN, NULL, @@ -202,12 +461,48 @@ prefs_main_save_cb(GtkWidget *save_bt, gpointer parent_w) } static void +pref_revert(pref_t *pref, gpointer user_data) +{ + /* Fetch the value of the preference, and set the appropriate variable + to it. */ + switch (pref->type) { + + case PREF_UINT: + *pref->varp.uint = pref->saved_val.uint; + break; + + case PREF_BOOL: + *pref->varp.bool = pref->saved_val.bool; + break; + + case PREF_ENUM: + *pref->varp.enump = pref->saved_val.enumval; + break; + + case PREF_STRING: + if (*pref->varp.string != NULL) + g_free(*pref->varp.string); + *pref->varp.string = g_strdup(pref->saved_val.string); + break; + } +} + +static void +module_prefs_revert(module_t *module, gpointer user_data) +{ + /* For all preferences in this module, revert its value to the value + it had when we popped up the Preferences dialog. */ + prefs_pref_foreach(module, pref_revert, NULL); +} + +static void prefs_main_cancel_cb(GtkWidget *cancel_bt, gpointer parent_w) { printer_prefs_cancel(gtk_object_get_data(GTK_OBJECT(parent_w), E_PRINT_PAGE_KEY)); column_prefs_cancel(gtk_object_get_data(GTK_OBJECT(parent_w), E_COLUMN_PAGE_KEY)); stream_prefs_cancel(gtk_object_get_data(GTK_OBJECT(parent_w), E_STREAM_PAGE_KEY)); gui_prefs_cancel(gtk_object_get_data(GTK_OBJECT(parent_w), E_GUI_PAGE_KEY)); + prefs_module_foreach(module_prefs_revert, NULL); gtk_widget_destroy(GTK_WIDGET(parent_w)); } diff --git a/packet-ip.c b/packet-ip.c index db729e6126..523357cde8 100644 --- a/packet-ip.c +++ b/packet-ip.c @@ -1,7 +1,7 @@ /* packet-ip.c * Routines for IP and miscellaneous IP protocol packet disassembly * - * $Id: packet-ip.c,v 1.94 2000/06/20 13:21:55 gram Exp $ + * $Id: packet-ip.c,v 1.95 2000/07/05 09:40:38 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@zing.org> @@ -40,6 +40,7 @@ #include <glib.h> #include "packet.h" #include "resolv.h" +#include "prefs.h" #ifdef NEED_SNPRINTF_H # ifdef HAVE_STDARG_H @@ -1390,6 +1391,7 @@ proto_register_ip(void) &ett_ip_option_route, &ett_ip_option_timestamp, }; + module_t *ip_module; proto_ip = proto_register_protocol ("Internet Protocol", "ip"); proto_register_field_array(proto_ip, hf, array_length(hf)); @@ -1397,6 +1399,13 @@ proto_register_ip(void) /* subdissector code */ ip_dissector_table = register_dissector_table("ip.proto"); + + /* Register a configuration option for decoding TOS as DSCP */ + ip_module = prefs_register_module("ip", "IP", NULL); + prefs_register_bool_preference(ip_module, "decode_tos_as_diffserv", + "Decode IPv4 TOS field as DiffServ field", +"Whether the IPv4 type-of-service field should be decoded as a Differentiated Services field", + &g_ip_dscp_actif); } void diff --git a/prefs-int.h b/prefs-int.h new file mode 100644 index 0000000000..0e96f5ff39 --- /dev/null +++ b/prefs-int.h @@ -0,0 +1,79 @@ +/* prefs-int.h + * Definitions for implementation of preference handling routines; + * used by "friends" of the preferences type. + * + * $Id: prefs-int.h,v 1.1 2000/07/05 09:40:40 guy Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@zing.org> + * Copyright 1998 Gerald Combs + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PREFS_INT_H__ +#define __PREFS_INT_H__ + +struct pref_module { + const char *name; /* name of module */ + const char *title; /* title of module (displayed in preferences notebook) */ + void (*apply_cb)(void); /* routine to call when preferences applied */ + GList *prefs; /* list of its preferences */ + int numprefs; /* number of preferences */ +}; + +typedef enum { + PREF_UINT, + PREF_BOOL, + PREF_ENUM, + PREF_STRING +} pref_type_t; + +struct preference { + const char *name; /* name of preference */ + const char *title; /* title to use in GUI */ + const char *description; /* human-readable description of preference */ + int ordinal; /* ordinal number of this preference */ + pref_type_t type; /* type of that preference */ + union { + guint *uint; + gboolean *bool; + gint *enump; + char **string; + } varp; /* pointer to variable storing the value */ + union { + guint uint; + gboolean bool; + gint enumval; + char *string; + } saved_val; /* original value, when editing from the GUI */ + union { + guint base; /* input/output base, for PREF_UINT */ + struct { + const enum_val *enumvals; /* list of name & values */ + gboolean radio_buttons; /* TRUE if it should be shown as + radio buttons rather than as an + option menu or combo box in + the preferences tab */ + } enum_info; /* for PREF_ENUM */ + } info; /* display/text file information */ + void *control; /* handle for GUI control for this preference */ +}; + +gint find_val_for_string(const char *needle, const enum_val *haystack, + gint default_value); + +#endif /* prefs-int.h */ @@ -1,7 +1,7 @@ /* prefs.c * Routines for handling preferences * - * $Id: prefs.c,v 1.30 2000/01/29 16:41:14 gram Exp $ + * $Id: prefs.c,v 1.31 2000/07/05 09:40:41 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@zing.org> @@ -36,6 +36,7 @@ #endif #include <stdlib.h> +#include <string.h> #include <ctype.h> #include <errno.h> @@ -53,6 +54,8 @@ #include "print.h" #include "util.h" +#include "prefs-int.h" + /* Internal functions */ static int set_pref(gchar*, gchar*); static GList *get_string_list(gchar *); @@ -60,7 +63,9 @@ static void clear_string_list(GList *); #define PF_NAME "preferences" -static int init_prefs = 1; +#define GPF_PATH DATAFILE_DIR "/ethereal.conf" + +static gboolean init_prefs = TRUE; static gchar *pf_path = NULL; e_prefs prefs; @@ -72,6 +77,244 @@ gchar *gui_ptree_expander_style_text[] = { "NONE", "SQUARE", "TRIANGLE", "CIRCULAR", NULL }; +/* + * List of modules with preference settings. + */ +static GList *modules; + +/* + * Register a module that will have preferences. + * Specify the name used for the module in the preferences file, the + * title used in the tab for it in a preferences dialog box, and a + * routine to call back when we apply the preferences. + */ +module_t * +prefs_register_module(const char *name, const char *title, + void (*apply_cb)(void)) +{ + module_t *module; + + module = g_malloc(sizeof (module_t)); + module->name = name; + module->title = title; + module->apply_cb = apply_cb; + module->prefs = NULL; /* no preferences, to start */ + module->numprefs = 0; + + modules = g_list_append(modules, module); + + return module; +} + +/* + * Find a module, given its name. + */ +static gint +module_match(gconstpointer a, gconstpointer b) +{ + const module_t *module = a; + const char *name = b; + + return strcmp(name, module->name); +} + +static module_t * +find_module(char *name) +{ + GList *list_entry; + + list_entry = g_list_find_custom(modules, name, module_match); + if (list_entry == NULL) + return NULL; /* no such module */ + return (module_t *) list_entry->data; +} + +typedef struct { + module_cb callback; + gpointer user_data; +} module_cb_arg_t; + +static void +do_module_callback(gpointer data, gpointer user_data) +{ + module_t *module = data; + module_cb_arg_t *arg = user_data; + + (*arg->callback)(module, arg->user_data); +} + +/* + * Call a callback function, with a specified argument, for each module. + */ +void +prefs_module_foreach(module_cb callback, gpointer user_data) +{ + module_cb_arg_t arg; + + arg.callback = callback; + arg.user_data = user_data; + g_list_foreach(modules, do_module_callback, &arg); +} + +static void +call_apply_cb(gpointer data, gpointer user_data) +{ + module_t *module = data; + + (*module->apply_cb)(); +} + +/* + * Call the "apply" callback function for each module. + */ +void +prefs_apply_all(void) +{ + g_list_foreach(modules, call_apply_cb, NULL); +} + +/* + * Register a preference in a module's list of preferences. + */ +static pref_t * +register_preference(module_t *module, const char *name, const char *title, + const char *description) +{ + pref_t *preference; + + preference = g_malloc(sizeof (pref_t)); + preference->name = name; + preference->title = title; + preference->description = description; + preference->ordinal = module->numprefs; + + module->prefs = g_list_append(module->prefs, preference); + module->numprefs++; + + return preference; +} + +/* + * Find a preference in a module's list of preferences, given the module + * and the preference's name. + */ +static gint +preference_match(gconstpointer a, gconstpointer b) +{ + const pref_t *pref = a; + const char *name = b; + + return strcmp(name, pref->name); +} + +static struct preference * +find_preference(module_t *module, char *name) +{ + GList *list_entry; + + list_entry = g_list_find_custom(module->prefs, name, preference_match); + if (list_entry == NULL) + return NULL; /* no such preference */ + return (struct preference *) list_entry->data; +} + +/* + * Register a preference with an unsigned integral value. + */ +void +prefs_register_uint_preference(module_t *module, const char *name, + const char *title, const char *description, guint base, guint *var) +{ + pref_t *preference; + + preference = register_preference(module, name, title, description); + preference->type = PREF_UINT; + preference->varp.uint = var; + preference->info.base = base; +} + +/* + * Register a preference with an Boolean value. + */ +void +prefs_register_bool_preference(module_t *module, const char *name, + const char *title, const char *description, gboolean *var) +{ + pref_t *preference; + + preference = register_preference(module, name, title, description); + preference->type = PREF_BOOL; + preference->varp.bool = var; +} + +/* + * Register a preference with an enumerated value. + */ +void +prefs_register_enum_preference(module_t *module, const char *name, + const char *title, const char *description, gint *var, + const enum_val *enumvals, gboolean radio_buttons) +{ + pref_t *preference; + + preference = register_preference(module, name, title, description); + preference->type = PREF_ENUM; + preference->varp.enump = var; + preference->info.enum_info.enumvals = enumvals; + preference->info.enum_info.radio_buttons = radio_buttons; +} + +/* + * Register a preference with a character-string value. + */ +void +prefs_register_string_preference(module_t *module, const char *name, + const char *title, const char *description, char **var) +{ + pref_t *preference; + + preference = register_preference(module, name, title, description); + preference->type = PREF_STRING; + preference->varp.string = var; + preference->saved_val.string = NULL; +} + +typedef struct { + pref_cb callback; + gpointer user_data; +} pref_cb_arg_t; + +static void +do_pref_callback(gpointer data, gpointer user_data) +{ + pref_t *pref = data; + pref_cb_arg_t *arg = user_data; + + (*arg->callback)(pref, arg->user_data); +} + +/* + * Call a callback function, with a specified argument, for each preference + * in a given module. + */ +void +prefs_pref_foreach(module_t *module, pref_cb callback, gpointer user_data) +{ + pref_cb_arg_t arg; + + arg.callback = callback; + arg.user_data = user_data; + g_list_foreach(module->prefs, do_pref_callback, &arg); +} + +/* + * Register all non-dissector modules' preferences. + */ +void +prefs_register_modules(void) +{ +} + /* Parse through a list of comma-separated, quoted strings. Return a list of the string data */ static GList * @@ -129,6 +372,29 @@ clear_string_list(GList *sl) { } } +/* + * Takes a string, a pointer to an array of "enum_val"s, and a default gint + * value. + * The array must be terminated by an entry with a null "name" string. + * If the string matches a "name" strings in an entry, the value from that + * entry is returned. Otherwise, the default value that was passed as the + * third argument is returned. + */ +gint +find_val_for_string(const char *needle, const enum_val *haystack, + gint default_value) +{ + int i = 0; + + while (haystack[i].name != NULL) { + if (strcasecmp(needle, haystack[i].name) == 0) { + return haystack[i].value; + } + i++; + } + return default_value; +} + /* Takes an string and a pointer to an array of strings, and a default int value. * The array must be terminated by a NULL string. If the string is found in the array * of strings, the index of that string in the array is returned. Otherwise, the @@ -167,24 +433,26 @@ print.file: /a/very/long/path/ #define MAX_VAL_LEN 1024 #define DEF_NUM_COLS 6 + +static void read_prefs_file(const char *pf_path, FILE *pf); + e_prefs * -read_prefs(char **pf_path_return) { - enum { START, IN_VAR, PRE_VAL, IN_VAL, IN_SKIP }; +read_prefs(int *gpf_errno_return, char **gpf_path_return, + int *pf_errno_return, char **pf_path_return) +{ + int i; FILE *pf; - gchar cur_var[MAX_VAR_LEN], cur_val[MAX_VAL_LEN]; - int got_c, state = START, i; - gint var_len = 0, val_len = 0, fline = 1, pline = 1; - gboolean got_val = FALSE; fmt_data *cfmt; gchar *col_fmt[] = {"No.", "%m", "Time", "%t", "Source", "%s", "Destination", "%d", "Protocol", "%p", "Info", "%i"}; - /* Initialize preferences. With any luck, these values will be - overwritten below. */ if (init_prefs) { - init_prefs = 0; + /* Initialize preferences to wired-in default values. + They may be overridded by the global preferences file or the + user's preferences file. */ + init_prefs = FALSE; prefs.pr_format = PR_FMT_TEXT; prefs.pr_dest = PR_DEST_CMD; prefs.pr_file = g_strdup("ethereal.out"); @@ -220,19 +488,57 @@ read_prefs(char **pf_path_return) { prefs.gui_ptree_expander_style = 1; } + /* Read the global preferences file, if it exists. */ + *gpf_path_return = NULL; + if ((pf = fopen(GPF_PATH, "r")) != NULL) { + /* We succeeded in opening it; read it. */ + read_prefs_file(GPF_PATH, pf); + fclose(pf); + } else { + /* We failed to open it. If we failed for some reason other than + "it doesn't exist", return the errno and the pathname, so our + caller can report the error. */ + if (errno != ENOENT) { + *gpf_errno_return = errno; + *gpf_path_return = GPF_PATH; + } + } + + /* Construct the pathname of the user's preferences file. */ if (! pf_path) { pf_path = (gchar *) g_malloc(strlen(get_home_dir()) + strlen(PF_DIR) + strlen(PF_NAME) + 4); sprintf(pf_path, "%s/%s/%s", get_home_dir(), PF_DIR, PF_NAME); } + /* Read the user's preferences file, if it exists. */ *pf_path_return = NULL; - if ((pf = fopen(pf_path, "r")) == NULL) { - if (errno != ENOENT) + if ((pf = fopen(pf_path, "r")) != NULL) { + /* We succeeded in opening it; read it. */ + read_prefs_file(pf_path, pf); + fclose(pf); + } else { + /* We failed to open it. If we failed for some reason other than + "it doesn't exist", return the errno and the pathname, so our + caller can report the error. */ + if (errno != ENOENT) { + *pf_errno_return = errno; *pf_path_return = pf_path; - return &prefs; + } } - + + return &prefs; +} + +static void +read_prefs_file(const char *pf_path, FILE *pf) +{ + enum { START, IN_VAR, PRE_VAL, IN_VAL, IN_SKIP }; + gchar cur_var[MAX_VAR_LEN], cur_val[MAX_VAL_LEN]; + int got_c, state = START; + gboolean got_val = FALSE; + gint var_len = 0, val_len = 0, fline = 1, pline = 1; + while ((got_c = getc(pf)) != EOF) { if (got_c == '\n') { state = START; @@ -259,8 +565,17 @@ read_prefs(char **pf_path_return) { if (got_val) { cur_var[var_len] = '\0'; cur_val[val_len] = '\0'; - if (! set_pref(cur_var, cur_val)) - g_warning ("%s line %d: Bogus preference", pf_path, pline); + switch (set_pref(cur_var, cur_val)) { + + case PREFS_SET_SYNTAX_ERR: + g_warning ("%s line %d: Syntax error", pf_path, pline); + break; + + case PREFS_SET_NO_SUCH_PREF: + g_warning ("%s line %d: No such preference \"%s\"", pf_path, + pline, cur_var); + break; + } } else { g_warning ("%s line %d: Incomplete preference", pf_path, pline); } @@ -311,15 +626,62 @@ read_prefs(char **pf_path_return) { if (got_val) { cur_var[var_len] = '\0'; cur_val[val_len] = '\0'; - if (! set_pref(cur_var, cur_val)) - g_warning ("%s line %d: Bogus preference", pf_path, pline); + switch (set_pref(cur_var, cur_val)) { + + case PREFS_SET_SYNTAX_ERR: + g_warning ("%s line %d: Syntax error", pf_path, pline); + break; + + case PREFS_SET_NO_SUCH_PREF: + g_warning ("%s line %d: No such preference \"%s\"", pf_path, + pline, cur_var); + break; + } } else { g_warning ("%s line %d: Incomplete preference", pf_path, pline); } } - fclose(pf); - - return &prefs; +} + +/* + * Given a string of the form "<pref name>:<pref value>", as might appear + * as an argument to a "-o" option, parse it and set the preference in + * question. Return an indication of whether it succeeded or failed + * in some fashion. + */ +int +prefs_set_pref(char *prefarg) +{ + u_char *p, *colonp; + int ret; + + colonp = strchr(prefarg, ':'); + if (colonp == NULL) + return PREFS_SET_SYNTAX_ERR; + + p = colonp; + *p++ = '\0'; + + /* + * Skip over any white space (there probably won't be any, but + * as we allow it in the preferences file, we might as well + * allow it here). + */ + while (isspace(*p)) + p++; + if (*p == '\0') { + /* + * Put the colon back, so if our caller uses, in an + * error message, the string they passed us, the message + * looks correct. + */ + *colonp = ':'; + return PREFS_SET_SYNTAX_ERR; + } + + ret = set_pref(prefarg, p); + *colonp = ':'; /* put the colon back */ + return ret; } #define PRS_PRINT_FMT "print.format" @@ -344,36 +706,42 @@ read_prefs(char **pf_path_return) { static gchar *pr_formats[] = { "text", "postscript" }; static gchar *pr_dests[] = { "command", "file" }; -int -set_pref(gchar *pref, gchar *value) { +static int +set_pref(gchar *pref_name, gchar *value) +{ GList *col_l; gint llen; fmt_data *cfmt; unsigned long int cval; + guint uval; + char *p; + gchar *dotp; + module_t *module; + pref_t *pref; - if (strcmp(pref, PRS_PRINT_FMT) == 0) { + if (strcmp(pref_name, PRS_PRINT_FMT) == 0) { if (strcmp(value, pr_formats[PR_FMT_TEXT]) == 0) { prefs.pr_format = PR_FMT_TEXT; } else if (strcmp(value, pr_formats[PR_FMT_PS]) == 0) { prefs.pr_format = PR_FMT_PS; } else { - return 0; + return PREFS_SET_SYNTAX_ERR; } - } else if (strcmp(pref, PRS_PRINT_DEST) == 0) { + } else if (strcmp(pref_name, PRS_PRINT_DEST) == 0) { if (strcmp(value, pr_dests[PR_DEST_CMD]) == 0) { prefs.pr_dest = PR_DEST_CMD; } else if (strcmp(value, pr_dests[PR_DEST_FILE]) == 0) { prefs.pr_dest = PR_DEST_FILE; } else { - return 0; + return PREFS_SET_SYNTAX_ERR; } - } else if (strcmp(pref, PRS_PRINT_FILE) == 0) { + } else if (strcmp(pref_name, PRS_PRINT_FILE) == 0) { if (prefs.pr_file) g_free(prefs.pr_file); prefs.pr_file = g_strdup(value); - } else if (strcmp(pref, PRS_PRINT_CMD) == 0) { + } else if (strcmp(pref_name, PRS_PRINT_CMD) == 0) { if (prefs.pr_cmd) g_free(prefs.pr_cmd); prefs.pr_cmd = g_strdup(value); - } else if (strcmp(pref, PRS_COL_FMT) == 0) { + } else if (strcmp(pref_name, PRS_COL_FMT) == 0) { if ((col_l = get_string_list(value)) && (g_list_length(col_l) % 2) == 0) { while (prefs.col_list) { cfmt = prefs.col_list->data; @@ -396,66 +764,195 @@ set_pref(gchar *pref, gchar *value) { /* To do: else print some sort of error? */ } clear_string_list(col_l); - } else if (strcmp(pref, PRS_STREAM_CL_FG) == 0) { + } else if (strcmp(pref_name, PRS_STREAM_CL_FG) == 0) { cval = strtoul(value, NULL, 16); prefs.st_client_fg.pixel = 0; prefs.st_client_fg.red = RED_COMPONENT(cval); prefs.st_client_fg.green = GREEN_COMPONENT(cval); prefs.st_client_fg.blue = BLUE_COMPONENT(cval); - } else if (strcmp(pref, PRS_STREAM_CL_BG) == 0) { + } else if (strcmp(pref_name, PRS_STREAM_CL_BG) == 0) { cval = strtoul(value, NULL, 16); prefs.st_client_bg.pixel = 0; prefs.st_client_bg.red = RED_COMPONENT(cval); prefs.st_client_bg.green = GREEN_COMPONENT(cval); prefs.st_client_bg.blue = BLUE_COMPONENT(cval); - } else if (strcmp(pref, PRS_STREAM_SR_FG) == 0) { + } else if (strcmp(pref_name, PRS_STREAM_SR_FG) == 0) { cval = strtoul(value, NULL, 16); prefs.st_server_fg.pixel = 0; prefs.st_server_fg.red = RED_COMPONENT(cval); prefs.st_server_fg.green = GREEN_COMPONENT(cval); prefs.st_server_fg.blue = BLUE_COMPONENT(cval); - } else if (strcmp(pref, PRS_STREAM_SR_BG) == 0) { + } else if (strcmp(pref_name, PRS_STREAM_SR_BG) == 0) { cval = strtoul(value, NULL, 16); prefs.st_server_bg.pixel = 0; prefs.st_server_bg.red = RED_COMPONENT(cval); prefs.st_server_bg.green = GREEN_COMPONENT(cval); prefs.st_server_bg.blue = BLUE_COMPONENT(cval); - } else if (strcmp(pref, PRS_GUI_SCROLLBAR_ON_RIGHT) == 0) { + } else if (strcmp(pref_name, PRS_GUI_SCROLLBAR_ON_RIGHT) == 0) { if (strcmp(value, "TRUE") == 0) { prefs.gui_scrollbar_on_right = TRUE; } else { prefs.gui_scrollbar_on_right = FALSE; } - } else if (strcmp(pref, PRS_GUI_PLIST_SEL_BROWSE) == 0) { + } else if (strcmp(pref_name, PRS_GUI_PLIST_SEL_BROWSE) == 0) { if (strcmp(value, "TRUE") == 0) { prefs.gui_plist_sel_browse = TRUE; } else { prefs.gui_plist_sel_browse = FALSE; } - } else if (strcmp(pref, PRS_GUI_PTREE_SEL_BROWSE) == 0) { + } else if (strcmp(pref_name, PRS_GUI_PTREE_SEL_BROWSE) == 0) { if (strcmp(value, "TRUE") == 0) { prefs.gui_ptree_sel_browse = TRUE; } else { prefs.gui_ptree_sel_browse = FALSE; } - } else if (strcmp(pref, PRS_GUI_PTREE_LINE_STYLE) == 0) { + } else if (strcmp(pref_name, PRS_GUI_PTREE_LINE_STYLE) == 0) { prefs.gui_ptree_line_style = find_index_from_string_array(value, gui_ptree_line_style_text, 0); - } else if (strcmp(pref, PRS_GUI_PTREE_EXPANDER_STYLE) == 0) { + } else if (strcmp(pref_name, PRS_GUI_PTREE_EXPANDER_STYLE) == 0) { prefs.gui_ptree_expander_style = find_index_from_string_array(value, gui_ptree_expander_style_text, 1); } else { - return 0; + /* To which module does this preference belong? */ + dotp = strchr(pref_name, '.'); + if (dotp == NULL) + return PREFS_SET_SYNTAX_ERR; /* no ".", so no module/name separator */ + *dotp = '\0'; /* separate module and preference name */ + module = find_module(pref_name); + *dotp = '.'; /* put the preference string back */ + if (module == NULL) + return PREFS_SET_NO_SUCH_PREF; /* no such module */ + dotp++; /* skip past separator to preference name */ + pref = find_preference(module, dotp); + if (pref == NULL) + return PREFS_SET_NO_SUCH_PREF; /* no such preference */ + + switch (pref->type) { + + case PREF_UINT: + uval = strtoul(value, &p, pref->info.base); + if (p == value || *p != '\0') + return PREFS_SET_SYNTAX_ERR; /* number was bad */ + *pref->varp.uint = uval; + break; + + case PREF_BOOL: + /* XXX - give an error if it's neither "true" nor "false"? */ + if (strcasecmp(value, "true") == 0) + *pref->varp.bool = TRUE; + else + *pref->varp.bool = FALSE; + break; + + case PREF_ENUM: + /* XXX - give an error if it doesn't match? */ + *pref->varp.enump = find_val_for_string(value, + pref->info.enum_info.enumvals, 1); + break; + + case PREF_STRING: + if (*pref->varp.string != NULL) + g_free(*pref->varp.string); + *pref->varp.string = g_strdup(value); + break; + } } - return 1; + return PREFS_SET_OK; +} + +typedef struct { + module_t *module; + FILE *pf; +} write_pref_arg_t; + +/* + * Write out a single preference. + */ +static void +write_pref(gpointer data, gpointer user_data) +{ + pref_t *pref = data; + write_pref_arg_t *arg = user_data; + const enum_val *enum_valp; + const char *val_string; + + fprintf(arg->pf, "\n# %s\n", pref->description); + + switch (pref->type) { + + case PREF_UINT: + switch (pref->info.base) { + + case 10: + fprintf(arg->pf, "# A decimal number.\n"); + fprintf(arg->pf, "%s.%s: %u\n", arg->module->name, + pref->name, *pref->varp.uint); + break; + + case 8: + fprintf(arg->pf, "# An octal number.\n"); + fprintf(arg->pf, "%s.%s: %#o\n", arg->module->name, + pref->name, *pref->varp.uint); + break; + + case 16: + fprintf(arg->pf, "# A hexadecimal number.\n"); + fprintf(arg->pf, "%s.%s: %#x\n", arg->module->name, + pref->name, *pref->varp.uint); + break; + } + break; + + case PREF_BOOL: + fprintf(arg->pf, "# TRUE or FALSE (case-insensitive).\n"); + fprintf(arg->pf, "%s.%s: %s\n", arg->module->name, pref->name, + *pref->varp.bool ? "TRUE" : "FALSE"); + break; + + case PREF_ENUM: + fprintf(arg->pf, "# One of: "); + enum_valp = pref->info.enum_info.enumvals; + val_string = NULL; + while (enum_valp->name != NULL) { + if (enum_valp->value == *pref->varp.enump) + val_string = enum_valp->name; + fprintf(arg->pf, "%s", enum_valp->name); + enum_valp++; + if (enum_valp->name == NULL) + fprintf(arg->pf, "\n"); + else + fprintf(arg->pf, ", "); + } + fprintf(arg->pf, "# (case-insensitive).\n"); + fprintf(arg->pf, "%s.%s: %s\n", arg->module->name, pref->name, + val_string); + break; + + case PREF_STRING: + fprintf(arg->pf, "# A string.\n"); + fprintf(arg->pf, "%s.%s: %s\n", arg->module->name, pref->name, + *pref->varp.string); + break; + } +} + +static void +write_module_prefs(gpointer data, gpointer user_data) +{ + write_pref_arg_t arg; + + arg.module = data; + arg.pf = user_data; + g_list_foreach(arg.module->prefs, write_pref, &arg); } int -write_prefs(char **pf_path_return) { +write_prefs(char **pf_path_return) +{ FILE *pf; struct stat s_buf; @@ -549,6 +1046,8 @@ write_prefs(char **pf_path_return) { fprintf(pf, PRS_GUI_PTREE_EXPANDER_STYLE ": %s\n", gui_ptree_expander_style_text[prefs.gui_ptree_expander_style]); + g_list_foreach(modules, write_module_prefs, pf); + fclose(pf); /* XXX - catch I/O errors (e.g. "ran out of disk space") and return @@ -1,7 +1,7 @@ /* prefs.h * Definitions for preference handling routines * - * $Id: prefs.h,v 1.15 2000/01/06 07:33:22 guy Exp $ + * $Id: prefs.h,v 1.16 2000/07/05 09:40:42 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@zing.org> @@ -50,7 +50,96 @@ typedef struct _e_prefs { extern e_prefs prefs; -e_prefs* read_prefs(char **); +/* + * Routines to let modules that have preference settings register + * themselves by name, and to let them register preference settings + * by name. + */ +struct pref_module; + +typedef struct pref_module module_t; + +/* + * Register a module that will have preferences. + * Specify the name used for the module in the preferences file, the + * title used in the tab for it in a preferences dialog box, and a + * routine to call back when we apply the preferences. + */ +module_t *prefs_register_module(const char *name, const char *title, + void (*apply_cb)(void)); + +typedef void (*module_cb)(module_t *module, gpointer user_data); + +/* + * Call a callback function, with a specified argument, for each module. + */ +void prefs_module_foreach(module_cb callback, gpointer user_data); + +/* + * Call the "apply" callback function for each module. + */ +void prefs_apply_all(void); + +struct preference; + +typedef struct preference pref_t; + +/* + * Register a preference with an unsigned integral value. + */ +void prefs_register_uint_preference(module_t *module, const char *name, + const char *title, const char *description, guint base, guint *var); + +/* + * Register a preference with an Boolean value. + */ +void prefs_register_bool_preference(module_t *module, const char *name, + const char *title, const char *description, gboolean *var); + +/* + * Register a preference with an enumerated value. + */ +typedef struct { + char *name; + gint value; +} enum_val; + +void prefs_register_enum_preference(module_t *module, const char *name, + const char *title, const char *description, gint *var, + const enum_val *enumvals, gboolean radio_buttons); + +/* + * Register a preference with a character-string value. + */ +void prefs_register_string_preference(module_t *module, const char *name, + const char *title, const char *description, char **var); + +typedef void (*pref_cb)(pref_t *pref, gpointer user_data); + +/* + * Call a callback function, with a specified argument, for each preference + * in a given module. + */ +void prefs_pref_foreach(module_t *module, pref_cb callback, gpointer user_data); + +/* + * Register all non-dissector modules' preferences. + */ +void prefs_register_modules(void); + +e_prefs *read_prefs(int *, char **, int *, char **); int write_prefs(char **); +/* + * Given a string of the form "<pref name>:<pref value>", as might appear + * as an argument to a "-o" option, parse it and set the preference in + * question. Return an indication of whether it succeeded or failed + * in some fashion. + */ +#define PREFS_SET_OK 0 /* succeeded */ +#define PREFS_SET_SYNTAX_ERR 1 /* syntax error in string */ +#define PREFS_SET_NO_SUCH_PREF 2 /* no such preference */ + +int prefs_set_pref(char *prefarg); + #endif /* prefs.h */ diff --git a/tethereal.c b/tethereal.c index 318f321619..bc98efed2b 100644 --- a/tethereal.c +++ b/tethereal.c @@ -1,6 +1,6 @@ /* tethereal.c * - * $Id: tethereal.c,v 1.33 2000/07/05 02:06:58 guy Exp $ + * $Id: tethereal.c,v 1.34 2000/07/05 09:40:43 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@zing.org> @@ -132,13 +132,13 @@ print_usage(void) VERSION, comp_info_str); #ifdef HAVE_LIBPCAP fprintf(stderr, "t%s [ -vVh ] [ -c count ] [ -D ] [ -f <capture filter> ]\n", PACKAGE); - fprintf(stderr, "\t[ -F <capture file type> ] [ -i interface ] [ -n ] [ -r infile ]\n"); - fprintf(stderr, "\t[ -R <read filter> ] [ -s snaplen ] [ -t <time stamp format> ]\n"); - fprintf(stderr, "\t[ -w savefile ] [ -x ]\n"); + fprintf(stderr, "\t[ -F <capture file type> ] [ -i interface ] [ -n ]\n"); + fprintf(stderr, "\t[ -o <preference setting> ] ... [ -r infile ] [ -R <read filter> ]\n"); + fprintf(stderr, "\t[ -s snaplen ] [ -t <time stamp format> ] [ -w savefile ] [ -x ]\n"); #else - fprintf(stderr, "t%s [ -vVh ] [ -D ] [ -F <capture file type> ] [ -n ] [ -r infile ]\n", PACKAGE); - fprintf(stderr, "\t[ -R <read filter> ] [ -t <time stamp format> ] [ -w savefile ]\n"); - fprintf(stderr, "\t[ -x ]\n"); + fprintf(stderr, "t%s [ -vVh ] [ -D ] [ -F <capture file type> ] [ -n ]\n", PACKAGE); + fprintf(stderr, "\t[ -o <preference setting> ] ... [ -r infile ] [ -R <read filter> ]\n"); + fprintf(stderr, "\t[ -t <time stamp format> ] [ -w savefile ] [ -x ]\n"); #endif fprintf(stderr, "Valid file type arguments to the \"-F\" flag:\n"); for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) { @@ -162,7 +162,8 @@ main(int argc, char *argv[]) extern char pcap_version[]; #endif #endif - char *pf_path; + char *gpf_path, *pf_path; + int gpf_open_errno, pf_open_errno; int err; #ifdef HAVE_LIBPCAP gboolean capture_filter_specified = FALSE; @@ -177,13 +178,22 @@ main(int argc, char *argv[]) dfilter *rfcode = NULL; e_prefs *prefs; + /* Register all dissectors; we must do this before checking for the + "-G" flag, as the "-G" flag dumps a list of fields registered + by the dissectors, and we must do it before we read the preferences, + in case any dissectors register preferences. */ + dissect_init(); + + /* Now register the preferences for any non-dissector modules. + We must do that before we read the preferences as well. */ + prefs_register_modules(); + /* If invoked with the "-G" flag, we dump out a glossary of display filter symbols. We do this here to mirror what happens in the GTK+ version, although it's not necessary here. */ if (argc >= 2 && strcmp(argv[1], "-G") == 0) { - dissect_init(); proto_registrar_dump(); exit(0); } @@ -191,10 +201,14 @@ main(int argc, char *argv[]) /* Set the C-language locale to the native environment. */ setlocale(LC_ALL, ""); - prefs = read_prefs(&pf_path); + prefs = read_prefs(&gpf_open_errno, &gpf_path, &pf_open_errno, &pf_path); + if (gpf_path != NULL) { + fprintf(stderr, "Can't open global preferences file \"%s\": %s.\n", pf_path, + strerror(gpf_open_errno)); + } if (pf_path != NULL) { - fprintf(stderr, "Can't open preferences file \"%s\": %s.\n", pf_path, - strerror(errno)); + fprintf(stderr, "Can't open your preferences file \"%s\": %s.\n", pf_path, + strerror(pf_open_errno)); } /* Initialize the capture file struct */ @@ -266,7 +280,7 @@ main(int argc, char *argv[]) ); /* Now get our args */ - while ((opt = getopt(argc, argv, "c:Df:F:hi:nr:R:s:t:vw:Vx")) != EOF) { + while ((opt = getopt(argc, argv, "c:Df:F:hi:no:r:R:s:t:vw:Vx")) != EOF) { switch (opt) { case 'c': /* Capture xxx packets */ #ifdef HAVE_LIBPCAP @@ -311,6 +325,21 @@ main(int argc, char *argv[]) case 'n': /* No name resolution */ g_resolving_actif = 0; break; + case 'o': /* Override preference from command line */ + switch (prefs_set_pref(optarg)) { + + case PREFS_SET_SYNTAX_ERR: + fprintf(stderr, "tethereal: Invalid -o flag \"%s\"\n", optarg); + exit(1); + break; + + case PREFS_SET_NO_SUCH_PREF: + fprintf(stderr, "tethereal: -o flag \"%s\" specifies unknown preference\n", + optarg); + exit(1); + break; + } + break; case 'r': /* Read capture file xxx */ cf_name = g_strdup(optarg); break; @@ -407,8 +436,6 @@ main(int argc, char *argv[]) else if (cfile.snap < MIN_PACKET_SIZE) cfile.snap = MIN_PACKET_SIZE; - dissect_init(); /* Init anything that needs initializing */ - if (rfilter != NULL) { if (dfilter_compile(rfilter, &rfcode) != 0) { fprintf(stderr, "tethereal: %s\n", dfilter_error_msg); |