X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=print.c;h=6c22250ed653446b4415b188220f9eee585b21f3;hb=ce6f881c71244b0dc81a488878333647bd271520;hp=536d7e5f83c3f9cd0247e404fc2db6626a9c1d47;hpb=e97617d73bdf6dd9322815f154ddb8caa8d51e7e;p=obnox%2Fwireshark%2Fwip.git diff --git a/print.c b/print.c index 536d7e5f83..6c22250ed6 100644 --- a/print.c +++ b/print.c @@ -1,24 +1,24 @@ /* print.c * Routines for printing packet analysis trees. * - * $Id: print.c,v 1.50 2002/06/22 00:21:38 guy Exp $ + * $Id$ * * Gilbert Ramirez * - * Ethereal - Network traffic analyzer - * By Gerald Combs + * Wireshark - Network traffic analyzer + * By Gerald Combs * Copyright 1998 Gerald Combs - * + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. @@ -31,162 +31,163 @@ #include #include -#ifdef HAVE_SYS_TYPES_H -# include -#endif - #include #include #include #include +#include +#include "packet-range.h" #include "print.h" #include "ps.h" -#include "util.h" -#include "packet-data.h" +#include "version_info.h" +#include +#include +#include +#include -static void proto_tree_print_node_text(GNode *node, gpointer data); -static void proto_tree_print_node_ps(GNode *node, gpointer data); -static void ps_clean_string(unsigned char *out, const unsigned char *in, - int outbuf_size); -static void print_hex_data_text(FILE *fh, register const u_char *cp, - register u_int length, char_enc encoding); -static void print_hex_data_ps(FILE *fh, register const u_char *cp, - register u_int length, char_enc encoding); +#define PDML_VERSION "0" +#define PSML_VERSION "0" typedef struct { - int level; - FILE *fh; - GSList *src_list; - gboolean print_all_levels; - gboolean print_hex_for_data; - char_enc encoding; + int level; + print_stream_t *stream; + gboolean success; + GSList *src_list; + print_dissections_e print_dissections; + gboolean print_hex_for_data; + packet_char_enc encoding; + epan_dissect_t *edt; } print_data; -FILE *open_print_dest(int to_file, const char *dest) +typedef struct { + int level; + FILE *fh; + GSList *src_list; + epan_dissect_t *edt; +} write_pdml_data; + +typedef struct { + output_fields_t* fields; + epan_dissect_t *edt; +} write_field_data_t; + +struct _output_fields { + gboolean print_header; + gchar separator; + gchar aggregator; + GPtrArray* fields; + GHashTable* field_indicies; + emem_strbuf_t** field_values; + gchar quote; +}; + +static const gchar* get_field_hex_value(GSList* src_list, field_info *fi); +static void proto_tree_print_node(proto_node *node, gpointer data); +static void proto_tree_write_node_pdml(proto_node *node, gpointer data); +static const guint8 *get_field_data(GSList *src_list, field_info *fi); +static void write_pdml_field_hex_value(write_pdml_data *pdata, field_info *fi); +static gboolean print_hex_data_buffer(print_stream_t *stream, const guchar *cp, + guint length, packet_char_enc encoding); +static void ps_clean_string(unsigned char *out, const unsigned char *in, + int outbuf_size); +static void print_escaped_xml(FILE *fh, const char *unescaped_string); + +static void print_pdml_geninfo(proto_tree *tree, FILE *fh); + +static void proto_tree_get_node_field_values(proto_node *node, gpointer data); + +static FILE * +open_print_dest(int to_file, const char *dest) { FILE *fh; /* Open the file or command for output */ if (to_file) - fh = fopen(dest, "w"); + fh = ws_fopen(dest, "w"); else fh = popen(dest, "w"); return fh; } -void close_print_dest(int to_file, FILE *fh) +static gboolean +close_print_dest(int to_file, FILE *fh) { /* Close the file or command */ if (to_file) - fclose(fh); + return (fclose(fh) == 0); else - pclose(fh); -} - -void print_preamble(FILE *fh, gint format) -{ - if (format == PR_FMT_PS) - print_ps_preamble(fh); + return (pclose(fh) == 0); } -void print_finale(FILE *fh, gint format) -{ - if (format == PR_FMT_PS) - print_ps_finale(fh); -} +#define MAX_PS_LINE_LENGTH 256 -void proto_tree_print(print_args_t *print_args, epan_dissect_t *edt, - FILE *fh) +gboolean +proto_tree_print(print_args_t *print_args, epan_dissect_t *edt, + print_stream_t *stream) { print_data data; /* Create the output */ data.level = 0; - data.fh = fh; + data.stream = stream; + data.success = TRUE; data.src_list = edt->pi.data_src; data.encoding = edt->pi.fd->flags.encoding; - data.print_all_levels = print_args->expand_all; + data.print_dissections = print_args->print_dissections; + /* If we're printing the entire packet in hex, don't + print uninterpreted data fields in hex as well. */ data.print_hex_for_data = !print_args->print_hex; - /* If we're printing the entire packet in hex, don't - print uninterpreted data fields in hex as well. */ - - if (print_args->format == PR_FMT_TEXT) { - g_node_children_foreach((GNode*) edt->tree, G_TRAVERSE_ALL, - proto_tree_print_node_text, &data); - } else { - g_node_children_foreach((GNode*) edt->tree, G_TRAVERSE_ALL, - proto_tree_print_node_ps, &data); - } -} - -/* - * Find the data source for a specified field, and return a pointer - * to the data in it. - */ -static const guint8 * -get_field_data(GSList *src_list, field_info *fi) -{ - GSList *src_le; - data_source *src; - tvbuff_t *src_tvb; + data.edt = edt; - for (src_le = src_list; src_le != NULL; src_le = src_le->next) { - src = src_le->data; - src_tvb = src->tvb; - if (fi->ds_tvb == src_tvb) { - /* - * Found it. - */ - return tvb_get_ptr(src_tvb, fi->start, fi->length); - } - } - g_assert_not_reached(); - return NULL; /* not found */ + proto_tree_children_foreach(edt->tree, proto_tree_print_node, &data); + return data.success; } #define MAX_INDENT 160 -/* Print a tree's data, and any child nodes, in plain text */ +/* Print a tree's data, and any child nodes. */ static -void proto_tree_print_node_text(GNode *node, gpointer data) +void proto_tree_print_node(proto_node *node, gpointer data) { - field_info *fi = PITEM_FINFO(node); + field_info *fi = PNODE_FINFO(node); print_data *pdata = (print_data*) data; - int i; - int num_spaces; - char space[MAX_INDENT+1]; const guint8 *pd; gchar label_str[ITEM_LABEL_LENGTH]; gchar *label_ptr; + g_assert(fi && "dissection with an invisible proto tree?"); + /* Don't print invisible entries. */ - if (!fi->visible) + if (PROTO_ITEM_IS_HIDDEN(node)) + return; + + /* Give up if we've already gotten an error. */ + if (!pdata->success) return; /* was a free format label produced? */ - if (fi->representation) { - label_ptr = fi->representation; + if (fi->rep) { + label_ptr = fi->rep->representation; } else { /* no, make a generic label */ label_ptr = label_str; proto_item_fill_label(fi, label_str); } - - /* Prepare the tabs for printing, depending on tree level */ - num_spaces = pdata->level * 4; - if (num_spaces > MAX_INDENT) { - num_spaces = MAX_INDENT; + + if (PROTO_ITEM_IS_GENERATED(node)) { + label_ptr = g_strdup_printf("[%s]", label_ptr); } - for (i = 0; i < num_spaces; i++) { - space[i] = ' '; + + if (!print_line(pdata->stream, pdata->level, label_ptr)) { + pdata->success = FALSE; + return; } - /* The string is NUL-terminated */ - space[num_spaces] = '\0'; - /* Print the text */ - fprintf(pdata->fh, "%s%s\n", space, label_ptr); + if (PROTO_ITEM_IS_GENERATED(node)) { + g_free(label_ptr); + } /* If it's uninterpreted data, dump it (unless our caller will be printing the entire packet in hex). */ @@ -195,35 +196,608 @@ void proto_tree_print_node_text(GNode *node, gpointer data) * Find the data for this field. */ pd = get_field_data(pdata->src_list, fi); - print_hex_data_text(pdata->fh, pd, fi->length, pdata->encoding); + if (pd) { + if (!print_hex_data_buffer(pdata->stream, pd, + fi->length, pdata->encoding)) { + pdata->success = FALSE; + return; + } + } } /* If we're printing all levels, or if this node is one with a subtree and its subtree is expanded, recurse into the subtree, if it exists. */ g_assert(fi->tree_type >= -1 && fi->tree_type < num_tree_types); - if (pdata->print_all_levels || - (fi->tree_type >= 0 && tree_is_expanded[fi->tree_type])) { - if (g_node_n_children(node) > 0) { + if (pdata->print_dissections == print_dissections_expanded || + (pdata->print_dissections == print_dissections_as_displayed && + fi->tree_type >= 0 && tree_is_expanded[fi->tree_type])) { + if (node->first_child != NULL) { pdata->level++; - g_node_children_foreach(node, G_TRAVERSE_ALL, - proto_tree_print_node_text, pdata); + proto_tree_children_foreach(node, + proto_tree_print_node, pdata); pdata->level--; + if (!pdata->success) + return; + } + } +} + +void +write_pdml_preamble(FILE *fh) +{ + fputs("\n", fh); + fputs("\n", PACKAGE, VERSION); +} + +void +proto_tree_write_pdml(epan_dissect_t *edt, FILE *fh) +{ + write_pdml_data data; + + /* Create the output */ + data.level = 0; + data.fh = fh; + data.src_list = edt->pi.data_src; + data.edt = edt; + + /* We shouldn't be called with a NULL pointer here because we've + * created a visible protocol tree */ + g_assert(data.src_list); + + fprintf(fh, "\n"); + + /* Print a "geninfo" protocol as required by PDML */ + print_pdml_geninfo(edt->tree, fh); + + proto_tree_children_foreach(edt->tree, proto_tree_write_node_pdml, + &data); + + fprintf(fh, "\n\n"); +} + +/* Write out a tree's data, and any child nodes, as PDML */ +static void +proto_tree_write_node_pdml(proto_node *node, gpointer data) +{ + field_info *fi = PNODE_FINFO(node); + write_pdml_data *pdata = (write_pdml_data*) data; + const gchar *label_ptr; + gchar label_str[ITEM_LABEL_LENGTH]; + char *dfilter_string; + size_t chop_len; + int i; + gboolean wrap_in_fake_protocol; + + g_assert(fi && "dissection with an invisible proto tree?"); + + /* Will wrap up top-level field items inside a fake protocol wrapper to + preserve the PDML schema */ + wrap_in_fake_protocol = + (((fi->hfinfo->type != FT_PROTOCOL) || + (fi->hfinfo->id == proto_data)) && + (pdata->level == 0)); + + /* Indent to the correct level */ + for (i = -1; i < pdata->level; i++) { + fputs(" ", pdata->fh); + } + + if (wrap_in_fake_protocol) { + /* Open fake protocol wrapper */ + fputs("\n", pdata->fh); + + /* Indent to increased level before writint out field */ + pdata->level++; + for (i = -1; i < pdata->level; i++) { + fputs(" ", pdata->fh); + } + } + + /* Text label. It's printed as a field with no name. */ + if (fi->hfinfo->id == hf_text_only) { + /* Get the text */ + if (fi->rep) { + label_ptr = fi->rep->representation; + } + else { + label_ptr = ""; + } + + /* Show empty name since it is a required field */ + fputs("fh); + fputs("\" show=\"", pdata->fh); + print_escaped_xml(pdata->fh, label_ptr); + + fprintf(pdata->fh, "\" size=\"%d", fi->length); + fprintf(pdata->fh, "\" pos=\"%d", fi->start); + + fputs("\" value=\"", pdata->fh); + write_pdml_field_hex_value(pdata, fi); + + if (node->first_child != NULL) { + fputs("\">\n", pdata->fh); + } + else { + fputs("\"/>\n", pdata->fh); + } + } + + /* Uninterpreted data, i.e., the "Data" protocol, is + * printed as a field instead of a protocol. */ + else if (fi->hfinfo->id == proto_data) { + + /* Write out field with data */ + fputs("fh); + write_pdml_field_hex_value(pdata, fi); + fputs("\"/>\n", pdata->fh); + } + /* Normal protocols and fields */ + else { + if (fi->hfinfo->type == FT_PROTOCOL) { + fputs("fh); + } + else { + fputs("fh); + } + print_escaped_xml(pdata->fh, fi->hfinfo->abbrev); + +#if 0 + /* PDML spec, see: + * http://www.nbee.org/doku.php?id=netpdl:pdml_specification + * + * the show fields contains things in 'human readable' format + * showname: contains only the name of the field + * show: contains only the data of the field + * showdtl: contains additional details of the field data + * showmap: contains mappings of the field data (e.g. the hostname to an IP address) + * + * XXX - the showname shouldn't contain the field data itself + * (like it's contained in the fi->rep->representation). + * Unfortunately, we don't have the field data representation for + * all fields, so this isn't currently possible */ + fputs("\" showname=\"", pdata->fh); + print_escaped_xml(pdata->fh, fi->hfinfo->name); +#endif + + if (fi->rep) { + fputs("\" showname=\"", pdata->fh); + print_escaped_xml(pdata->fh, fi->rep->representation); + } + else { + label_ptr = label_str; + proto_item_fill_label(fi, label_str); + fputs("\" showname=\"", pdata->fh); + print_escaped_xml(pdata->fh, label_ptr); + } + + if (PROTO_ITEM_IS_HIDDEN(node)) + fprintf(pdata->fh, "\" hide=\"yes"); + + fprintf(pdata->fh, "\" size=\"%d", fi->length); + fprintf(pdata->fh, "\" pos=\"%d", fi->start); +/* fprintf(pdata->fh, "\" id=\"%d", fi->hfinfo->id);*/ + + /* show, value, and unmaskedvalue attributes */ + switch (fi->hfinfo->type) + { + case FT_PROTOCOL: + break; + case FT_NONE: + fputs("\" show=\"\" value=\"", pdata->fh); + break; + default: + /* XXX - this is a hack until we can just call + * fvalue_to_string_repr() for *all* FT_* types. */ + dfilter_string = proto_construct_match_selected_string(fi, + pdata->edt); + if (dfilter_string != NULL) { + chop_len = strlen(fi->hfinfo->abbrev) + 4; /* for " == " */ + + /* XXX - Remove double-quotes. Again, once we + * can call fvalue_to_string_repr(), we can + * ask it not to produce the version for + * display-filters, and thus, no + * double-quotes. */ + if (dfilter_string[strlen(dfilter_string)-1] == '"') { + dfilter_string[strlen(dfilter_string)-1] = '\0'; + chop_len++; + } + + fputs("\" show=\"", pdata->fh); + print_escaped_xml(pdata->fh, &dfilter_string[chop_len]); + } + + /* + * XXX - should we omit "value" for any fields? + * What should we do for fields whose length is 0? + * They might come from a pseudo-header or from + * the capture header (e.g., time stamps), or + * they might be generated fields. + */ + if (fi->length > 0) { + fputs("\" value=\"", pdata->fh); + + if (fi->hfinfo->bitmask!=0) { + fprintf(pdata->fh, "%X", fvalue_get_uinteger(&fi->value)); + fputs("\" unmaskedvalue=\"", pdata->fh); + write_pdml_field_hex_value(pdata, fi); + } + else { + write_pdml_field_hex_value(pdata, fi); + } + } + } + + if (node->first_child != NULL) { + fputs("\">\n", pdata->fh); + } + else if (fi->hfinfo->id == proto_data) { + fputs("\">\n", pdata->fh); + } + else { + fputs("\"/>\n", pdata->fh); + } + } + + /* We always print all levels for PDML. Recurse here. */ + if (node->first_child != NULL) { + pdata->level++; + proto_tree_children_foreach(node, + proto_tree_write_node_pdml, pdata); + pdata->level--; + } + + /* Take back the extra level we added for fake wrapper protocol */ + if (wrap_in_fake_protocol) { + pdata->level--; + } + + if (node->first_child != NULL) { + /* Indent to correct level */ + for (i = -1; i < pdata->level; i++) { + fputs(" ", pdata->fh); + } + /* Close off current element */ + if (fi->hfinfo->id != proto_data) { /* Data protocol uses simple tags */ + if (fi->hfinfo->type == FT_PROTOCOL) { + fputs("\n", pdata->fh); + } + else { + fputs("\n", pdata->fh); + } + } + } + + /* Close off fake wrapper protocol */ + if (wrap_in_fake_protocol) { + fputs("\n", pdata->fh); + } +} + +/* Print info for a 'geninfo' pseudo-protocol. This is required by + * the PDML spec. The information is contained in Wireshark's 'frame' protocol, + * but we produce a 'geninfo' protocol in the PDML to conform to spec. + * The 'frame' protocol follows the 'geninfo' protocol in the PDML. */ +static void +print_pdml_geninfo(proto_tree *tree, FILE *fh) +{ + guint32 num, len, caplen; + nstime_t *timestamp; + GPtrArray *finfo_array; + field_info *frame_finfo; + + /* Get frame protocol's finfo. */ + finfo_array = proto_find_finfo(tree, proto_frame); + if (g_ptr_array_len(finfo_array) < 1) { + return; + } + frame_finfo = (field_info *)finfo_array->pdata[0]; + g_ptr_array_free(finfo_array, TRUE); + + /* frame.number --> geninfo.num */ + finfo_array = proto_find_finfo(tree, hf_frame_number); + if (g_ptr_array_len(finfo_array) < 1) { + return; + } + num = fvalue_get_uinteger(&((field_info*)finfo_array->pdata[0])->value); + g_ptr_array_free(finfo_array, TRUE); + + /* frame.frame_len --> geninfo.len */ + finfo_array = proto_find_finfo(tree, hf_frame_len); + if (g_ptr_array_len(finfo_array) < 1) { + return; + } + len = fvalue_get_uinteger(&((field_info*)finfo_array->pdata[0])->value); + g_ptr_array_free(finfo_array, TRUE); + + /* frame.cap_len --> geninfo.caplen */ + finfo_array = proto_find_finfo(tree, hf_frame_capture_len); + if (g_ptr_array_len(finfo_array) < 1) { + return; + } + caplen = fvalue_get_uinteger(&((field_info*)finfo_array->pdata[0])->value); + g_ptr_array_free(finfo_array, TRUE); + + /* frame.time --> geninfo.timestamp */ + finfo_array = proto_find_finfo(tree, hf_frame_arrival_time); + if (g_ptr_array_len(finfo_array) < 1) { + return; + } + timestamp = (nstime_t *)fvalue_get(&((field_info*)finfo_array->pdata[0])->value); + g_ptr_array_free(finfo_array, TRUE); + + /* Print geninfo start */ + fprintf(fh, +" \n", + frame_finfo->length); + + /* Print geninfo.num */ + fprintf(fh, +" \n", + num, num, frame_finfo->length); + + /* Print geninfo.len */ + fprintf(fh, +" \n", + len, len, frame_finfo->length); + + /* Print geninfo.caplen */ + fprintf(fh, +" \n", + caplen, caplen, frame_finfo->length); + + /* Print geninfo.timestamp */ + fprintf(fh, +" \n", + abs_time_to_str(timestamp, ABSOLUTE_TIME_LOCAL, TRUE), (int) timestamp->secs, timestamp->nsecs, frame_finfo->length); + + /* Print geninfo end */ + fprintf(fh, +" \n"); +} + +void +write_pdml_finale(FILE *fh) +{ + fputs("\n", fh); +} + +void +write_psml_preamble(FILE *fh) +{ + fputs("\n", fh); + fputs("\n", PACKAGE, VERSION); +} + +void +proto_tree_write_psml(epan_dissect_t *edt, FILE *fh) +{ + gint i; + static gboolean structure_written = FALSE; + + /* if this is the first packet, we have to create the PSML structure output */ + if(!structure_written) { + fprintf(fh, "\n"); + + for(i=0; i < edt->pi.cinfo->num_cols; i++) { + fprintf(fh, "
"); + print_escaped_xml(fh, edt->pi.cinfo->col_title[i]); + fprintf(fh, "
\n"); + } + + fprintf(fh, "
\n\n"); + + structure_written = TRUE; + } + + fprintf(fh, "\n"); + + for(i=0; i < edt->pi.cinfo->num_cols; i++) { + fprintf(fh, "
"); + print_escaped_xml(fh, edt->pi.cinfo->col_data[i]); + fprintf(fh, "
\n"); + } + + fprintf(fh, "
\n\n"); +} + +void +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_) +{ + +} + +void +write_carrays_preamble(FILE *fh _U_) +{ + +} + +void +proto_tree_write_carrays(const guint8 *pd, guint32 len, guint32 num, FILE *fh) +{ + guint32 i = 0; + + if (!len) + return; + + fprintf(fh, "char pkt%u[] = {\n", num); + + for (i = 0; i < len; i++) { + + fprintf(fh, "0x%02x", *(pd + i)); + + if (i == (len - 1)) { + fprintf(fh, " };\n\n"); + break; + } + + if (!((i + 1) % 8)) { + fprintf(fh, ", \n"); + } else { + fprintf(fh, ", "); + } + } +} + +void +write_carrays_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. + */ +static const guint8 * +get_field_data(GSList *src_list, field_info *fi) +{ + GSList *src_le; + data_source *src; + tvbuff_t *src_tvb; + gint length, tvbuff_length; + + for (src_le = src_list; src_le != NULL; src_le = src_le->next) { + src = (data_source *)src_le->data; + src_tvb = src->tvb; + if (fi->ds_tvb == src_tvb) { + /* + * Found it. + * + * XXX - a field can have a length that runs past + * the end of the tvbuff. Ideally, that should + * be fixed when adding an item to the protocol + * tree, but checking the length when doing + * that could be expensive. Until we fix that, + * we'll do the check here. + */ + tvbuff_length = tvb_length_remaining(src_tvb, + fi->start); + if (tvbuff_length < 0) { + return NULL; + } + length = fi->length; + if (length > tvbuff_length) + length = tvbuff_length; + return tvb_get_ptr(src_tvb, fi->start, length); + } + } + g_assert_not_reached(); + return NULL; /* not found */ +} + +/* Print a string, escaping out certain characters that need to + * escaped out for XML. */ +static void +print_escaped_xml(FILE *fh, const char *unescaped_string) +{ + const char *p; + char temp_str[8]; + + for (p = unescaped_string; *p != '\0'; p++) { + switch (*p) { + case '&': + fputs("&", fh); + break; + case '<': + fputs("<", fh); + break; + case '>': + fputs(">", fh); + break; + case '"': + fputs(""", fh); + break; + case '\'': + fputs("'", fh); + break; + default: + if (g_ascii_isprint(*p)) + fputc(*p, fh); + else { + g_snprintf(temp_str, sizeof(temp_str), "\\x%x", (guint8)*p); + fputs(temp_str, fh); + } + } + } +} + +static void +write_pdml_field_hex_value(write_pdml_data *pdata, field_info *fi) +{ + int i; + const guint8 *pd; + + if (!fi->ds_tvb) + return; + + if (fi->length > tvb_length_remaining(fi->ds_tvb, fi->start)) { + fprintf(pdata->fh, "field length invalid!"); + return; + } + + /* Find the data for this field. */ + pd = get_field_data(pdata->src_list, fi); + + if (pd) { + /* Print a simple hex dump */ + for (i = 0 ; i < fi->length; i++) { + fprintf(pdata->fh, "%02x", pd[i]); } } } -void print_hex_data(FILE *fh, gint format, epan_dissect_t *edt) +gboolean +print_hex_data(print_stream_t *stream, epan_dissect_t *edt) { gboolean multiple_sources; GSList *src_le; data_source *src; tvbuff_t *tvb; - char *name; + const char *name; char *line; - const u_char *cp; + const guchar *cp; guint length; + /* We shouldn't be called with a NULL pointer here because we've + * created a visible protocol tree */ + g_assert(edt->pi.data_src); + /* * Set "multiple_sources" iff this frame has more than one * data source; if it does, we need to print the name of @@ -234,141 +808,127 @@ void print_hex_data(FILE *fh, gint format, epan_dissect_t *edt) for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) { - src = src_le->data; + src = (data_source *)src_le->data; tvb = src->tvb; if (multiple_sources) { - name = src->name; - print_line(fh, format, "\n"); - line = g_malloc(strlen(name) + 3); /* :\n\0 */ - strcpy(line, name); - strcat(line, ":\n"); - print_line(fh, format, line); + name = get_data_source_name(src); + print_line(stream, 0, ""); + line = g_strdup_printf("%s:", name); + print_line(stream, 0, line); g_free(line); } length = tvb_length(tvb); + if (length == 0) + return TRUE; cp = tvb_get_ptr(tvb, 0, length); - if (format == PR_FMT_PS) { - print_hex_data_ps(fh, cp, length, - edt->pi.fd->flags.encoding); - } else { - print_hex_data_text(fh, cp, length, - edt->pi.fd->flags.encoding); - } + if (!print_hex_data_buffer(stream, cp, length, + edt->pi.fd->flags.encoding)) + return FALSE; } + return TRUE; } /* * This routine is based on a routine created by Dan Lasley * . * - * It was modified for Ethereal by Gilbert Ramirez and others. + * It was modified for Wireshark by Gilbert Ramirez and others. */ -static -void print_hex_data_common(FILE *fh, register const u_char *cp, - register u_int length, char_enc encoding, - void (*print_hex_data_start)(FILE *), - void (*print_hex_data_line)(FILE *, u_char *)) -{ - register unsigned int ad, i, j, k; - u_char c; - u_char line[6+50+16+1]; - static u_char binhex[16] = { + +#define MAX_OFFSET_LEN 8 /* max length of hex offset of bytes */ +#define BYTES_PER_LINE 16 /* max byte values printed on a line */ +#define HEX_DUMP_LEN (BYTES_PER_LINE*3) + /* max number of characters hex dump takes - + 2 digits plus trailing blank */ +#define DATA_DUMP_LEN (HEX_DUMP_LEN + 2 + BYTES_PER_LINE) + /* number of characters those bytes take; + 3 characters per byte of hex dump, + 2 blanks separating hex from ASCII, + 1 character per byte of ASCII dump */ +#define MAX_LINE_LEN (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN) + /* number of characters per line; + offset, 2 blanks separating offset + from data dump, data dump */ + +static gboolean +print_hex_data_buffer(print_stream_t *stream, const guchar *cp, + guint length, packet_char_enc encoding) +{ + register unsigned int ad, i, j, k, l; + guchar c; + guchar line[MAX_LINE_LEN + 1]; + unsigned int use_digits; + static guchar binhex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - if (print_hex_data_start != NULL) - (*print_hex_data_start)(fh); - (*print_hex_data_line)(fh, ""); + if (!print_line(stream, 0, "")) + return FALSE; + + /* + * How many of the leading digits of the offset will we supply? + * We always supply at least 4 digits, but if the maximum offset + * won't fit in 4 digits, we use as many digits as will be needed. + */ + if (((length - 1) & 0xF0000000) != 0) + use_digits = 8; /* need all 8 digits */ + else if (((length - 1) & 0x0F000000) != 0) + use_digits = 7; /* need 7 digits */ + else if (((length - 1) & 0x00F00000) != 0) + use_digits = 6; /* need 6 digits */ + else if (((length - 1) & 0x000F0000) != 0) + use_digits = 5; /* need 5 digits */ + else + use_digits = 4; /* we'll supply 4 digits */ + ad = 0; + i = 0; j = 0; k = 0; - sprintf(line, "%04x ", ad); - memset(line+6, ' ', sizeof line-6); - line[sizeof (line)-1] = '\0'; - for (i=0; i> (l*4)) & 0xF; + line[j++] = binhex[c]; + } while (l != 0); + line[j++] = ' '; + line[j++] = ' '; + memset(line+j, ' ', DATA_DUMP_LEN); + + /* + * Offset in line of ASCII dump. + */ + k = j + HEX_DUMP_LEN + 2; + } c = *cp++; - line[6+j++] = binhex[c>>4]; - line[6+j++] = binhex[c&0xf]; + line[j++] = binhex[c>>4]; + line[j++] = binhex[c&0xf]; j++; - if (encoding == CHAR_EBCDIC) { + if (encoding == PACKET_CHAR_ENC_CHAR_EBCDIC) { c = EBCDIC_to_ASCII1(c); } - line[6+50+k++] = c >= ' ' && c < 0x7f ? c : '.'; - if ((i & 15) == 15) { - (*print_hex_data_line)(fh, line); + line[k++] = c >= ' ' && c < 0x7f ? c : '.'; + i++; + if ((i & 15) == 0 || i == length) { + /* + * We'll be starting a new line, or + * we're finished printing this buffer; + * dump out the line we've constructed, + * and advance the offset. + */ + line[k] = '\0'; + if (!print_line(stream, 0, line)) + return FALSE; ad += 16; - j = 0; - k = 0; - sprintf(line, "%04x ", ad); - memset(line+6, ' ', sizeof line-6); - line[sizeof (line)-1] = '\0'; } } - - if (line[0] != ' ') - (*print_hex_data_line)(fh, line); -} - -static void -print_hex_data_line_text(FILE *fh, u_char *line) -{ - fprintf(fh, "%s\n", line); -} - -static -void print_hex_data_text(FILE *fh, register const u_char *cp, - register u_int length, char_enc encoding) -{ - print_hex_data_common(fh, cp, length, encoding, NULL, - print_hex_data_line_text); -} - -#define MAX_LINE_LENGTH 256 - -/* Print a node's data, and any child nodes, in PostScript */ -static -void proto_tree_print_node_ps(GNode *node, gpointer data) -{ - field_info *fi = PITEM_FINFO(node); - print_data *pdata = (print_data*) data; - gchar label_str[ITEM_LABEL_LENGTH]; - gchar *label_ptr; - const guint8 *pd; - char psbuffer[MAX_LINE_LENGTH]; /* static sized buffer! */ - - if (!fi->visible) - return; - - /* was a free format label produced? */ - if (fi->representation) { - label_ptr = fi->representation; - } - else { /* no, make a generic label */ - label_ptr = label_str; - proto_item_fill_label(fi, label_str); - } - - /* Print the text */ - ps_clean_string(psbuffer, label_ptr, MAX_LINE_LENGTH); - fprintf(pdata->fh, "%d (%s) putline\n", pdata->level, psbuffer); - - /* If it's uninterpreted data, dump it (unless our caller will - be printing the entire packet in hex). */ - if (fi->hfinfo->id == proto_data && pdata->print_hex_for_data) { - /* - * Find the data for this field. - */ - pd = get_field_data(pdata->src_list, fi); - print_hex_data_ps(pdata->fh, pd, fi->length, pdata->encoding); - } - - /* Recurse into the subtree, if it exists */ - if (g_node_n_children(node) > 0) { - pdata->level++; - g_node_children_foreach(node, G_TRAVERSE_ALL, - proto_tree_print_node_ps, pdata); - pdata->level--; - } + return TRUE; } static @@ -399,30 +959,671 @@ void ps_clean_string(unsigned char *out, const unsigned char *in, } } -static void -print_hex_data_line_ps(FILE *fh, u_char *line) +/* Some formats need stuff at the beginning of the output */ +gboolean +print_preamble(print_stream_t *self, gchar *filename) +{ + return (self->ops->print_preamble)(self, filename); +} + +gboolean +print_line(print_stream_t *self, int indent, const char *line) { - u_char psline[MAX_LINE_LENGTH]; + return (self->ops->print_line)(self, indent, line); +} - ps_clean_string(psline, line, MAX_LINE_LENGTH); - fprintf(fh, "(%s) hexdump\n", psline); +/* Insert bookmark */ +gboolean +print_bookmark(print_stream_t *self, const gchar *name, const gchar *title) +{ + return (self->ops->print_bookmark)(self, name, title); } -static -void print_hex_data_ps(FILE *fh, register const u_char *cp, - register u_int length, char_enc encoding) +gboolean +new_page(print_stream_t *self) +{ + return (self->ops->new_page)(self); +} + +/* Some formats need stuff at the end of the output */ +gboolean +print_finale(print_stream_t *self) +{ + return (self->ops->print_finale)(self); +} + +gboolean +destroy_print_stream(print_stream_t *self) +{ + return (self->ops->destroy)(self); +} + +typedef struct { + int to_file; + FILE *fh; +} output_text; + +static gboolean +print_preamble_text(print_stream_t *self _U_, gchar *filename _U_) +{ + /* do nothing */ + return TRUE; /* always succeeds */ +} + +static gboolean +print_line_text(print_stream_t *self, int indent, const char *line) +{ + output_text *output = (output_text *)self->data; + char space[MAX_INDENT+1]; + int i; + int num_spaces; + + /* Prepare the tabs for printing, depending on tree level */ + num_spaces = indent * 4; + if (num_spaces > MAX_INDENT) { + num_spaces = MAX_INDENT; + } + for (i = 0; i < num_spaces; i++) { + space[i] = ' '; + } + /* The string is NUL-terminated */ + space[num_spaces] = '\0'; + + fputs(space, output->fh); + fputs(line, output->fh); + putc('\n', output->fh); + return !ferror(output->fh); +} + +static gboolean +print_bookmark_text(print_stream_t *self _U_, const gchar *name _U_, + const gchar *title _U_) +{ + /* do nothing */ + return TRUE; +} + +static gboolean +new_page_text(print_stream_t *self) +{ + output_text *output = (output_text *)self->data; + + fputs("\f", output->fh); + return !ferror(output->fh); +} + +static gboolean +print_finale_text(print_stream_t *self _U_) +{ + /* do nothing */ + return TRUE; /* always succeeds */ +} + +static gboolean +destroy_text(print_stream_t *self) +{ + output_text *output = (output_text *)self->data; + gboolean ret; + + ret = close_print_dest(output->to_file, output->fh); + g_free(output); + g_free(self); + return ret; +} + +static const print_stream_ops_t print_text_ops = { + print_preamble_text, + print_line_text, + print_bookmark_text, + new_page_text, + print_finale_text, + destroy_text +}; + +static print_stream_t * +print_stream_text_alloc(int to_file, FILE *fh) +{ + print_stream_t *stream; + output_text *output; + + output = (output_text *)g_malloc(sizeof *output); + output->to_file = to_file; + output->fh = fh; + stream = (print_stream_t *)g_malloc(sizeof (print_stream_t)); + stream->ops = &print_text_ops; + stream->data = output; + + return stream; +} + +print_stream_t * +print_stream_text_new(int to_file, const char *dest) +{ + FILE *fh; + + fh = open_print_dest(to_file, dest); + if (fh == NULL) + return NULL; + + return print_stream_text_alloc(to_file, fh); +} + +print_stream_t * +print_stream_text_stdio_new(FILE *fh) +{ + return print_stream_text_alloc(TRUE, fh); +} + +typedef struct { + int to_file; + FILE *fh; +} output_ps; + +static gboolean +print_preamble_ps(print_stream_t *self, gchar *filename) +{ + output_ps *output = (output_ps *)self->data; + unsigned char psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */ + + print_ps_preamble(output->fh); + + fputs("%% Set the font to 8 point\n", output->fh); + fputs("/Courier findfont 8 scalefont setfont\n", output->fh); + fputs("\n", output->fh); + fputs("%% the page title\n", output->fh); + ps_clean_string(psbuffer, filename, MAX_PS_LINE_LENGTH); + fprintf(output->fh, "/ws_pagetitle (%s - Wireshark " VERSION "%s) def\n", psbuffer, wireshark_svnversion); + fputs("\n", output->fh); + return !ferror(output->fh); +} + +static gboolean +print_line_ps(print_stream_t *self, int indent, const char *line) +{ + output_ps *output = (output_ps *)self->data; + unsigned char psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */ + + ps_clean_string(psbuffer, line, MAX_PS_LINE_LENGTH); + fprintf(output->fh, "%d (%s) putline\n", indent, psbuffer); + return !ferror(output->fh); +} + +static gboolean +print_bookmark_ps(print_stream_t *self, const gchar *name, const gchar *title) +{ + output_ps *output = (output_ps *)self->data; + unsigned char psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */ + + /* + * See the Adobe "pdfmark reference": + * + * http://partners.adobe.com/asn/acrobat/docs/pdfmark.pdf + * + * The pdfmark stuff tells code that turns PostScript into PDF + * things that it should do. + * + * The /OUT stuff creates a bookmark that goes to the + * destination with "name" as the name and "title" as the title. + * + * The "/DEST" creates the destination. + */ + ps_clean_string(psbuffer, title, MAX_PS_LINE_LENGTH); + fprintf(output->fh, "[/Dest /%s /Title (%s) /OUT pdfmark\n", name, + psbuffer); + fputs("[/View [/XYZ -4 currentpoint matrix currentmatrix matrix defaultmatrix\n", + output->fh); + fputs("matrix invertmatrix matrix concatmatrix transform exch pop 20 add null]\n", + output->fh); + fprintf(output->fh, "/Dest /%s /DEST pdfmark\n", name); + return !ferror(output->fh); +} + +static gboolean +new_page_ps(print_stream_t *self) +{ + output_ps *output = (output_ps *)self->data; + + fputs("formfeed\n", output->fh); + return !ferror(output->fh); +} + +static gboolean +print_finale_ps(print_stream_t *self) +{ + output_ps *output = (output_ps *)self->data; + + print_ps_finale(output->fh); + return !ferror(output->fh); +} + +static gboolean +destroy_ps(print_stream_t *self) +{ + output_ps *output = (output_ps *)self->data; + gboolean ret; + + ret = close_print_dest(output->to_file, output->fh); + g_free(output); + g_free(self); + return ret; +} + +static const print_stream_ops_t print_ps_ops = { + print_preamble_ps, + print_line_ps, + print_bookmark_ps, + new_page_ps, + print_finale_ps, + destroy_ps +}; + +static print_stream_t * +print_stream_ps_alloc(int to_file, FILE *fh) +{ + print_stream_t *stream; + output_ps *output; + + output = (output_ps *)g_malloc(sizeof *output); + output->to_file = to_file; + output->fh = fh; + stream = (print_stream_t *)g_malloc(sizeof (print_stream_t)); + stream->ops = &print_ps_ops; + stream->data = output; + + return stream; +} + +print_stream_t * +print_stream_ps_new(int to_file, const char *dest) +{ + FILE *fh; + + fh = open_print_dest(to_file, dest); + if (fh == NULL) + return NULL; + + return print_stream_ps_alloc(to_file, fh); +} + +print_stream_t * +print_stream_ps_stdio_new(FILE *fh) +{ + return print_stream_ps_alloc(TRUE, fh); +} + +output_fields_t* output_fields_new() +{ + output_fields_t* fields = g_new(output_fields_t, 1); + fields->print_header = FALSE; + fields->separator = '\t'; + fields->aggregator = ','; + fields->fields = NULL; /*Do lazy initialisation */ + fields->field_indicies = NULL; + fields->field_values = NULL; + fields->quote='\0'; + return fields; +} + +gsize output_fields_num_fields(output_fields_t* fields) +{ + g_assert(fields); + + if(NULL == fields->fields) { + return 0; + } else { + return fields->fields->len; + } +} + +void output_fields_free(output_fields_t* fields) +{ + g_assert(fields); + + if(NULL != fields->field_indicies) { + /* Keys are stored in fields->fields, values are + * integers. + */ + g_hash_table_destroy(fields->field_indicies); + } + if(NULL != fields->fields) { + gsize i; + for(i = 0; i < fields->fields->len; ++i) { + gchar* field = (gchar *)g_ptr_array_index(fields->fields,i); + g_free(field); + } + g_ptr_array_free(fields->fields, TRUE); + } + + g_free(fields); +} + +void output_fields_add(output_fields_t* fields, const gchar* field) +{ + gchar* field_copy; + + g_assert(fields); + g_assert(field); + + + if(NULL == fields->fields) { + fields->fields = g_ptr_array_new(); + } + + field_copy = g_strdup(field); + + g_ptr_array_add(fields->fields, field_copy); +} + +gboolean output_fields_set_option(output_fields_t* info, gchar* option) +{ + const gchar* option_name; + const gchar* option_value; + + g_assert(info); + g_assert(option); + + if('\0' == *option) { + return FALSE; /* Is this guarded against by option parsing? */ + } + option_name = strtok(option,"="); + option_value = option + strlen(option_name) + 1; + if(0 == strcmp(option_name, "header")) { + switch(NULL == option_value ? '\0' : *option_value) { + case 'n': + info->print_header = FALSE; + break; + case 'y': + info->print_header = TRUE; + break; + default: + return FALSE; + } + return TRUE; + } + + if(0 == strcmp(option_name,"separator")) { + switch(NULL == option_value ? '\0' : *option_value) { + case '\0': + return FALSE; + case '/': + switch(*++option_value) { + case 't': + info->separator = '\t'; + break; + case 's': + info->separator = ' '; + break; + default: + info->separator = '\\'; + } + break; + default: + info->separator = *option_value; + break; + } + return TRUE; + } + + if(0 == strcmp(option_name,"aggregator")) { + switch(NULL == option_value ? '\0' : *option_value) { + case '\0': + return FALSE; + case '/': + switch(*++option_value) { + case 's': + info->aggregator = ' '; + break; + default: + info->aggregator = '\\'; + } + break; + default: + info->aggregator = *option_value; + break; + } + return TRUE; + } + + if(0 == strcmp(option_name, "quote")) { + switch(NULL == option_value ? '\0' : *option_value) { + default: /* Fall through */ + case '\0': + info->quote='\0'; + return FALSE; + case 'd': + info->quote='"'; + break; + case 's': + info->quote='\''; + break; + case 'n': + info->quote='\0'; + break; + } + return TRUE; + } + + return FALSE; +} + +void output_fields_list_options(FILE *fh) +{ + fprintf(fh, "TShark: The available options for field output \"E\" are:\n"); + fputs("header=y|n Print field abbreviations as first line of output (def: N: no)\n", fh); + fputs("separator=/t|/s| Set the separator to use; \"/t\" = tab,\n \"/s\" = space (def: /t: tab)\n", fh); + fputs("quote=d|s|n Print either d: double-quotes, s: single quotes or n: no quotes around field values (def: n: none)\n", fh); +} + + +void write_fields_preamble(output_fields_t* fields, FILE *fh) +{ + gsize i; + + g_assert(fields); + g_assert(fh); + + if(!fields->print_header) { + return; + } + + for(i = 0; i < fields->fields->len; ++i) { + const gchar* field = (const gchar *)g_ptr_array_index(fields->fields,i); + if(i != 0 ) { + fputc(fields->separator, fh); + } + fputs(field, fh); + } + fputc('\n', fh); +} + +static void proto_tree_get_node_field_values(proto_node *node, gpointer data) +{ + write_field_data_t *call_data; + field_info *fi; + gpointer field_index; + + call_data = (write_field_data_t *)data; + fi = PNODE_FINFO(node); + + g_assert(fi && "dissection with an invisible proto tree?"); + + field_index = g_hash_table_lookup(call_data->fields->field_indicies, fi->hfinfo->abbrev); + if(NULL != field_index) { + const gchar* value; + + value = get_node_field_value(fi, call_data->edt); /* ep_alloced string */ + + if(NULL != value && '\0' != *value) { + guint actual_index; + actual_index = GPOINTER_TO_UINT(field_index); + /* Unwrap change made to disambiguiate zero / null */ + if (call_data->fields->field_values[actual_index - 1] == NULL ) { + call_data->fields->field_values[actual_index - 1] = ep_strbuf_new(value); + } else { + ep_strbuf_append_printf(call_data->fields->field_values[actual_index - 1], + "%c%s",call_data->fields->aggregator,value); + } + } + } + + /* Recurse here. */ + if (node->first_child != NULL) { + proto_tree_children_foreach(node, proto_tree_get_node_field_values, + call_data); + } +} + +void proto_tree_write_fields(output_fields_t* fields, epan_dissect_t *edt, FILE *fh) +{ + gsize i; + + write_field_data_t data; + + g_assert(fields); + g_assert(edt); + g_assert(fh); + + data.fields = fields; + data.edt = edt; + + if(NULL == fields->field_indicies) { + /* Prepare a lookup table from string abbreviation for field to its index. */ + fields->field_indicies = g_hash_table_new(g_str_hash, g_str_equal); + + i = 0; + while( i < fields->fields->len) { + gchar* field = (gchar *)g_ptr_array_index(fields->fields, i); + /* Store field indicies +1 so that zero is not a valid value, + * and can be distinguished from NULL as a pointer. + */ + ++i; + g_hash_table_insert(fields->field_indicies, field, GUINT_TO_POINTER(i)); + } + } + + /* Buffer to store values for this packet */ + fields->field_values = ep_alloc_array0(emem_strbuf_t*, fields->fields->len); + + proto_tree_children_foreach(edt->tree, proto_tree_get_node_field_values, + &data); + + for(i = 0; i < fields->fields->len; ++i) { + if(0 != i) { + fputc(fields->separator, fh); + } + if(NULL != fields->field_values[i]) { + if(fields->quote != '\0') { + fputc(fields->quote, fh); + } + fputs(fields->field_values[i]->str, fh); + if(fields->quote != '\0') { + fputc(fields->quote, fh); + } + } + } +} + +void write_fields_finale(output_fields_t* fields _U_ , FILE *fh _U_) +{ + /* Nothing to do */ +} + +/* Returns an ep_alloced string or a static constant*/ +const gchar* get_node_field_value(field_info* fi, epan_dissect_t* edt) { - print_hex_data_common(fh, cp, length, encoding, - print_ps_hex, print_hex_data_line_ps); + if (fi->hfinfo->id == hf_text_only) { + /* Text label. + * Get the text */ + if (fi->rep) { + return fi->rep->representation; + } + else { + return get_field_hex_value(edt->pi.data_src, fi); + } + } + else if (fi->hfinfo->id == proto_data) { + /* Uninterpreted data, i.e., the "Data" protocol, is + * printed as a field instead of a protocol. */ + return get_field_hex_value(edt->pi.data_src, fi); + } + else { + /* Normal protocols and fields */ + gchar *dfilter_string; + size_t chop_len; + + switch (fi->hfinfo->type) + { + case FT_PROTOCOL: + /* Print out the full details for the protocol. */ + if (fi->rep) { + return fi->rep->representation; + } else { + /* Just print out the protocol abbreviation */ + return fi->hfinfo->abbrev;; + } + case FT_NONE: + /* Return "1" so that the presence of a field of type + * FT_NONE can be checked when using -T fields */ + return "1"; + default: + /* XXX - this is a hack until we can just call + * fvalue_to_string_repr() for *all* FT_* types. */ + dfilter_string = proto_construct_match_selected_string(fi, + edt); + if (dfilter_string != NULL) { + chop_len = strlen(fi->hfinfo->abbrev) + 4; /* for " == " */ + + /* XXX - Remove double-quotes. Again, once we + * can call fvalue_to_string_repr(), we can + * ask it not to produce the version for + * display-filters, and thus, no + * double-quotes. */ + if (dfilter_string[strlen(dfilter_string)-1] == '"') { + dfilter_string[strlen(dfilter_string)-1] = '\0'; + chop_len++; + } + + return &(dfilter_string[chop_len]); + } else { + return get_field_hex_value(edt->pi.data_src, fi); + } + } + } } -void print_line(FILE *fh, gint format, char *line) +static const gchar* +get_field_hex_value(GSList* src_list, field_info *fi) { - char psbuffer[MAX_LINE_LENGTH]; /* static sized buffer! */ + const guint8 *pd; + + if (!fi->ds_tvb) + return NULL; + + if (fi->length > tvb_length_remaining(fi->ds_tvb, fi->start)) { + return "field length invalid!"; + } + + /* Find the data for this field. */ + pd = get_field_data(src_list, fi); + + if (pd) { + int i; + gchar* buffer; + gchar* p; + int len; + const int chars_per_byte = 2; - if (format == PR_FMT_PS) { - ps_clean_string(psbuffer, line, MAX_LINE_LENGTH); - fprintf(fh, "(%s) hexdump\n", psbuffer); - } else - fputs(line, fh); + len = chars_per_byte * fi->length; + buffer = ep_alloc_array(gchar, len + 1); + buffer[len] = '\0'; /* Ensure NULL termination in bad cases */ + p = buffer; + /* Print a simple hex dump */ + for (i = 0 ; i < fi->length; i++) { + g_snprintf(p, chars_per_byte+1, "%02x", pd[i]); + p += chars_per_byte; + } + return buffer; + } else { + return NULL; + } }