add Merge functionality to Ethereal in an experimental state.
Copied and sligthly modified merge.c from mergecap.c (needs a lot of code cleanup, though) svn path=/trunk/; revision=11171
This commit is contained in:
parent
a93978dc30
commit
bd3aea8d04
|
@ -3,7 +3,7 @@
|
|||
# a) common to both files and
|
||||
# b) portable between both files
|
||||
#
|
||||
# $Id: Makefile.common,v 1.46 2004/05/25 10:09:03 sahlberg Exp $
|
||||
# $Id: Makefile.common,v 1.47 2004/06/17 21:53:24 ulfl Exp $
|
||||
#
|
||||
# Ethereal - Network traffic analyzer
|
||||
# By Gerald Combs <gerald@ethereal.com>
|
||||
|
@ -336,6 +336,7 @@ ethereal_SOURCES = \
|
|||
file.c \
|
||||
filters.c \
|
||||
g711.c \
|
||||
merge.c \
|
||||
proto_hier_stats.c \
|
||||
summary.c
|
||||
|
||||
|
|
327
gtk/file_dlg.c
327
gtk/file_dlg.c
|
@ -1,7 +1,7 @@
|
|||
/* file_dlg.c
|
||||
* Dialog boxes for handling files
|
||||
*
|
||||
* $Id: file_dlg.c,v 1.108 2004/06/01 17:33:36 ulfl Exp $
|
||||
* $Id: file_dlg.c,v 1.109 2004/06/17 21:53:25 ulfl Exp $
|
||||
*
|
||||
* Ethereal - Network traffic analyzer
|
||||
* By Gerald Combs <gerald@ethereal.com>
|
||||
|
@ -58,6 +58,8 @@
|
|||
|
||||
static void file_open_ok_cb(GtkWidget *w, gpointer fs);
|
||||
static void file_open_destroy_cb(GtkWidget *win, gpointer user_data);
|
||||
static void file_merge_ok_cb(GtkWidget *w, gpointer fs);
|
||||
static void file_merge_destroy_cb(GtkWidget *win, gpointer user_data);
|
||||
static void select_file_type_cb(GtkWidget *w, gpointer data);
|
||||
static void file_save_as_ok_cb(GtkWidget *w, gpointer fs);
|
||||
static void file_save_as_destroy_cb(GtkWidget *win, gpointer user_data);
|
||||
|
@ -70,6 +72,10 @@ static void file_color_export_destroy_cb(GtkWidget *win, gpointer user_data);
|
|||
#define E_FILE_N_RESOLVE_KEY "file_dlg_network_resolve_key"
|
||||
#define E_FILE_T_RESOLVE_KEY "file_dlg_transport_resolve_key"
|
||||
|
||||
#define E_MERGE_PREPEND_KEY "merge_dlg_prepend_key"
|
||||
#define E_MERGE_CHRONO_KEY "merge_dlg_chrono_key"
|
||||
#define E_MERGE_APPEND_KEY "merge_dlg_append_key"
|
||||
|
||||
#define ARGUMENT_CL "argument_cl"
|
||||
|
||||
/*
|
||||
|
@ -244,7 +250,7 @@ file_open_cmd(GtkWidget *w)
|
|||
#endif
|
||||
}
|
||||
|
||||
void file_open_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
|
||||
static void file_open_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
|
||||
{
|
||||
switch(btn) {
|
||||
case(ESD_BTN_YES):
|
||||
|
@ -373,6 +379,320 @@ file_open_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
|
|||
file_open_w = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep a static pointer to the current "Marge Capture File" window, if
|
||||
* any, so that if somebody tries to do "File:Merge" while there's already
|
||||
* an "Merge Capture File" window up, we just pop up the existing one,
|
||||
* rather than creating a new one.
|
||||
*/
|
||||
static GtkWidget *file_merge_w;
|
||||
|
||||
/* Merge existing with another file */
|
||||
void
|
||||
file_merge_cmd(GtkWidget *w)
|
||||
{
|
||||
GtkWidget *main_vb, *filter_hbox, *filter_bt, *filter_te,
|
||||
*prepend_rb, *chrono_rb, *append_rb;
|
||||
#if GTK_MAJOR_VERSION < 2
|
||||
GtkAccelGroup *accel_group;
|
||||
#endif
|
||||
/* No Apply button, and "OK" just sets our text widget, it doesn't
|
||||
activate it (i.e., it doesn't cause us to try to open the file). */
|
||||
static construct_args_t args = {
|
||||
"Ethereal: Read Filter",
|
||||
FALSE,
|
||||
FALSE
|
||||
};
|
||||
|
||||
if (file_merge_w != NULL) {
|
||||
/* There's already an "Merge Capture File" dialog box; reactivate it. */
|
||||
reactivate_window(file_merge_w);
|
||||
return;
|
||||
}
|
||||
|
||||
file_merge_w = file_selection_new("Ethereal: Merge with Capture File",
|
||||
FILE_SELECTION_OPEN);
|
||||
/* window is already shown here, gtk_window_set_default_size() will not work */
|
||||
WIDGET_SET_SIZE(file_merge_w, DEF_WIDTH, DEF_HEIGHT);
|
||||
|
||||
#if GTK_MAJOR_VERSION < 2
|
||||
/* Accelerator group for the accelerators (or, as they're called in
|
||||
Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
|
||||
Ctrl+<key> is an accelerator). */
|
||||
accel_group = gtk_accel_group_new();
|
||||
gtk_window_add_accel_group(GTK_WINDOW(file_merge_w), accel_group);
|
||||
#endif
|
||||
|
||||
switch (prefs.gui_fileopen_style) {
|
||||
|
||||
case FO_STYLE_LAST_OPENED:
|
||||
/* The user has specified that we should start out in the last directory
|
||||
we looked in. If we've already opened a file, use its containing
|
||||
directory, if we could determine it, as the directory, otherwise
|
||||
use the "last opened" directory saved in the preferences file if
|
||||
there was one. */
|
||||
/* This is now the default behaviour in file_selection_new() */
|
||||
break;
|
||||
|
||||
case FO_STYLE_SPECIFIED:
|
||||
/* The user has specified that we should always start out in a
|
||||
specified directory; if they've specified that directory,
|
||||
start out by showing the files in that dir. */
|
||||
if (prefs.gui_fileopen_dir[0] != '\0')
|
||||
file_selection_set_current_folder(file_merge_w, prefs.gui_fileopen_dir);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Container for each row of widgets */
|
||||
main_vb = gtk_vbox_new(FALSE, 3);
|
||||
gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
|
||||
file_selection_set_extra_widget(file_merge_w, main_vb);
|
||||
gtk_widget_show(main_vb);
|
||||
|
||||
filter_hbox = gtk_hbox_new(FALSE, 1);
|
||||
gtk_container_border_width(GTK_CONTAINER(filter_hbox), 0);
|
||||
gtk_box_pack_start(GTK_BOX(main_vb), filter_hbox, FALSE, FALSE, 0);
|
||||
gtk_widget_show(filter_hbox);
|
||||
|
||||
filter_bt = BUTTON_NEW_FROM_STOCK(ETHEREAL_STOCK_DISPLAY_FILTER_ENTRY);
|
||||
SIGNAL_CONNECT(filter_bt, "clicked", display_filter_construct_cb, &args);
|
||||
SIGNAL_CONNECT(filter_bt, "destroy", filter_button_destroy_cb, NULL);
|
||||
gtk_box_pack_start(GTK_BOX(filter_hbox), filter_bt, FALSE, TRUE, 0);
|
||||
gtk_widget_show(filter_bt);
|
||||
|
||||
filter_te = gtk_entry_new();
|
||||
OBJECT_SET_DATA(filter_bt, E_FILT_TE_PTR_KEY, filter_te);
|
||||
gtk_box_pack_start(GTK_BOX(filter_hbox), filter_te, TRUE, TRUE, 3);
|
||||
SIGNAL_CONNECT(filter_te, "changed", filter_te_syntax_check_cb, NULL);
|
||||
gtk_widget_show(filter_te);
|
||||
|
||||
#if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
|
||||
OBJECT_SET_DATA(file_merge_w, E_RFILTER_TE_KEY, filter_te);
|
||||
#else
|
||||
OBJECT_SET_DATA(GTK_FILE_SELECTION(file_merge_w)->ok_button,
|
||||
E_RFILTER_TE_KEY, filter_te);
|
||||
#endif
|
||||
|
||||
prepend_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(NULL, "Prepend packets to existing file", accel_group);
|
||||
gtk_box_pack_start(GTK_BOX(main_vb), prepend_rb, FALSE, FALSE, 0);
|
||||
#if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
|
||||
OBJECT_SET_DATA(file_merge_w,
|
||||
E_MERGE_PREPEND_KEY, prepend_rb);
|
||||
#else
|
||||
OBJECT_SET_DATA(GTK_FILE_SELECTION(file_merge_w)->ok_button,
|
||||
E_MERGE_PREPEND_KEY, prepend_rb);
|
||||
#endif
|
||||
gtk_widget_show(prepend_rb);
|
||||
|
||||
chrono_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(prepend_rb, "Sort packets chronological", accel_group);
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chrono_rb), TRUE);
|
||||
gtk_box_pack_start(GTK_BOX(main_vb), chrono_rb, FALSE, FALSE, 0);
|
||||
gtk_widget_show(chrono_rb);
|
||||
#if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
|
||||
OBJECT_SET_DATA(file_merge_w, E_MERGE_CHRONO_KEY, chrono_rb);
|
||||
#else
|
||||
OBJECT_SET_DATA(GTK_FILE_SELECTION(file_merge_w)->ok_button,
|
||||
E_MERGE_CHRONO_KEY, chrono_rb);
|
||||
#endif
|
||||
|
||||
append_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(prepend_rb, "Append packets to existing file", accel_group);
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(append_rb),
|
||||
g_resolv_flags & RESOLV_TRANSPORT);
|
||||
gtk_box_pack_start(GTK_BOX(main_vb), append_rb, FALSE, FALSE, 0);
|
||||
gtk_widget_show(append_rb);
|
||||
#if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
|
||||
OBJECT_SET_DATA(file_merge_w, E_MERGE_APPEND_KEY, append_rb);
|
||||
#else
|
||||
OBJECT_SET_DATA(GTK_FILE_SELECTION(file_merge_w)->ok_button,
|
||||
E_MERGE_APPEND_KEY, append_rb);
|
||||
#endif
|
||||
|
||||
|
||||
SIGNAL_CONNECT(file_merge_w, "destroy", file_merge_destroy_cb, NULL);
|
||||
|
||||
#if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
|
||||
OBJECT_SET_DATA(file_merge_w, E_DFILTER_TE_KEY,
|
||||
OBJECT_GET_DATA(w, E_DFILTER_TE_KEY));
|
||||
if (gtk_dialog_run(GTK_DIALOG(file_merge_w)) == GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
file_merge_ok_cb(file_merge_w, file_merge_w);
|
||||
}
|
||||
else window_destroy(file_merge_w);
|
||||
#else
|
||||
/* Connect the ok_button to file_merge_ok_cb function and pass along a
|
||||
pointer to the file selection box widget */
|
||||
SIGNAL_CONNECT(GTK_FILE_SELECTION(file_merge_w)->ok_button, "clicked",
|
||||
file_merge_ok_cb, file_merge_w);
|
||||
|
||||
OBJECT_SET_DATA(GTK_FILE_SELECTION(file_merge_w)->ok_button,
|
||||
E_DFILTER_TE_KEY, OBJECT_GET_DATA(w, E_DFILTER_TE_KEY));
|
||||
|
||||
/* Connect the cancel_button to destroy the widget */
|
||||
window_set_cancel_button(file_merge_w,
|
||||
GTK_FILE_SELECTION(file_merge_w)->cancel_button, window_cancel_button_cb);
|
||||
|
||||
SIGNAL_CONNECT(file_merge_w, "delete_event", window_delete_event_cb, NULL);
|
||||
|
||||
gtk_widget_show(file_merge_w);
|
||||
window_present(file_merge_w);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void file_merge_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
|
||||
{
|
||||
switch(btn) {
|
||||
case(ESD_BTN_YES):
|
||||
/* save file first */
|
||||
file_save_as_cmd(after_save_merge_dialog, data);
|
||||
break;
|
||||
case(ESD_BTN_NO):
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
file_merge_cmd_cb(GtkWidget *widget, gpointer data _U_) {
|
||||
gpointer dialog;
|
||||
|
||||
if((cfile.state != FILE_CLOSED) && !cfile.user_saved) {
|
||||
/* user didn't saved his current file, ask him */
|
||||
dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_YES_NO,
|
||||
PRIMARY_TEXT_START "Save the capture file before merging to another one?" PRIMARY_TEXT_END "\n\n"
|
||||
"A temporary capture file cannot be merged.");
|
||||
simple_dialog_set_cb(dialog, file_merge_answered_cb, widget);
|
||||
} else {
|
||||
/* unchanged file, just start to merge */
|
||||
file_merge_cmd(widget);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern gboolean
|
||||
merge_two_files(char *out_filename, char *in_file0, char *in_file1, gboolean append);
|
||||
|
||||
static void
|
||||
file_merge_ok_cb(GtkWidget *w, gpointer fs) {
|
||||
gchar *cf_name, *rfilter, *s;
|
||||
gchar *cf_current_name, *cf_merged_name;
|
||||
GtkWidget *filter_te, *rb;
|
||||
dfilter_t *rfcode = NULL;
|
||||
int err;
|
||||
gboolean merge_ok;
|
||||
|
||||
#if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
|
||||
cf_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
|
||||
#else
|
||||
cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
|
||||
#endif
|
||||
filter_te = OBJECT_GET_DATA(w, E_RFILTER_TE_KEY);
|
||||
rfilter = (gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
|
||||
if (!dfilter_compile(rfilter, &rfcode)) {
|
||||
bad_dfilter_alert_box(rfilter);
|
||||
g_free(cf_name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Perhaps the user specified a directory instead of a file.
|
||||
Check whether they did. */
|
||||
if (test_for_directory(cf_name) == EISDIR) {
|
||||
/* It's a directory - set the file selection box to display that
|
||||
directory, don't try to open the directory as a capture file. */
|
||||
set_last_open_dir(cf_name);
|
||||
g_free(cf_name);
|
||||
file_selection_set_current_folder(fs, get_last_open_dir());
|
||||
return;
|
||||
}
|
||||
|
||||
cf_current_name = strdup(cfile.filename);
|
||||
/*XXX should use temp file stuff in util routines */
|
||||
cf_merged_name = tmpnam(NULL);
|
||||
|
||||
/* merge or append the files */
|
||||
rb = OBJECT_GET_DATA(w, E_MERGE_CHRONO_KEY);
|
||||
if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rb))) {
|
||||
/* chonological order */
|
||||
merge_ok = merge_two_files(cf_merged_name, cf_current_name, cf_name, FALSE);
|
||||
} else {
|
||||
rb = OBJECT_GET_DATA(w, E_MERGE_PREPEND_KEY);
|
||||
if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rb))) {
|
||||
/* prepend file */
|
||||
merge_ok = merge_two_files(cf_merged_name, cf_current_name, cf_name, TRUE);
|
||||
} else {
|
||||
/* append file */
|
||||
merge_ok = merge_two_files(cf_merged_name, cf_name, cf_current_name, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
cf_close(&cfile);
|
||||
g_free(cf_current_name);
|
||||
cf_name = cf_merged_name;
|
||||
|
||||
if(!merge_ok) {
|
||||
if (rfcode != NULL)
|
||||
dfilter_free(rfcode);
|
||||
g_free(cf_name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try to open the capture file. */
|
||||
if ((err = cf_open(cf_name, FALSE, &cfile)) != 0) {
|
||||
/* We couldn't open it; don't dismiss the open dialog box,
|
||||
just leave it around so that the user can, after they
|
||||
dismiss the alert box popped up for the open error,
|
||||
try again. */
|
||||
if (rfcode != NULL)
|
||||
dfilter_free(rfcode);
|
||||
g_free(cf_name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Attach the new read filter to "cf" ("cf_open()" succeeded, so
|
||||
it closed the previous capture file, and thus destroyed any
|
||||
previous read filter attached to "cf"). */
|
||||
cfile.rfcode = rfcode;
|
||||
cfile.is_tempfile = TRUE;
|
||||
|
||||
/* We've crossed the Rubicon; get rid of the file selection box. */
|
||||
window_destroy(GTK_WIDGET (fs));
|
||||
|
||||
switch (cf_read(&cfile)) {
|
||||
|
||||
case READ_SUCCESS:
|
||||
case READ_ERROR:
|
||||
/* Just because we got an error, that doesn't mean we were unable
|
||||
to read any of the file; we handle what we could get from the
|
||||
file. */
|
||||
break;
|
||||
|
||||
case READ_ABORTED:
|
||||
/* The user bailed out of re-reading the capture file; the
|
||||
capture file has been closed - just free the capture file name
|
||||
string and return (without changing the last containing
|
||||
directory). */
|
||||
g_free(cf_name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Save the name of the containing directory specified in the path name,
|
||||
if any; we can write over cf_name, which is a good thing, given that
|
||||
"get_dirname()" does write over its argument. */
|
||||
s = get_dirname(cf_name);
|
||||
set_last_open_dir(s);
|
||||
gtk_widget_grab_focus(packet_list);
|
||||
|
||||
g_free(cf_name);
|
||||
}
|
||||
|
||||
static void
|
||||
file_merge_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
|
||||
{
|
||||
/* Note that we no longer have a "Merge Capture File" dialog box. */
|
||||
file_merge_w = NULL;
|
||||
}
|
||||
|
||||
|
||||
void file_close_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
|
||||
{
|
||||
switch(btn) {
|
||||
|
@ -717,6 +1037,9 @@ file_save_as_ok_cb(GtkWidget *w _U_, gpointer fs) {
|
|||
case(after_save_open_dnd_file):
|
||||
dnd_open_file_cmd(action_after_save_data_g);
|
||||
break;
|
||||
case(after_save_merge_dialog):
|
||||
file_merge_cmd(action_after_save_data_g);
|
||||
break;
|
||||
#ifdef HAVE_LIBPCAP
|
||||
case(after_save_capture_dialog):
|
||||
capture_prep();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* file_dlg.h
|
||||
* Definitions for dialog boxes for handling files
|
||||
*
|
||||
* $Id: file_dlg.h,v 1.12 2004/06/04 20:05:30 ulfl Exp $
|
||||
* $Id: file_dlg.h,v 1.13 2004/06/17 21:53:25 ulfl Exp $
|
||||
*
|
||||
* Ethereal - Network traffic analyzer
|
||||
* By Gerald Combs <gerald@ethereal.com>
|
||||
|
@ -37,6 +37,7 @@ typedef enum {
|
|||
after_save_open_dialog, /**< open the file open dialog */
|
||||
after_save_open_recent_file, /**< open the specified recent file */
|
||||
after_save_open_dnd_file, /**< open the specified file from drag and drop */
|
||||
after_save_merge_dialog, /**< open the file merge dialog */
|
||||
after_save_capture_dialog, /**< open the capture dialog */
|
||||
after_save_exit /**< exit program */
|
||||
} action_after_save_e;
|
||||
|
@ -55,6 +56,13 @@ void file_save_as_cmd(action_after_save_e action_after_save, gpointer action_aft
|
|||
*/
|
||||
void file_open_cmd_cb(GtkWidget *widget, gpointer data);
|
||||
|
||||
/** User requested the "Merge" dialog box.
|
||||
*
|
||||
* @param widget parent widget
|
||||
* @param data unused
|
||||
*/
|
||||
void file_merge_cmd_cb(GtkWidget *widget, gpointer data);
|
||||
|
||||
/** User requested the "Save" dialog box.
|
||||
*
|
||||
* @param widget parent widget
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* menu.c
|
||||
* Menu routines
|
||||
*
|
||||
* $Id: menu.c,v 1.201 2004/06/03 21:46:27 guy Exp $
|
||||
* $Id: menu.c,v 1.202 2004/06/17 21:53:26 ulfl Exp $
|
||||
*
|
||||
* Ethereal - Network traffic analyzer
|
||||
* By Gerald Combs <gerald@ethereal.com>
|
||||
|
@ -150,6 +150,7 @@ static GtkItemFactoryEntry menu_items[] =
|
|||
ITEM_FACTORY_STOCK_ENTRY("/File/_Open...", "<control>O", file_open_cmd_cb,
|
||||
0, GTK_STOCK_OPEN),
|
||||
ITEM_FACTORY_ENTRY("/File/Open _Recent", NULL, NULL, 0, "<Branch>", NULL),
|
||||
ITEM_FACTORY_ENTRY("/File/_Merge...", NULL, file_merge_cmd_cb, 0, NULL, NULL),
|
||||
ITEM_FACTORY_STOCK_ENTRY("/File/_Close", "<control>W", file_close_cmd_cb,
|
||||
0, GTK_STOCK_CLOSE),
|
||||
ITEM_FACTORY_ENTRY("/File/<separator>", NULL, NULL, 0, "<Separator>", NULL),
|
||||
|
@ -1437,6 +1438,7 @@ set_menus_for_capture_file(gboolean have_capture_file)
|
|||
{
|
||||
set_menu_sensitivity(main_menu_factory, "/File/Open...", have_capture_file);
|
||||
set_menu_sensitivity(main_menu_factory, "/File/Open Recent", have_capture_file);
|
||||
set_menu_sensitivity(main_menu_factory, "/File/Merge...", have_capture_file);
|
||||
set_menu_sensitivity(main_menu_factory, "/File/Close", have_capture_file);
|
||||
set_menu_sensitivity(main_menu_factory, "/File/Save As...",
|
||||
have_capture_file);
|
||||
|
|
|
@ -0,0 +1,458 @@
|
|||
/* Combine two dump files, either by appending or by merging by timestamp
|
||||
*
|
||||
* $Id: merge.c,v 1.1 2004/06/17 21:53:25 ulfl Exp $
|
||||
*
|
||||
* Written by Scott Renfro <scott@renfro.org> based on
|
||||
* editcap by Richard Sharpe and Guy Harris
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <glib.h>
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include "wtap.h"
|
||||
|
||||
#ifdef NEED_GETOPT_H
|
||||
#include "getopt.h"
|
||||
#endif
|
||||
|
||||
#include "cvsversion.h"
|
||||
|
||||
/*
|
||||
* Global variables
|
||||
*/
|
||||
static int verbose = 0; /* Not so verbose */
|
||||
|
||||
/*
|
||||
* Structures to manage our files
|
||||
*/
|
||||
typedef struct in_file_t {
|
||||
const char *filename;
|
||||
wtap *wth;
|
||||
int err;
|
||||
gchar *err_info;
|
||||
long data_offset;
|
||||
gboolean ok;
|
||||
} in_file_t;
|
||||
|
||||
typedef struct out_file_t {
|
||||
const char *filename;
|
||||
wtap_dumper *pdh;
|
||||
int file_type;
|
||||
int frame_type;
|
||||
unsigned int snaplen;
|
||||
int count;
|
||||
} out_file_t;
|
||||
static out_file_t out_file;
|
||||
|
||||
/*
|
||||
* Routine to write frame to output file
|
||||
*/
|
||||
static gboolean
|
||||
write_frame(guchar *user, const struct wtap_pkthdr *phdr, long offset _U_,
|
||||
union wtap_pseudo_header *pseudo_header, const guchar *buf)
|
||||
{
|
||||
wtap_dumper *pdh = (wtap_dumper*)user;
|
||||
int err;
|
||||
struct wtap_pkthdr snap_phdr;
|
||||
|
||||
if (verbose)
|
||||
printf("Record: %u\n", out_file.count++);
|
||||
|
||||
/* We simply write it, perhaps after truncating it; we could do other
|
||||
* things, like modify it. */
|
||||
if (out_file.snaplen != 0 && phdr->caplen > out_file.snaplen) {
|
||||
snap_phdr = *phdr;
|
||||
snap_phdr.caplen = out_file.snaplen;
|
||||
phdr = &snap_phdr;
|
||||
}
|
||||
|
||||
if (!wtap_dump(pdh, phdr, pseudo_header, buf, &err)) {
|
||||
fprintf(stderr, "mergecap: Error writing to %s: %s\n",
|
||||
out_file.filename, wtap_strerror(err));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
append_loop(wtap *wth, int count, wtap_handler callback, guchar* user, int *err,
|
||||
gchar **err_info)
|
||||
{
|
||||
long data_offset;
|
||||
int loop = 0;
|
||||
|
||||
/* Start by clearing error flag */
|
||||
*err = 0;
|
||||
|
||||
while ( (wtap_read(wth, err, err_info, &data_offset)) ) {
|
||||
if(!write_frame(user, wtap_phdr(wth), data_offset,
|
||||
wtap_pseudoheader(wth), wtap_buf_ptr(wth)))
|
||||
return FALSE;
|
||||
if (count > 0 && ++loop >= count)
|
||||
break;
|
||||
}
|
||||
|
||||
if (*err == 0) {
|
||||
return TRUE; /* success */
|
||||
} else {
|
||||
return FALSE; /* failure */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* routine to concatenate files
|
||||
*/
|
||||
static void
|
||||
append_files(int count, in_file_t in_files[], out_file_t *out_file)
|
||||
{
|
||||
int i;
|
||||
int err;
|
||||
gchar *err_info;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (!append_loop(in_files[i].wth, 0, write_frame,
|
||||
(guchar*)out_file->pdh, &err, &err_info)) {
|
||||
fprintf(stderr, "mergecap: Error appending %s to %s: %s\n",
|
||||
in_files[i].filename, out_file->filename, wtap_strerror(err));
|
||||
switch (err) {
|
||||
|
||||
case WTAP_ERR_UNSUPPORTED:
|
||||
case WTAP_ERR_UNSUPPORTED_ENCAP:
|
||||
case WTAP_ERR_BAD_RECORD:
|
||||
fprintf(stderr, "(%s)\n", err_info);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* returns TRUE if first argument is earlier than second
|
||||
*/
|
||||
static gboolean
|
||||
is_earlier(struct timeval *l, struct timeval *r) {
|
||||
if (l->tv_sec > r->tv_sec) { /* left is later */
|
||||
return FALSE;
|
||||
} else if (l->tv_sec < r->tv_sec) { /* left is earlier */
|
||||
return TRUE;
|
||||
} else if (l->tv_usec > r->tv_usec) { /* tv_sec equal, l.usec later */
|
||||
return FALSE;
|
||||
}
|
||||
/* either one < two or one == two
|
||||
* either way, return one
|
||||
*/
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* returns index of earliest timestamp in set of input files
|
||||
* or -1 if no valid files remain
|
||||
*/
|
||||
static int
|
||||
earliest(int count, in_file_t in_files[]) {
|
||||
int i;
|
||||
int ei = -1;
|
||||
struct timeval tv = {LONG_MAX, LONG_MAX};
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
struct wtap_pkthdr *phdr = wtap_phdr(in_files[i].wth);
|
||||
|
||||
if (in_files[i].ok && is_earlier(&(phdr->ts), &tv)) {
|
||||
tv = phdr->ts;
|
||||
ei = i;
|
||||
}
|
||||
}
|
||||
return ei;
|
||||
}
|
||||
|
||||
/*
|
||||
* actually merge the files
|
||||
*/
|
||||
static gboolean
|
||||
merge(int count, in_file_t in_files[], out_file_t *out_file)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* prime the pump (read in first frame from each file) */
|
||||
for (i = 0; i < count; i++) {
|
||||
in_files[i].ok = wtap_read(in_files[i].wth, &(in_files[i].err),
|
||||
&(in_files[i].err_info),
|
||||
&(in_files[i].data_offset));
|
||||
}
|
||||
|
||||
/* now keep writing the earliest frame until we're out of frames */
|
||||
while ( -1 != (i = earliest(count, in_files))) {
|
||||
|
||||
/* write out earliest frame, and fetch another from its
|
||||
* input file
|
||||
*/
|
||||
if(!write_frame((guchar*)out_file->pdh,
|
||||
wtap_phdr(in_files[i].wth),
|
||||
in_files[i].data_offset,
|
||||
wtap_pseudoheader(in_files[i].wth),
|
||||
wtap_buf_ptr(in_files[i].wth)))
|
||||
return FALSE;
|
||||
in_files[i].ok = wtap_read(in_files[i].wth, &(in_files[i].err),
|
||||
&(in_files[i].err_info),
|
||||
&(in_files[i].data_offset));
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Select an output frame type based on the input files
|
||||
* From Guy: If all files have the same frame type, then use that.
|
||||
* Otherwise select WTAP_ENCAP_PER_PACKET. If the selected
|
||||
* output file type doesn't support per packet frame types,
|
||||
* then the wtap_dump_open call will fail with a reasonable
|
||||
* error condition.
|
||||
*/
|
||||
static int
|
||||
select_frame_type(int count, in_file_t files[])
|
||||
{
|
||||
int i;
|
||||
int selected_frame_type;
|
||||
|
||||
selected_frame_type = wtap_file_encap(files[0].wth);
|
||||
|
||||
for (i = 1; i < count; i++) {
|
||||
int this_frame_type = wtap_file_encap(files[i].wth);
|
||||
if (selected_frame_type != this_frame_type) {
|
||||
selected_frame_type = WTAP_ENCAP_PER_PACKET;
|
||||
if (verbose) {
|
||||
fprintf(stderr, "mergecap: multiple frame encapsulation types detected\n");
|
||||
fprintf(stderr, " defaulting to WTAP_ENCAP_PER_PACKET\n");
|
||||
fprintf(stderr, " %s had type %s (%s)\n",
|
||||
files[0].filename,
|
||||
wtap_encap_string(selected_frame_type),
|
||||
wtap_encap_short_string(selected_frame_type));
|
||||
fprintf(stderr, " %s had type %s (%s)\n",
|
||||
files[i].filename,
|
||||
wtap_encap_string(this_frame_type),
|
||||
wtap_encap_short_string(this_frame_type));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
fprintf(stderr, "mergecap: selected frame_type %s (%s)\n",
|
||||
wtap_encap_string(selected_frame_type),
|
||||
wtap_encap_short_string(selected_frame_type));
|
||||
}
|
||||
|
||||
return selected_frame_type;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Close the output file
|
||||
*/
|
||||
static void
|
||||
close_outfile(out_file_t *out_file)
|
||||
{
|
||||
int err;
|
||||
if (!wtap_dump_close(out_file->pdh, &err)) {
|
||||
fprintf(stderr, "mergecap: Error closing file %s: %s\n",
|
||||
out_file->filename, wtap_strerror(err));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Open the output file
|
||||
*
|
||||
* Return FALSE if file cannot be opened (so caller can clean up)
|
||||
*/
|
||||
static gboolean
|
||||
open_outfile(out_file_t *out_file, int snapshot_len)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!out_file) {
|
||||
fprintf(stderr, "mergecap: internal error (null out_file)\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Allow output to stdout by using - */
|
||||
if (strncmp(out_file->filename, "-", 2) == 0)
|
||||
out_file->filename = "";
|
||||
|
||||
|
||||
out_file->pdh = wtap_dump_open(out_file->filename, out_file->file_type,
|
||||
out_file->frame_type, snapshot_len, &err);
|
||||
if (!out_file->pdh) {
|
||||
fprintf(stderr, "mergecap: Can't open/create %s:\n", out_file->filename);
|
||||
fprintf(stderr, " %s\n", wtap_strerror(err));
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Scan through input files and find maximum snapshot length
|
||||
*/
|
||||
static int
|
||||
max_snapshot_length(int count, in_file_t in_files[])
|
||||
{
|
||||
int i;
|
||||
int max_snapshot = 0;
|
||||
int snapshot_length;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
snapshot_length = wtap_snapshot_length(in_files[i].wth);
|
||||
if (snapshot_length == 0) {
|
||||
/* Snapshot length of input file not known. */
|
||||
snapshot_length = WTAP_MAX_PACKET_SIZE;
|
||||
}
|
||||
if (snapshot_length > max_snapshot)
|
||||
max_snapshot = snapshot_length;
|
||||
}
|
||||
return max_snapshot;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Scan through and close each input file
|
||||
*/
|
||||
static void
|
||||
close_in_files(int count, in_file_t in_files[])
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < count; i++) {
|
||||
wtap_close(in_files[i].wth);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Scan through the arguments and open the input files
|
||||
*/
|
||||
static int
|
||||
open_in_files(int in_file_count, char *argv[], in_file_t *in_files[])
|
||||
{
|
||||
int i;
|
||||
int count = 0;
|
||||
int err;
|
||||
gchar *err_info;
|
||||
in_file_t *files;
|
||||
int files_size = in_file_count * sizeof(in_file_t);
|
||||
|
||||
|
||||
files = g_malloc(files_size);
|
||||
*in_files = files;
|
||||
|
||||
for (i = 0; i < in_file_count; i++) {
|
||||
files[count].filename = argv[i];
|
||||
files[count].wth = wtap_open_offline(argv[i], &err, &err_info, FALSE);
|
||||
files[count].err = 0;
|
||||
files[count].data_offset = 0;
|
||||
files[count].ok = TRUE;
|
||||
if (!files[count].wth) {
|
||||
fprintf(stderr, "mergecap: skipping %s: %s\n", argv[i],
|
||||
wtap_strerror(err));
|
||||
switch (err) {
|
||||
|
||||
case WTAP_ERR_UNSUPPORTED:
|
||||
case WTAP_ERR_UNSUPPORTED_ENCAP:
|
||||
case WTAP_ERR_BAD_RECORD:
|
||||
fprintf(stderr, "(%s)\n", err_info);
|
||||
g_free(err_info);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (verbose) {
|
||||
fprintf(stderr, "mergecap: %s is type %s.\n", argv[i],
|
||||
wtap_file_type_string(wtap_file_type(files[count].wth)));
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (verbose)
|
||||
fprintf(stderr, "mergecap: opened %d of %d input files\n", count,
|
||||
in_file_count);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
merge_two_files(char *out_filename, char *in_file0, char *in_file1, gboolean do_append)
|
||||
{
|
||||
extern char *optarg;
|
||||
extern int optind;
|
||||
int in_file_count = 0;
|
||||
in_file_t *in_files = NULL;
|
||||
char *in_filenames[2];
|
||||
|
||||
/* initialize out_file */
|
||||
out_file.filename = out_filename;
|
||||
out_file.pdh = NULL; /* wiretap dumpfile */
|
||||
out_file.file_type = WTAP_FILE_PCAP; /* default to "libpcap" */
|
||||
out_file.frame_type = -2; /* leave type alone */
|
||||
out_file.snaplen = 0; /* no limit */
|
||||
out_file.count = 1; /* frames output */
|
||||
|
||||
/* check for proper args; at a minimum, must have an output
|
||||
* filename and one input file
|
||||
*/
|
||||
in_file_count = 2;
|
||||
|
||||
in_filenames[0] = in_file0;
|
||||
in_filenames[1] = in_file1;
|
||||
|
||||
/* open the input files */
|
||||
in_file_count = open_in_files(in_file_count, in_filenames, &in_files);
|
||||
if (in_file_count < 1) {
|
||||
fprintf(stderr, "mergecap: No valid input files\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* set the outfile frame type */
|
||||
if (out_file.frame_type == -2)
|
||||
out_file.frame_type = select_frame_type(in_file_count, in_files);
|
||||
|
||||
/* open the outfile */
|
||||
if (!open_outfile(&out_file, max_snapshot_length(in_file_count, in_files))) {
|
||||
close_in_files(in_file_count, in_files);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* do the merge (or append) */
|
||||
if (do_append)
|
||||
append_files(in_file_count, in_files, &out_file);
|
||||
else
|
||||
merge(in_file_count, in_files, &out_file);
|
||||
|
||||
close_in_files(in_file_count, in_files);
|
||||
close_outfile(&out_file);
|
||||
|
||||
free(in_files);
|
||||
|
||||
return TRUE;
|
||||
}
|
Loading…
Reference in New Issue