aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am3
-rw-r--r--gtk/main.c78
-rw-r--r--gtk/prefs_dlg.c297
-rw-r--r--packet-ip.c11
-rw-r--r--prefs-int.h79
-rw-r--r--prefs.c585
-rw-r--r--prefs.h93
-rw-r--r--tethereal.c57
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 */
diff --git a/prefs.c b/prefs.c
index 3150e6882b..e4b2e18829 100644
--- a/prefs.c
+++ b/prefs.c
@@ -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
diff --git a/prefs.h b/prefs.h
index c13bb78dbe..ab628c0eb5 100644
--- a/prefs.h
+++ b/prefs.h
@@ -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);