diff --git a/file.c b/file.c index 9858f86ecf..d38c2d8973 100644 --- a/file.c +++ b/file.c @@ -2115,6 +2115,77 @@ cf_write_psml_packets(capture_file *cf, print_args_t *print_args) return CF_PRINT_OK; } +static gboolean +write_csv_packet(capture_file *cf, frame_data *fdata, + union wtap_pseudo_header *pseudo_header, const guint8 *pd, + void *argsp) +{ + FILE *fh = argsp; + epan_dissect_t *edt; + + /* Fill in the column information, but don't create the protocol tree. */ + edt = epan_dissect_new(FALSE, FALSE); + epan_dissect_run(edt, pseudo_header, pd, fdata, &cf->cinfo); + epan_dissect_fill_in_columns(edt); + + /* Write out the information in that tree. */ + proto_tree_write_csv(edt, fh); + + epan_dissect_free(edt); + + return !ferror(fh); +} + +cf_print_status_t +cf_write_csv_packets(capture_file *cf, print_args_t *print_args) +{ + FILE *fh; + psp_return_t ret; + + fh = fopen(print_args->file, "w"); + if (fh == NULL) + return CF_PRINT_OPEN_ERROR; /* attempt to open destination failed */ + + write_csv_preamble(fh); + if (ferror(fh)) { + fclose(fh); + return CF_PRINT_WRITE_ERROR; + } + + /* Iterate through the list of packets, printing the packets we were + told to print. */ + ret = process_specified_packets(cf, &print_args->range, "Writing CSV", + "selected packets", write_csv_packet, + fh); + + switch (ret) { + + case PSP_FINISHED: + /* Completed successfully. */ + break; + + case PSP_STOPPED: + /* Well, the user decided to abort the printing. */ + break; + + case PSP_FAILED: + /* Error while printing. */ + fclose(fh); + return CF_PRINT_WRITE_ERROR; + } + + write_csv_finale(fh); + if (ferror(fh)) { + fclose(fh); + return CF_PRINT_WRITE_ERROR; + } + + /* XXX - check for an error */ + fclose(fh); + + return CF_PRINT_OK; +} + /* Scan through the packet list and change all columns that use the "command-line-specified" time stamp format to use the current value of that format. */ diff --git a/file.h b/file.h index bb120049e7..00c35cf9b1 100644 --- a/file.h +++ b/file.h @@ -296,6 +296,15 @@ cf_print_status_t cf_write_pdml_packets(capture_file *cf, print_args_t *print_ar */ cf_print_status_t cf_write_psml_packets(capture_file *cf, print_args_t *print_args); +/** + * Print (export) the capture file into CSV format. + * + * @param cf the capture file + * @param print_args the arguments what and how to export + * @return one of cf_print_status_t + */ +cf_print_status_t cf_write_csv_packets(capture_file *cf, print_args_t *print_args); + /** * Find Packet in protocol tree. * diff --git a/gtk/main.h b/gtk/main.h index e3a0b2cfb7..21376493d8 100644 --- a/gtk/main.h +++ b/gtk/main.h @@ -180,6 +180,13 @@ extern void export_psml_cmd_cb(GtkWidget *widget, gpointer data); */ extern void export_pdml_cmd_cb(GtkWidget *widget, gpointer data); +/** User requested "Export as CSV" by menu. + * + * @param widget parent widget (unused) + * @param data unused + */ +extern void export_csv_cmd_cb(GtkWidget *widget, gpointer data); + /** User requested "Expand Tree" by menu. * * @param widget parent widget (unused) diff --git a/gtk/menu.c b/gtk/menu.c index 5ed63c6bab..61bc3a68c8 100644 --- a/gtk/menu.c +++ b/gtk/menu.c @@ -182,6 +182,9 @@ static GtkItemFactoryEntry menu_items[] = 0, NULL, NULL), ITEM_FACTORY_ENTRY("/File/Export/as \"_PostScript\" file...", NULL, export_ps_cmd_cb, 0, NULL, NULL), + ITEM_FACTORY_ENTRY("/File/Export/as \"_CSV\" (Comma Separated Values packet summary) file...", + NULL, export_csv_cmd_cb, 0, NULL, NULL), + ITEM_FACTORY_ENTRY("/File/Export/", NULL, NULL, 0, "", NULL), ITEM_FACTORY_ENTRY("/File/Export/as XML - \"P_SML\" (packet summary) file...", NULL, export_psml_cmd_cb, 0, NULL, NULL), ITEM_FACTORY_ENTRY("/File/Export/as XML - \"P_DML\" (packet details) file...", NULL, export_pdml_cmd_cb, diff --git a/gtk/print_dlg.c b/gtk/print_dlg.c index 2856bf8cc8..390997d765 100644 --- a/gtk/print_dlg.c +++ b/gtk/print_dlg.c @@ -57,7 +57,8 @@ typedef enum { output_action_export_text, /* export to plain text */ output_action_export_ps, /* export to postscript */ output_action_export_psml, /* export to packet summary markup language */ - output_action_export_pdml /* export to packet data markup language */ + output_action_export_pdml, /* export to packet data markup language */ + output_action_export_csv /* export to csv file */ } output_action_e; @@ -84,6 +85,7 @@ static void print_destroy_cb(GtkWidget *win, gpointer user_data); #define PRINT_PS_RB_KEY "printer_ps_radio_button" #define PRINT_PDML_RB_KEY "printer_pdml_radio_button" #define PRINT_PSML_RB_KEY "printer_psml_radio_button" +#define PRINT_CSV_RB_KEY "printer_csv_radio_button" #define PRINT_DEST_CB_KEY "printer_destination_check_button" #define PRINT_SUMMARY_CB_KEY "printer_summary_check_button" @@ -333,6 +335,47 @@ export_pdml_cmd_cb(GtkWidget *widget _U_, gpointer data _U_) SIGNAL_CONNECT(export_pdml_win, "destroy", print_destroy_cb, &export_pdml_win); } +/* + * Keep a static pointer to the current "Export csv" window, if any, so that if + * somebody tries to do "File:Export to CSV" while there's already a "Export csv" window + * up, we just pop up the existing one, rather than creating a new one. + */ +static GtkWidget *export_csv_win = NULL; + +static print_args_t export_csv_args; + +static gboolean export_csv_prefs_init = FALSE; + +void +export_csv_cmd_cb(GtkWidget *widget _U_, gpointer data _U_) +{ + print_args_t *args = &export_csv_args; + + if (export_csv_win != NULL) { + /* There's already a "Export csv" dialog box; reactivate it. */ + reactivate_window(export_csv_win); + return; + } + + /* get settings from preferences (and other initial values) only once */ + if(export_csv_prefs_init == FALSE) { + export_csv_prefs_init = TRUE; + args->format = PR_FMT_TEXT; /* XXX */ + args->to_file = TRUE; + args->file = g_strdup(""); + args->cmd = g_strdup(""); + args->print_summary = FALSE; + args->print_dissections = print_dissections_none; + args->print_hex = FALSE; + args->print_formfeed = FALSE; + } + + /* init the printing range */ + packet_range_init(&args->range); + + export_csv_win = open_print_dialog("Ethereal: Export as \"Comma Separated Values\" File", output_action_export_csv, args); + SIGNAL_CONNECT(export_csv_win, "destroy", print_destroy_cb, &export_csv_win); +} static void print_browse_file_cb(GtkWidget *file_bt, GtkWidget *file_te) @@ -355,7 +398,7 @@ open_print_dialog(char *title, output_action_e action, print_args_t *args) GtkWidget *main_vb; GtkWidget *printer_fr, *printer_vb, *export_format_lb; - GtkWidget *text_rb, *ps_rb, *pdml_rb, *psml_rb; + GtkWidget *text_rb, *ps_rb, *pdml_rb, *psml_rb, *csv_rb; GtkWidget *printer_tb, *dest_cb; #ifndef _WIN32 GtkWidget *cmd_lb, *cmd_te; @@ -448,6 +491,16 @@ open_print_dialog(char *title, output_action_e action, print_args_t *args) gtk_box_pack_start(GTK_BOX(printer_vb), psml_rb, FALSE, FALSE, 0); /* gtk_widget_show(psml_rb); */ + csv_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(text_rb, "_CSV", accel_group); + if (action == output_action_export_csv) + gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(csv_rb), TRUE); + gtk_tooltips_set_tip (tooltips, csv_rb, + "Print output in \"Comma Separated Values\" (CSV) format, " + "a text format compatible with OpenOffice and Excel. " + "One row for each packet, with its timestamp and size.", NULL); + gtk_box_pack_start(GTK_BOX(printer_vb), csv_rb, FALSE, FALSE, 0); + /* gtk_widget_show(csv_rb); */ + /* printer table */ #ifndef _WIN32 printer_tb = gtk_table_new(2, 3, FALSE); @@ -653,6 +706,7 @@ open_print_dialog(char *title, output_action_e action, print_args_t *args) OBJECT_SET_DATA(ok_bt, PRINT_PS_RB_KEY, ps_rb); OBJECT_SET_DATA(ok_bt, PRINT_PDML_RB_KEY, pdml_rb); OBJECT_SET_DATA(ok_bt, PRINT_PSML_RB_KEY, psml_rb); + OBJECT_SET_DATA(ok_bt, PRINT_CSV_RB_KEY, csv_rb); OBJECT_SET_DATA(ok_bt, PRINT_DEST_CB_KEY, dest_cb); #ifndef _WIN32 OBJECT_SET_DATA(ok_bt, PRINT_CMD_TE_KEY, cmd_te); @@ -766,6 +820,7 @@ print_ok_cb(GtkWidget *ok_bt, gpointer parent_w) gchar *f_name; gchar *dirname; gboolean export_as_pdml = FALSE, export_as_psml = FALSE; + gboolean export_as_csv = FALSE; #ifdef _WIN32 gboolean win_printer = FALSE; #endif @@ -823,6 +878,9 @@ print_ok_cb(GtkWidget *ok_bt, gpointer parent_w) button = (GtkWidget *)OBJECT_GET_DATA(ok_bt, PRINT_PSML_RB_KEY); if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button))) export_as_psml = TRUE; + button = (GtkWidget *)OBJECT_GET_DATA(ok_bt, PRINT_CSV_RB_KEY); + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button))) + export_as_csv = TRUE; button = (GtkWidget *)OBJECT_GET_DATA(ok_bt, PRINT_SUMMARY_CB_KEY); args->print_summary = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button)); @@ -860,6 +918,8 @@ print_ok_cb(GtkWidget *ok_bt, gpointer parent_w) status = cf_write_pdml_packets(&cfile, args); else if (export_as_psml) status = cf_write_psml_packets(&cfile, args); + else if (export_as_csv) + status = cf_write_csv_packets(&cfile, args); else { switch (args->format) { diff --git a/print.c b/print.c index 9c5f8f0f63..6c45b040d2 100644 --- a/print.c +++ b/print.c @@ -513,6 +513,37 @@ write_psml_finale(FILE *fh) fputs("\n", fh); } +void +write_csv_preamble(FILE *fh _U_) +{ + +} + +void +proto_tree_write_csv(epan_dissect_t *edt, FILE *fh) +{ + gint i; + + /* if this is the first packet, we have to write the CSV header */ + if(edt->pi.fd->num == 1) { + for(i=0; i < edt->pi.cinfo->num_cols - 1; i++) + fprintf(fh, "\"%s\", ", edt->pi.cinfo->col_title[i]); + + fprintf(fh, "\"%s\"\n", edt->pi.cinfo->col_title[i]); + } + + for(i=0; i < edt->pi.cinfo->num_cols - 1; i++) + fprintf(fh, "\"%s\", ", edt->pi.cinfo->col_data[i]); + + fprintf(fh, "\"%s\"\n", edt->pi.cinfo->col_data[i]); +} + +void +write_csv_finale(FILE *fh _U_) +{ + +} + /* * Find the data source for a specified field, and return a pointer * to the data in it. Returns NULL if the data is out of bounds. diff --git a/print.h b/print.h index a0871fb47b..1325e03be7 100644 --- a/print.h +++ b/print.h @@ -121,4 +121,8 @@ extern void write_psml_preamble(FILE *fh); extern void proto_tree_write_psml(epan_dissect_t *edt, FILE *fh); extern void write_psml_finale(FILE *fh); +extern void write_csv_preamble(FILE *fh); +extern void proto_tree_write_csv(epan_dissect_t *edt, FILE *fh); +extern void write_csv_finale(FILE *fh); + #endif /* print.h */