aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.common3
-rw-r--r--gtk/file_dlg.c327
-rw-r--r--gtk/file_dlg.h10
-rw-r--r--gtk/menu.c4
-rw-r--r--merge.c458
5 files changed, 797 insertions, 5 deletions
diff --git a/Makefile.common b/Makefile.common
index 3c2c044861..89957a6f69 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -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
diff --git a/gtk/file_dlg.c b/gtk/file_dlg.c
index 8d77a24666..5b3264390e 100644
--- a/gtk/file_dlg.c
+++ b/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();
diff --git a/gtk/file_dlg.h b/gtk/file_dlg.h
index 19594c9c3b..59923ac166 100644
--- a/gtk/file_dlg.h
+++ b/gtk/file_dlg.h
@@ -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
diff --git a/gtk/menu.c b/gtk/menu.c
index c9b7c60bdf..808e109b80 100644
--- a/gtk/menu.c
+++ b/gtk/menu.c
@@ -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);
diff --git a/merge.c b/merge.c
new file mode 100644
index 0000000000..c91b4b18a7
--- /dev/null
+++ b/merge.c
@@ -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;
+}