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:
Ulf Lamping 2004-06-17 21:53:26 +00:00
parent a93978dc30
commit bd3aea8d04
5 changed files with 797 additions and 5 deletions

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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);

458
merge.c Normal file
View 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;
}