diff options
-rw-r--r-- | Makefile.common | 3 | ||||
-rw-r--r-- | gtk/file_dlg.c | 327 | ||||
-rw-r--r-- | gtk/file_dlg.h | 10 | ||||
-rw-r--r-- | gtk/menu.c | 4 | ||||
-rw-r--r-- | merge.c | 458 |
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; +} |