Clean up indentation.
[obnox/wireshark/wip.git] / print.c
1 /* print.c
2  * Routines for printing packet analysis trees.
3  *
4  * $Id$
5  *
6  * Gilbert Ramirez <gram@alumni.rice.edu>
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <stdio.h>
32 #include <string.h>
33
34 #include <epan/epan.h>
35 #include <epan/epan_dissect.h>
36 #include <epan/tvbuff.h>
37 #include <epan/packet.h>
38 #include <epan/emem.h>
39
40 #include "packet-range.h"
41 #include "print.h"
42 #include "ps.h"
43 #include "file_util.h"
44 #include <epan/charsets.h>
45 #include <epan/dissectors/packet-data.h>
46 #include <epan/dissectors/packet-frame.h>
47
48 #define PDML_VERSION "0"
49 #define PSML_VERSION "0"
50
51 typedef struct {
52         int                     level;
53         print_stream_t          *stream;
54         gboolean                success;
55         GSList                  *src_list;
56         print_dissections_e     print_dissections;
57         gboolean                print_hex_for_data;
58         char_enc                encoding;
59         epan_dissect_t          *edt;
60 } print_data;
61
62 typedef struct {
63         int                     level;
64         FILE                    *fh;
65         GSList                  *src_list;
66         epan_dissect_t          *edt;
67 } write_pdml_data;
68
69 typedef struct {
70     output_fields_t* fields;
71         epan_dissect_t          *edt;
72 } write_field_data_t;
73
74 struct _output_fields {
75     gboolean print_header;
76     gchar separator;
77     GPtrArray* fields;
78     GHashTable* field_indicies;
79     const gchar** field_values;
80     gchar quote;
81 };
82
83 static const gchar* get_field_hex_value(GSList* src_list, field_info *fi);
84 static const gchar* get_node_field_value(field_info* fi, epan_dissect_t* edt);
85 static void proto_tree_print_node(proto_node *node, gpointer data);
86 static void proto_tree_write_node_pdml(proto_node *node, gpointer data);
87 static const guint8 *get_field_data(GSList *src_list, field_info *fi);
88 static void write_pdml_field_hex_value(write_pdml_data *pdata, field_info *fi);
89 static gboolean print_hex_data_buffer(print_stream_t *stream, const guchar *cp,
90     guint length, char_enc encoding);
91 static void ps_clean_string(unsigned char *out, const unsigned char *in,
92                         int outbuf_size);
93 static void print_escaped_xml(FILE *fh, const char *unescaped_string);
94
95 static void print_pdml_geninfo(proto_tree *tree, FILE *fh);
96
97 static void proto_tree_get_node_field_values(proto_node *node, gpointer data);
98
99 static FILE *
100 open_print_dest(int to_file, const char *dest)
101 {
102         FILE    *fh;
103
104         /* Open the file or command for output */
105         if (to_file)
106                 fh = eth_fopen(dest, "w");
107         else
108                 fh = popen(dest, "w");
109
110         return fh;
111 }
112
113 static gboolean
114 close_print_dest(int to_file, FILE *fh)
115 {
116         /* Close the file or command */
117         if (to_file)
118                 return (fclose(fh) == 0);
119         else
120                 return (pclose(fh) == 0);
121 }
122
123 #define MAX_PS_LINE_LENGTH 256
124
125 gboolean
126 proto_tree_print(print_args_t *print_args, epan_dissect_t *edt,
127     print_stream_t *stream)
128 {
129         print_data data;
130
131         /* Create the output */
132         data.level = 0;
133         data.stream = stream;
134         data.success = TRUE;
135         data.src_list = edt->pi.data_src;
136         data.encoding = edt->pi.fd->flags.encoding;
137         data.print_dissections = print_args->print_dissections;
138         /* If we're printing the entire packet in hex, don't
139            print uninterpreted data fields in hex as well. */
140         data.print_hex_for_data = !print_args->print_hex;
141         data.edt = edt;
142
143         proto_tree_children_foreach(edt->tree, proto_tree_print_node, &data);
144         return data.success;
145 }
146
147 #define MAX_INDENT      160
148
149 /* Print a tree's data, and any child nodes. */
150 static
151 void proto_tree_print_node(proto_node *node, gpointer data)
152 {
153         field_info      *fi = PITEM_FINFO(node);
154         print_data      *pdata = (print_data*) data;
155         const guint8    *pd;
156         gchar           label_str[ITEM_LABEL_LENGTH];
157         gchar           *label_ptr;
158
159         /* Don't print invisible entries. */
160         if (PROTO_ITEM_IS_HIDDEN(node))
161                 return;
162
163         /* Give up if we've already gotten an error. */
164         if (!pdata->success)
165                 return;
166
167         /* was a free format label produced? */
168         if (fi->rep) {
169                 label_ptr = fi->rep->representation;
170         }
171         else { /* no, make a generic label */
172                 label_ptr = label_str;
173                 proto_item_fill_label(fi, label_str);
174         }
175
176         if (PROTO_ITEM_IS_GENERATED(node)) {
177                 label_ptr = g_strdup_printf("[%s]", label_ptr);
178         }
179
180         if (!print_line(pdata->stream, pdata->level, label_ptr)) {
181                 pdata->success = FALSE;
182                 return;
183         }
184
185         if (PROTO_ITEM_IS_GENERATED(node)) {
186                 g_free(label_ptr);
187         }
188
189         /* If it's uninterpreted data, dump it (unless our caller will
190            be printing the entire packet in hex). */
191         if (fi->hfinfo->id == proto_data && pdata->print_hex_for_data) {
192                 /*
193                  * Find the data for this field.
194                  */
195                 pd = get_field_data(pdata->src_list, fi);
196                 if (pd) {
197                         if (!print_hex_data_buffer(pdata->stream, pd,
198                             fi->length, pdata->encoding)) {
199                                 pdata->success = FALSE;
200                                 return;
201                         }
202                 }
203         }
204
205         /* If we're printing all levels, or if this node is one with a
206            subtree and its subtree is expanded, recurse into the subtree,
207            if it exists. */
208         g_assert(fi->tree_type >= -1 && fi->tree_type < num_tree_types);
209         if (pdata->print_dissections == print_dissections_expanded ||
210             (pdata->print_dissections == print_dissections_as_displayed &&
211                 fi->tree_type >= 0 && tree_is_expanded[fi->tree_type])) {
212                 if (node->first_child != NULL) {
213                         pdata->level++;
214                         proto_tree_children_foreach(node,
215                                 proto_tree_print_node, pdata);
216                         pdata->level--;
217                         if (!pdata->success)
218                                 return;
219                 }
220         }
221 }
222
223 void
224 write_pdml_preamble(FILE *fh)
225 {
226         fputs("<?xml version=\"1.0\"?>\n", fh);
227         fputs("<pdml version=\"" PDML_VERSION "\" ", fh);
228         fprintf(fh, "creator=\"%s/%s\">\n", PACKAGE, VERSION);
229 }
230
231 void
232 proto_tree_write_pdml(epan_dissect_t *edt, FILE *fh)
233 {
234         write_pdml_data data;
235
236         /* Create the output */
237         data.level = 0;
238         data.fh = fh;
239         data.src_list = edt->pi.data_src;
240         data.edt = edt;
241
242         fprintf(fh, "<packet>\n");
243
244         /* Print a "geninfo" protocol as required by PDML */
245         print_pdml_geninfo(edt->tree, fh);
246
247         proto_tree_children_foreach(edt->tree, proto_tree_write_node_pdml,
248             &data);
249
250         fprintf(fh, "</packet>\n\n");
251 }
252
253 /* Write out a tree's data, and any child nodes, as PDML */
254 static void
255 proto_tree_write_node_pdml(proto_node *node, gpointer data)
256 {
257         field_info      *fi = PITEM_FINFO(node);
258         write_pdml_data *pdata = (write_pdml_data*) data;
259         const gchar     *label_ptr;
260         gchar           label_str[ITEM_LABEL_LENGTH];
261         char            *dfilter_string;
262         int             chop_len;
263         int             i;
264
265         for (i = -1; i < pdata->level; i++) {
266                 fputs("  ", pdata->fh);
267         }
268
269         /* Text label. It's printed as a field with no name. */
270         if (fi->hfinfo->id == hf_text_only) {
271                 /* Get the text */
272                 if (fi->rep) {
273                         label_ptr = fi->rep->representation;
274                 }
275                 else {
276                         label_ptr = "";
277                 }
278
279                 /* Show empty name since it is a required field */
280                 fputs("<field name=\"", pdata->fh);
281                 fputs("\" show=\"", pdata->fh);
282                 print_escaped_xml(pdata->fh, label_ptr);
283
284                 fprintf(pdata->fh, "\" size=\"%d", fi->length);
285                 fprintf(pdata->fh, "\" pos=\"%d", fi->start);
286
287                 fputs("\" value=\"", pdata->fh);
288                 write_pdml_field_hex_value(pdata, fi);
289
290                 if (node->first_child != NULL) {
291                         fputs("\">\n", pdata->fh);
292                 }
293                 else {
294                         fputs("\"/>\n", pdata->fh);
295                 }
296         }
297         /* Uninterpreted data, i.e., the "Data" protocol, is
298          * printed as a field instead of a protocol. */
299         else if (fi->hfinfo->id == proto_data) {
300
301                 fputs("<field name=\"data\" value=\"", pdata->fh);
302
303                 write_pdml_field_hex_value(pdata, fi);
304
305                 fputs("\"/>\n", pdata->fh);
306
307         }
308         /* Normal protocols and fields */
309         else {
310                 if (fi->hfinfo->type == FT_PROTOCOL) {
311                         fputs("<proto name=\"", pdata->fh);
312                 }
313                 else {
314                         fputs("<field name=\"", pdata->fh);
315                 }
316                 print_escaped_xml(pdata->fh, fi->hfinfo->abbrev);
317
318 #if 0
319         /* PDML spec, see: 
320          * http://analyzer.polito.it/30alpha/docs/dissectors/PDMLSpec.htm
321          *
322          * the show fields contains things in 'human readable' format
323          * showname: contains only the name of the field
324          * show: contains only the data of the field
325          * showdtl: contains additional details of the field data
326          * showmap: contains mappings of the field data (e.g. the hostname to an IP address)
327          *
328          * XXX - the showname shouldn't contain the field data itself 
329          * (like it's contained in the fi->rep->representation). 
330          * Unfortunately, we don't have the field data representation for 
331          * all fields, so this isn't currently possible */
332                 fputs("\" showname=\"", pdata->fh);
333                 print_escaped_xml(pdata->fh, fi->hfinfo->name);
334 #endif
335
336                 if (fi->rep) {
337                         fputs("\" showname=\"", pdata->fh);
338                         print_escaped_xml(pdata->fh, fi->rep->representation);
339                 }
340                 else {
341                         label_ptr = label_str;
342                         proto_item_fill_label(fi, label_str);
343                         fputs("\" showname=\"", pdata->fh);
344                         print_escaped_xml(pdata->fh, label_ptr);
345                 }
346
347                 if (PROTO_ITEM_IS_HIDDEN(node))
348                         fprintf(pdata->fh, "\" hide=\"yes");
349
350                 fprintf(pdata->fh, "\" size=\"%d", fi->length);
351                 fprintf(pdata->fh, "\" pos=\"%d", fi->start);
352 /*              fprintf(pdata->fh, "\" id=\"%d", fi->hfinfo->id);*/
353
354                 /* show, value, and unmaskedvalue attributes */
355                 switch (fi->hfinfo->type)
356                 {
357                 case FT_PROTOCOL:
358                         break;
359                 case FT_NONE:
360                         fputs("\" show=\"\" value=\"",  pdata->fh);
361                         break;
362                 default:
363                         /* XXX - this is a hack until we can just call
364                          * fvalue_to_string_repr() for *all* FT_* types. */
365                         dfilter_string = proto_construct_match_selected_string(fi,
366                             pdata->edt);
367                         if (dfilter_string != NULL) {
368                                 chop_len = strlen(fi->hfinfo->abbrev) + 4; /* for " == " */
369
370                                 /* XXX - Remove double-quotes. Again, once we
371                                  * can call fvalue_to_string_repr(), we can
372                                  * ask it not to produce the version for
373                                  * display-filters, and thus, no
374                                  * double-quotes. */
375                                 if (dfilter_string[strlen(dfilter_string)-1] == '"') {
376                                         dfilter_string[strlen(dfilter_string)-1] = '\0';
377                                         chop_len++;
378                                 }
379
380                                 fputs("\" show=\"", pdata->fh);
381                                 print_escaped_xml(pdata->fh, &dfilter_string[chop_len]);
382                         }
383
384                         /*
385                          * XXX - should we omit "value" for any fields?
386                          * What should we do for fields whose length is 0?
387                          * They might come from a pseudo-header or from
388                          * the capture header (e.g., time stamps), or
389                          * they might be generated fields.
390                          */
391                         if (fi->length > 0) {
392                                 fputs("\" value=\"", pdata->fh);
393
394                                 if (fi->hfinfo->bitmask!=0) {
395                                         fprintf(pdata->fh, "%X", fvalue_get_uinteger(&fi->value));
396                                         fputs("\" unmaskedvalue=\"", pdata->fh);
397                                         write_pdml_field_hex_value(pdata, fi);
398                                 }
399                                 else {
400                                         write_pdml_field_hex_value(pdata, fi);
401                                 }
402                         }
403                 }
404
405                 if (node->first_child != NULL) {
406                         fputs("\">\n", pdata->fh);
407                 }
408                 else if (fi->hfinfo->id == proto_data) {
409                         fputs("\">\n", pdata->fh);
410                 }
411                 else {
412                         fputs("\"/>\n", pdata->fh);
413                 }
414         }
415
416         /* We always print all levels for PDML. Recurse here. */
417         if (node->first_child != NULL) {
418                 pdata->level++;
419                 proto_tree_children_foreach(node,
420                                 proto_tree_write_node_pdml, pdata);
421                 pdata->level--;
422         }
423
424         if (node->first_child != NULL) {
425                 for (i = -1; i < pdata->level; i++) {
426                         fputs("  ", pdata->fh);
427                 }
428                 if (fi->hfinfo->id != proto_data) {   /* Data protocol uses simple tags */
429                         if (fi->hfinfo->type == FT_PROTOCOL) {
430                                 fputs("</proto>\n", pdata->fh);
431                         }
432                         else {
433                                 fputs("</field>\n", pdata->fh);
434                         }
435                 }
436         }
437 }
438
439 /* Print info for a 'geninfo' pseudo-protocol. This is required by
440  * the PDML spec. The information is contained in Wireshark's 'frame' protocol,
441  * but we produce a 'geninfo' protocol in the PDML to conform to spec.
442  * The 'frame' protocol follows the 'geninfo' protocol in the PDML. */
443 static void
444 print_pdml_geninfo(proto_tree *tree, FILE *fh)
445 {
446         guint32 num, len, caplen;
447         nstime_t *timestamp;
448         GPtrArray *finfo_array;
449         field_info *frame_finfo;
450
451         /* Get frame protocol's finfo. */
452         finfo_array = proto_find_finfo(tree, proto_frame);
453         if (g_ptr_array_len(finfo_array) < 1) {
454                 return;
455         }
456         frame_finfo = finfo_array->pdata[0];
457         g_ptr_array_free(finfo_array, FALSE);
458
459         /* frame.number --> geninfo.num */
460         finfo_array = proto_find_finfo(tree, hf_frame_number);
461         if (g_ptr_array_len(finfo_array) < 1) {
462                 return;
463         }
464         num = fvalue_get_uinteger(&((field_info*)finfo_array->pdata[0])->value);
465         g_ptr_array_free(finfo_array, FALSE);
466
467         /* frame.pkt_len --> geninfo.len */
468         finfo_array = proto_find_finfo(tree, hf_frame_packet_len);
469         if (g_ptr_array_len(finfo_array) < 1) {
470                 return;
471         }
472         len = fvalue_get_uinteger(&((field_info*)finfo_array->pdata[0])->value);
473         g_ptr_array_free(finfo_array, FALSE);
474
475         /* frame.cap_len --> geninfo.caplen */
476         finfo_array = proto_find_finfo(tree, hf_frame_capture_len);
477         if (g_ptr_array_len(finfo_array) < 1) {
478                 return;
479         }
480         caplen = fvalue_get_uinteger(&((field_info*)finfo_array->pdata[0])->value);
481         g_ptr_array_free(finfo_array, FALSE);
482
483         /* frame.time --> geninfo.timestamp */
484         finfo_array = proto_find_finfo(tree, hf_frame_arrival_time);
485         if (g_ptr_array_len(finfo_array) < 1) {
486                 return;
487         }
488         timestamp = fvalue_get(&((field_info*)finfo_array->pdata[0])->value);
489         g_ptr_array_free(finfo_array, FALSE);
490
491         /* Print geninfo start */
492         fprintf(fh,
493 "  <proto name=\"geninfo\" pos=\"0\" showname=\"General information\" size=\"%u\">\n",
494                 frame_finfo->length);
495
496         /* Print geninfo.num */
497         fprintf(fh,
498 "    <field name=\"num\" pos=\"0\" show=\"%u\" showname=\"Number\" value=\"%x\" size=\"%u\"/>\n",
499                 num, num, frame_finfo->length);
500
501         /* Print geninfo.len */
502         fprintf(fh,
503 "    <field name=\"len\" pos=\"0\" show=\"%u\" showname=\"Packet Length\" value=\"%x\" size=\"%u\"/>\n",
504                 len, len, frame_finfo->length);
505
506         /* Print geninfo.caplen */
507         fprintf(fh,
508 "    <field name=\"caplen\" pos=\"0\" show=\"%u\" showname=\"Captured Length\" value=\"%x\" size=\"%u\"/>\n",
509                 caplen, caplen, frame_finfo->length);
510
511         /* Print geninfo.timestamp */
512         fprintf(fh,
513 "    <field name=\"timestamp\" pos=\"0\" show=\"%s\" showname=\"Captured Time\" value=\"%d.%09d\" size=\"%u\"/>\n",
514                 abs_time_to_str(timestamp), (int) timestamp->secs, timestamp->nsecs, frame_finfo->length);
515
516         /* Print geninfo end */
517         fprintf(fh,
518 "  </proto>\n");
519 }
520
521 void
522 write_pdml_finale(FILE *fh)
523 {
524         fputs("</pdml>\n", fh);
525 }
526
527 void
528 write_psml_preamble(FILE *fh)
529 {
530         fputs("<?xml version=\"1.0\"?>\n", fh);
531         fputs("<psml version=\"" PSML_VERSION "\" ", fh);
532         fprintf(fh, "creator=\"%s/%s\">\n", PACKAGE, VERSION);
533 }
534
535 void
536 proto_tree_write_psml(epan_dissect_t *edt, FILE *fh)
537 {
538         gint    i;
539
540         /* if this is the first packet, we have to create the PSML structure output */
541         if(edt->pi.fd->num == 1) {
542             fprintf(fh, "<structure>\n");
543
544             for(i=0; i < edt->pi.cinfo->num_cols; i++) {
545                 fprintf(fh, "<section>");
546                 print_escaped_xml(fh, edt->pi.cinfo->col_title[i]);
547                 fprintf(fh, "</section>\n");
548             }
549
550             fprintf(fh, "</structure>\n\n");
551         }
552
553         fprintf(fh, "<packet>\n");
554
555         for(i=0; i < edt->pi.cinfo->num_cols; i++) {
556             fprintf(fh, "<section>");
557             print_escaped_xml(fh, edt->pi.cinfo->col_data[i]);
558             fprintf(fh, "</section>\n");
559         }
560
561         fprintf(fh, "</packet>\n\n");
562 }
563
564 void
565 write_psml_finale(FILE *fh)
566 {
567         fputs("</psml>\n", fh);
568 }
569
570 void
571 write_csv_preamble(FILE *fh _U_)
572 {
573
574 }
575
576 void
577 proto_tree_write_csv(epan_dissect_t *edt, FILE *fh)
578 {
579         gint    i;
580
581         /* if this is the first packet, we have to write the CSV header */
582         if(edt->pi.fd->num == 1) {
583             for(i=0; i < edt->pi.cinfo->num_cols - 1; i++)
584                 fprintf(fh, "\"%s\",", edt->pi.cinfo->col_title[i]);
585
586             fprintf(fh, "\"%s\"\n", edt->pi.cinfo->col_title[i]);
587         }
588
589         for(i=0; i < edt->pi.cinfo->num_cols - 1; i++)
590             fprintf(fh, "\"%s\",", edt->pi.cinfo->col_data[i]);
591
592         fprintf(fh, "\"%s\"\n", edt->pi.cinfo->col_data[i]);
593 }
594
595 void
596 write_csv_finale(FILE *fh _U_)
597 {
598
599 }
600
601 /*
602  * Find the data source for a specified field, and return a pointer
603  * to the data in it. Returns NULL if the data is out of bounds.
604  */
605 static const guint8 *
606 get_field_data(GSList *src_list, field_info *fi)
607 {
608         GSList *src_le;
609         data_source *src;
610         tvbuff_t *src_tvb;
611         gint length, tvbuff_length;
612
613         for (src_le = src_list; src_le != NULL; src_le = src_le->next) {
614                 src = src_le->data;
615                 src_tvb = src->tvb;
616                 if (fi->ds_tvb == src_tvb) {
617                         /*
618                          * Found it.
619                          *
620                          * XXX - a field can have a length that runs past
621                          * the end of the tvbuff.  Ideally, that should
622                          * be fixed when adding an item to the protocol
623                          * tree, but checking the length when doing
624                          * that could be expensive.  Until we fix that,
625                          * we'll do the check here.
626                          */
627                         tvbuff_length = tvb_length_remaining(src_tvb,
628                             fi->start);
629                         if (tvbuff_length < 0) {
630                                 return NULL;
631                         }
632                         length = fi->length;
633                         if (length > tvbuff_length)
634                                 length = tvbuff_length;
635                         return tvb_get_ptr(src_tvb, fi->start, length);
636                 }
637         }
638         g_assert_not_reached();
639         return NULL;    /* not found */
640 }
641
642 /* Print a string, escaping out certain characters that need to
643  * escaped out for XML. */
644 static void
645 print_escaped_xml(FILE *fh, const char *unescaped_string)
646 {
647         const char *p;
648
649         for (p = unescaped_string; *p != '\0'; p++) {
650                 switch (*p) {
651                         case '&':
652                                 fputs("&amp;", fh);
653                                 break;
654                         case '<':
655                                 fputs("&lt;", fh);
656                                 break;
657                         case '>':
658                                 fputs("&gt;", fh);
659                                 break;
660                         case '"':
661                                 fputs("&quot;", fh);
662                                 break;
663                         case '\'':
664                                 fputs("&apos;", fh);
665                                 break;
666                         default:
667                                 fputc(*p, fh);
668                 }
669         }
670 }
671
672 static void
673 write_pdml_field_hex_value(write_pdml_data *pdata, field_info *fi)
674 {
675         int i;
676         const guint8 *pd;
677
678         if (fi->length > tvb_length_remaining(fi->ds_tvb, fi->start)) {
679                 fprintf(pdata->fh, "field length invalid!");
680                 return;
681         }
682
683         /* Find the data for this field. */
684         pd = get_field_data(pdata->src_list, fi);
685
686         if (pd) {
687                 /* Print a simple hex dump */
688                 for (i = 0 ; i < fi->length; i++) {
689                         fprintf(pdata->fh, "%02x", pd[i]);
690                 }
691         }
692 }
693
694 gboolean
695 print_hex_data(print_stream_t *stream, epan_dissect_t *edt)
696 {
697         gboolean multiple_sources;
698         GSList *src_le;
699         data_source *src;
700         tvbuff_t *tvb;
701         char *name;
702         char *line;
703         const guchar *cp;
704         guint length;
705
706         /*
707          * Set "multiple_sources" iff this frame has more than one
708          * data source; if it does, we need to print the name of
709          * the data source before printing the data from the
710          * data source.
711          */
712         multiple_sources = (edt->pi.data_src->next != NULL);
713
714         for (src_le = edt->pi.data_src; src_le != NULL;
715             src_le = src_le->next) {
716                 src = src_le->data;
717                 tvb = src->tvb;
718                 if (multiple_sources) {
719                         name = src->name;
720                         print_line(stream, 0, "");
721                         line = g_malloc(strlen(name) + 2);      /* <name>:\0 */
722                         strcpy(line, name);
723                         strcat(line, ":");
724                         print_line(stream, 0, line);
725                         g_free(line);
726                 }
727                 length = tvb_length(tvb);
728                 if (length == 0)
729                     return TRUE;
730                 cp = tvb_get_ptr(tvb, 0, length);
731                 if (!print_hex_data_buffer(stream, cp, length,
732                     edt->pi.fd->flags.encoding))
733                         return FALSE;
734         }
735         return TRUE;
736 }
737
738 /*
739  * This routine is based on a routine created by Dan Lasley
740  * <DLASLEY@PROMUS.com>.
741  *
742  * It was modified for Wireshark by Gilbert Ramirez and others.
743  */
744
745 #define MAX_OFFSET_LEN  8       /* max length of hex offset of bytes */
746 #define BYTES_PER_LINE  16      /* max byte values printed on a line */
747 #define HEX_DUMP_LEN    (BYTES_PER_LINE*3)
748                                 /* max number of characters hex dump takes -
749                                    2 digits plus trailing blank */
750 #define DATA_DUMP_LEN   (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
751                                 /* number of characters those bytes take;
752                                    3 characters per byte of hex dump,
753                                    2 blanks separating hex from ASCII,
754                                    1 character per byte of ASCII dump */
755 #define MAX_LINE_LEN    (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
756                                 /* number of characters per line;
757                                    offset, 2 blanks separating offset
758                                    from data dump, data dump */
759
760 static gboolean
761 print_hex_data_buffer(print_stream_t *stream, const guchar *cp,
762     guint length, char_enc encoding)
763 {
764         register unsigned int ad, i, j, k, l;
765         guchar c;
766         guchar line[MAX_LINE_LEN + 1];
767         unsigned int use_digits;
768         static guchar binhex[16] = {
769                 '0', '1', '2', '3', '4', '5', '6', '7',
770                 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
771
772         if (!print_line(stream, 0, ""))
773                 return FALSE;
774
775         /*
776          * How many of the leading digits of the offset will we supply?
777          * We always supply at least 4 digits, but if the maximum offset
778          * won't fit in 4 digits, we use as many digits as will be needed.
779          */
780         if (((length - 1) & 0xF0000000) != 0)
781                 use_digits = 8; /* need all 8 digits */
782         else if (((length - 1) & 0x0F000000) != 0)
783                 use_digits = 7; /* need 7 digits */
784         else if (((length - 1) & 0x00F00000) != 0)
785                 use_digits = 6; /* need 6 digits */
786         else if (((length - 1) & 0x000F0000) != 0)
787                 use_digits = 5; /* need 5 digits */
788         else
789                 use_digits = 4; /* we'll supply 4 digits */
790
791         ad = 0;
792         i = 0;
793         j = 0;
794         k = 0;
795         while (i < length) {
796                 if ((i & 15) == 0) {
797                         /*
798                          * Start of a new line.
799                          */
800                         j = 0;
801                         k = 0;
802                         l = use_digits;
803                         do {
804                                 l--;
805                                 c = (ad >> (l*4)) & 0xF;
806                                 line[j++] = binhex[c];
807                         } while (l != 0);
808                         line[j++] = ' ';
809                         line[j++] = ' ';
810                         memset(line+j, ' ', DATA_DUMP_LEN);
811
812                         /*
813                          * Offset in line of ASCII dump.
814                          */
815                         k = j + HEX_DUMP_LEN + 2;
816                 }
817                 c = *cp++;
818                 line[j++] = binhex[c>>4];
819                 line[j++] = binhex[c&0xf];
820                 j++;
821                 if (encoding == CHAR_EBCDIC) {
822                         c = EBCDIC_to_ASCII1(c);
823                 }
824                 line[k++] = c >= ' ' && c < 0x7f ? c : '.';
825                 i++;
826                 if ((i & 15) == 0 || i == length) {
827                         /*
828                          * We'll be starting a new line, or
829                          * we're finished printing this buffer;
830                          * dump out the line we've constructed,
831                          * and advance the offset.
832                          */
833                         line[k] = '\0';
834                         if (!print_line(stream, 0, line))
835                                 return FALSE;
836                         ad += 16;
837                 }
838         }
839         return TRUE;
840 }
841
842 static
843 void ps_clean_string(unsigned char *out, const unsigned char *in,
844                         int outbuf_size)
845 {
846         int rd, wr;
847         char c;
848
849         for (rd = 0, wr = 0 ; wr < outbuf_size; rd++, wr++ ) {
850                 c = in[rd];
851                 switch (c) {
852                         case '(':
853                         case ')':
854                         case '\\':
855                                 out[wr] = '\\';
856                                 out[++wr] = c;
857                                 break;
858
859                         default:
860                                 out[wr] = c;
861                                 break;
862                 }
863
864                 if (c == 0) {
865                         break;
866                 }
867         }
868 }
869
870 /* Some formats need stuff at the beginning of the output */
871 gboolean
872 print_preamble(print_stream_t *self, gchar *filename)
873 {
874         return (self->ops->print_preamble)(self, filename);
875 }
876
877 gboolean
878 print_line(print_stream_t *self, int indent, const char *line)
879 {
880         return (self->ops->print_line)(self, indent, line);
881 }
882
883 /* Insert bookmark */
884 gboolean
885 print_bookmark(print_stream_t *self, const gchar *name, const gchar *title)
886 {
887         return (self->ops->print_bookmark)(self, name, title);
888 }
889
890 gboolean
891 new_page(print_stream_t *self)
892 {
893         return (self->ops->new_page)(self);
894 }
895
896 /* Some formats need stuff at the end of the output */
897 gboolean
898 print_finale(print_stream_t *self)
899 {
900         return (self->ops->print_finale)(self);
901 }
902
903 gboolean
904 destroy_print_stream(print_stream_t *self)
905 {
906         return (self->ops->destroy)(self);
907 }
908
909 typedef struct {
910         int to_file;
911         FILE *fh;
912 } output_text;
913
914 static gboolean
915 print_preamble_text(print_stream_t *self _U_, gchar *filename _U_)
916 {
917         /* do nothing */
918         return TRUE;    /* always succeeds */
919 }
920
921 static gboolean
922 print_line_text(print_stream_t *self, int indent, const char *line)
923 {
924         output_text *output = self->data;
925         char space[MAX_INDENT+1];
926         int i;
927         int num_spaces;
928
929         /* Prepare the tabs for printing, depending on tree level */
930         num_spaces = indent * 4;
931         if (num_spaces > MAX_INDENT) {
932                 num_spaces = MAX_INDENT;
933         }
934         for (i = 0; i < num_spaces; i++) {
935                 space[i] = ' ';
936         }
937         /* The string is NUL-terminated */
938         space[num_spaces] = '\0';
939
940         fputs(space, output->fh);
941         fputs(line, output->fh);
942         putc('\n', output->fh);
943         return !ferror(output->fh);
944 }
945
946 static gboolean
947 print_bookmark_text(print_stream_t *self _U_, const gchar *name _U_,
948     const gchar *title _U_)
949 {
950         /* do nothing */
951         return TRUE;
952 }
953
954 static gboolean
955 new_page_text(print_stream_t *self)
956 {
957         output_text *output = self->data;
958
959         fputs("\f", output->fh);
960         return !ferror(output->fh);
961 }
962
963 static gboolean
964 print_finale_text(print_stream_t *self _U_)
965 {
966         /* do nothing */
967         return TRUE;    /* always succeeds */
968 }
969
970 static gboolean
971 destroy_text(print_stream_t *self)
972 {
973         output_text *output = self->data;
974         gboolean ret;
975
976         ret = close_print_dest(output->to_file, output->fh);
977         g_free(output);
978         g_free(self);
979         return ret;
980 }
981
982 static const print_stream_ops_t print_text_ops = {
983         print_preamble_text,
984         print_line_text,
985         print_bookmark_text,
986         new_page_text,
987         print_finale_text,
988         destroy_text
989 };
990
991 print_stream_t *
992 print_stream_text_new(int to_file, const char *dest)
993 {
994         FILE *fh;
995         print_stream_t *stream;
996         output_text *output;
997
998         fh = open_print_dest(to_file, dest);
999         if (fh == NULL)
1000                 return NULL;
1001
1002         output = g_malloc(sizeof *output);
1003         output->to_file = to_file;
1004         output->fh = fh;
1005         stream = g_malloc(sizeof (print_stream_t));
1006         stream->ops = &print_text_ops;
1007         stream->data = output;
1008
1009         return stream;
1010 }
1011
1012 print_stream_t *
1013 print_stream_text_stdio_new(FILE *fh)
1014 {
1015         print_stream_t *stream;
1016         output_text *output;
1017
1018         output = g_malloc(sizeof *output);
1019         output->to_file = TRUE;
1020         output->fh = fh;
1021         stream = g_malloc(sizeof (print_stream_t));
1022         stream->ops = &print_text_ops;
1023         stream->data = output;
1024
1025         return stream;
1026 }
1027
1028 typedef struct {
1029         int to_file;
1030         FILE *fh;
1031 } output_ps;
1032
1033 static gboolean
1034 print_preamble_ps(print_stream_t *self, gchar *filename)
1035 {
1036         output_ps *output = self->data;
1037         unsigned char psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */
1038
1039         print_ps_preamble(output->fh);
1040
1041         fputs("%% Set the font to 10 point\n", output->fh);
1042         fputs("/Courier findfont 10 scalefont setfont\n", output->fh);
1043         fputs("\n", output->fh);
1044         fputs("%% the page title\n", output->fh);
1045         ps_clean_string(psbuffer, filename, MAX_PS_LINE_LENGTH);
1046         fprintf(output->fh, "/eth_pagetitle (%s - Wireshark) def\n", psbuffer);
1047         fputs("\n", output->fh);
1048         return !ferror(output->fh);
1049 }
1050
1051 static gboolean
1052 print_line_ps(print_stream_t *self, int indent, const char *line)
1053 {
1054         output_ps *output = self->data;
1055         unsigned char psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */
1056
1057         ps_clean_string(psbuffer, line, MAX_PS_LINE_LENGTH);
1058         fprintf(output->fh, "%d (%s) putline\n", indent, psbuffer);
1059         return !ferror(output->fh);
1060 }
1061
1062 static gboolean
1063 print_bookmark_ps(print_stream_t *self, const gchar *name, const gchar *title)
1064 {
1065         output_ps *output = self->data;
1066         unsigned char psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */
1067
1068         /*
1069          * See the Adobe "pdfmark reference":
1070          *
1071          *      http://partners.adobe.com/asn/acrobat/docs/pdfmark.pdf
1072          *
1073          * The pdfmark stuff tells code that turns PostScript into PDF
1074          * things that it should do.
1075          *
1076          * The /OUT stuff creates a bookmark that goes to the
1077          * destination with "name" as the name and "title" as the title.
1078          *
1079          * The "/DEST" creates the destination.
1080          */
1081         ps_clean_string(psbuffer, title, MAX_PS_LINE_LENGTH);
1082         fprintf(output->fh, "[/Dest /%s /Title (%s)   /OUT pdfmark\n", name,
1083             psbuffer);
1084         fputs("[/View [/XYZ -4 currentpoint matrix currentmatrix matrix defaultmatrix\n",
1085             output->fh);
1086         fputs("matrix invertmatrix matrix concatmatrix transform exch pop 20 add null]\n",
1087             output->fh);
1088         fprintf(output->fh, "/Dest /%s /DEST pdfmark\n", name);
1089         return !ferror(output->fh);
1090 }
1091
1092 static gboolean
1093 new_page_ps(print_stream_t *self)
1094 {
1095         output_ps *output = self->data;
1096
1097         fputs("formfeed\n", output->fh);
1098         return !ferror(output->fh);
1099 }
1100
1101 static gboolean
1102 print_finale_ps(print_stream_t *self)
1103 {
1104         output_ps *output = self->data;
1105
1106         print_ps_finale(output->fh);
1107         return !ferror(output->fh);
1108 }
1109
1110 static gboolean
1111 destroy_ps(print_stream_t *self)
1112 {
1113         output_ps *output = self->data;
1114         gboolean ret;
1115
1116         ret = close_print_dest(output->to_file, output->fh);
1117         g_free(output);
1118         g_free(self);
1119         return ret;
1120 }
1121
1122 static const print_stream_ops_t print_ps_ops = {
1123         print_preamble_ps,
1124         print_line_ps,
1125         print_bookmark_ps,
1126         new_page_ps,
1127         print_finale_ps,
1128         destroy_ps
1129 };
1130
1131 print_stream_t *
1132 print_stream_ps_new(int to_file, const char *dest)
1133 {
1134         FILE *fh;
1135         print_stream_t *stream;
1136         output_ps *output;
1137
1138         fh = open_print_dest(to_file, dest);
1139         if (fh == NULL)
1140                 return NULL;
1141
1142         output = g_malloc(sizeof *output);
1143         output->to_file = to_file;
1144         output->fh = fh;
1145         stream = g_malloc(sizeof (print_stream_t));
1146         stream->ops = &print_ps_ops;
1147         stream->data = output;
1148
1149         return stream;
1150 }
1151
1152 print_stream_t *
1153 print_stream_ps_stdio_new(FILE *fh)
1154 {
1155         print_stream_t *stream;
1156         output_ps *output;
1157
1158         output = g_malloc(sizeof *output);
1159         output->to_file = TRUE;
1160         output->fh = fh;
1161         stream = g_malloc(sizeof (print_stream_t));
1162         stream->ops = &print_ps_ops;
1163         stream->data = output;
1164
1165         return stream;
1166 }
1167
1168 output_fields_t* output_fields_new()
1169 {
1170     output_fields_t* fields = g_new(output_fields_t, 1);
1171     fields->print_header = FALSE;
1172     fields->separator = '\t';
1173     fields->fields = NULL; /*Do lazy initialisation */
1174     fields->field_indicies = NULL;
1175     fields->field_values = NULL;
1176     fields->quote='\0'; 
1177     return fields;
1178 }
1179
1180 gsize output_fields_num_fields(output_fields_t* fields)
1181 {
1182     g_assert(fields);
1183
1184     if(NULL == fields->fields) {
1185         return 0;
1186     } else {
1187         return fields->fields->len;
1188     }
1189 }
1190
1191 void output_fields_free(output_fields_t* fields)
1192 {
1193     g_assert(fields);
1194
1195     if(NULL != fields->field_indicies) {
1196         /* Keys are stored in fields->fields, values are
1197          * integers.
1198          */
1199         g_hash_table_destroy(fields->field_indicies);
1200     }
1201     if(NULL != fields->fields) {
1202         gsize i;
1203         for(i = 0; i < fields->fields->len; ++i) {
1204             gchar* field = g_ptr_array_index(fields->fields,i);
1205             g_free(field);
1206         }
1207         g_ptr_array_free(fields->fields, TRUE);
1208     }
1209
1210     g_free(fields);    
1211 }
1212
1213 void output_fields_add(output_fields_t* fields, const gchar* field)
1214 {
1215     gchar* field_copy;
1216
1217     g_assert(fields);
1218     g_assert(field);
1219
1220
1221     if(NULL == fields->fields) {
1222         fields->fields = g_ptr_array_new();
1223     }
1224
1225     field_copy = g_strdup(field);
1226
1227     g_ptr_array_add(fields->fields, field_copy);
1228 }
1229
1230 gboolean output_fields_set_option(output_fields_t* info, gchar* option)
1231 {
1232     const gchar* option_name;
1233     const gchar* option_value;
1234
1235     g_assert(info);
1236     g_assert(option);
1237
1238     if('\0' == *option) {
1239         return FALSE; /* Is this guarded against by option parsing? */
1240     }
1241     option_name = strtok(option,"=");
1242     option_value = option + strlen(option_name) + 1;
1243     if(0 == strcmp(option_name, "header")) {
1244         switch(NULL == option_value ? '\0' : *option_value) {
1245         case 'n':
1246             info->print_header = FALSE;
1247             break;
1248         case 'y':
1249             info->print_header = TRUE;
1250             break;
1251         default:
1252             return FALSE;
1253         }
1254         return TRUE;
1255     }
1256
1257     if(0 == strcmp(option_name,"separator")) {
1258         switch(NULL == option_value ? '\0' : *option_value) {
1259         case '\0':
1260             return FALSE;
1261         case '/':
1262             switch(*++option_value) {
1263             case 't':
1264                 info->separator = '\t';
1265                 break;
1266             case 's':
1267                 info->separator = ' ';
1268                 break;
1269             default:
1270                 info->separator = '\\';
1271             }
1272             break;
1273         default:
1274             info->separator = *option_value;
1275             break;
1276         }
1277         return TRUE;       
1278     }
1279
1280     if(0 == strcmp(option_name, "quote")) {
1281         switch(NULL == option_value ? '\0' : *option_value) {
1282         default: /* Fall through */
1283         case '\0':
1284             info->quote='\0';
1285             return FALSE;
1286         case 'd':
1287             info->quote='"';
1288             break;
1289         case 's':
1290             info->quote='\'';
1291             break;
1292         case 'n':
1293             info->quote='\0';
1294             break;
1295         }
1296         return TRUE;        
1297     }
1298
1299     return FALSE;
1300 }
1301
1302 void output_fields_list_options(FILE *fh)
1303 {
1304     fprintf(fh, "TShark: The available options for field output \"E\" are:\n");
1305     fputs("header=y|n   Print field abbreviations as first line of output (def: N: no)\n", fh);
1306     fputs("separator=/t|/s|<character>   Set the separator to use; \"/t\" = tab,\n \"/s\" = space (def: /t: tab)\n", fh);
1307     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);
1308 }
1309
1310
1311 void write_fields_preamble(output_fields_t* fields, FILE *fh)
1312 {
1313     gsize i;
1314
1315     g_assert(fields);
1316     g_assert(fh);
1317
1318     if(!fields->print_header) {
1319         return;
1320     }
1321
1322     for(i = 0; i < fields->fields->len; ++i) {
1323         const gchar* field = g_ptr_array_index(fields->fields,i);
1324         if(i != 0 ) {
1325             fputc(fields->separator, fh);
1326         }
1327         fputs(field, fh);
1328     }    
1329     fputc('\n', fh);
1330 }
1331
1332 static void proto_tree_get_node_field_values(proto_node *node, gpointer data) 
1333 {
1334     write_field_data_t *call_data;
1335     field_info *fi;
1336     gpointer field_index;
1337
1338     call_data = data;
1339     fi = PITEM_FINFO(node);
1340
1341     field_index = g_hash_table_lookup(call_data->fields->field_indicies, fi->hfinfo->abbrev);
1342     if(NULL != field_index) {
1343         const gchar* value;
1344
1345         value = get_node_field_value(fi, call_data->edt); /* ep_alloced string */
1346
1347         if(NULL != value && '\0' != *value) {
1348             guint actual_index;
1349             actual_index = GPOINTER_TO_UINT(field_index);
1350             /* Unwrap change made to disambiguiate zero / null */
1351             call_data->fields->field_values[actual_index - 1] = value;
1352         }
1353     }
1354     
1355     /* Recurse here. */
1356     if (node->first_child != NULL) {
1357         proto_tree_children_foreach(node, proto_tree_get_node_field_values,
1358                                     call_data);
1359     }
1360 }
1361
1362 void proto_tree_write_fields(output_fields_t* fields, epan_dissect_t *edt, FILE *fh)
1363 {
1364     gsize i;
1365
1366     write_field_data_t data;
1367
1368     g_assert(fields);
1369     g_assert(edt);
1370     g_assert(fh);
1371
1372     data.fields = fields;
1373     data.edt = edt;
1374
1375     if(NULL == fields->field_indicies) {
1376         /* Prepare a lookup table from string abbreviation for field to its index. */
1377         fields->field_indicies = g_hash_table_new(g_str_hash, g_str_equal);
1378
1379         i = 0;
1380         while( i < fields->fields->len) {
1381             gchar* field = g_ptr_array_index(fields->fields, i);
1382              /* Store field indicies +1 so that zero is not a valid value, 
1383               * and can be distinguished from NULL as a pointer.
1384               */
1385             ++i;
1386             g_hash_table_insert(fields->field_indicies, field, GUINT_TO_POINTER(i));            
1387         }
1388     }
1389
1390     /* Buffer to store values for this packet */
1391     fields->field_values = ep_alloc_array0(const gchar*, fields->fields->len);
1392     
1393     proto_tree_children_foreach(edt->tree, proto_tree_get_node_field_values,
1394                                 &data);
1395
1396     for(i = 0; i < fields->fields->len; ++i) {
1397         if(0 != i) {
1398             fputc(fields->separator, fh);
1399         }
1400         if(NULL != fields->field_values[i]) {
1401             if(fields->quote != '\0') {
1402                 fputc(fields->quote, fh);
1403             }
1404             fputs(fields->field_values[i], fh);
1405             if(fields->quote != '\0') {
1406                 fputc(fields->quote, fh);
1407             }
1408         }
1409     }
1410 }
1411
1412 void write_fields_finale(output_fields_t* fields _U_ , FILE *fh _U_)
1413 {
1414     /* Nothing to do */
1415 }
1416
1417 /* Returns an ep_alloced string or a static constant*/
1418 static const gchar* get_node_field_value(field_info* fi, epan_dissect_t* edt)
1419 {
1420     if (fi->hfinfo->id == hf_text_only) {
1421         /* Text label.
1422          * Get the text */
1423         if (fi->rep) {
1424             return fi->rep->representation;
1425         }
1426         else {
1427             return get_field_hex_value(edt->pi.data_src, fi);
1428         }
1429     }
1430     else if (fi->hfinfo->id == proto_data) {
1431         /* Uninterpreted data, i.e., the "Data" protocol, is
1432          * printed as a field instead of a protocol. */
1433         return get_field_hex_value(edt->pi.data_src, fi);
1434     }
1435     else {
1436         /* Normal protocols and fields */
1437         gchar      *dfilter_string;
1438         gint        chop_len;
1439
1440         switch (fi->hfinfo->type)
1441         {
1442         case FT_PROTOCOL:
1443             /* Print out the full details for the protocol. */
1444             if (fi->rep) {
1445                 return fi->rep->representation;
1446             } else {
1447                 /* Just print out the protocol abbreviation */
1448                 return fi->hfinfo->abbrev;;
1449             }
1450         case FT_NONE:
1451             /* Return "1" so that the presence of a field of type
1452              * FT_NONE can be checked when using -T fields */
1453             return "1";
1454         default:
1455             /* XXX - this is a hack until we can just call
1456              * fvalue_to_string_repr() for *all* FT_* types. */
1457             dfilter_string = proto_construct_match_selected_string(fi,
1458                 edt);
1459             if (dfilter_string != NULL) {
1460                 chop_len = strlen(fi->hfinfo->abbrev) + 4; /* for " == " */
1461
1462                 /* XXX - Remove double-quotes. Again, once we
1463                  * can call fvalue_to_string_repr(), we can
1464                  * ask it not to produce the version for
1465                  * display-filters, and thus, no
1466                  * double-quotes. */
1467                 if (dfilter_string[strlen(dfilter_string)-1] == '"') {
1468                     dfilter_string[strlen(dfilter_string)-1] = '\0';
1469                     chop_len++;
1470                 }
1471
1472                 return &(dfilter_string[chop_len]);
1473             } else {
1474                 return get_field_hex_value(edt->pi.data_src, fi);
1475             }
1476         }
1477     }
1478 }
1479
1480 static const gchar*
1481 get_field_hex_value(GSList* src_list, field_info *fi)
1482 {
1483     const guint8 *pd;
1484
1485     if (fi->length > tvb_length_remaining(fi->ds_tvb, fi->start)) {
1486         return "field length invalid!";
1487     }
1488
1489     /* Find the data for this field. */
1490     pd = get_field_data(src_list, fi);
1491
1492     if (pd) {
1493         int i;
1494         gchar* buffer;
1495         gchar* p;
1496         int len;
1497         const int chars_per_byte = 2;
1498
1499         len = chars_per_byte * fi->length;
1500         buffer = ep_alloc_array(gchar, len + 1);
1501         buffer[len] = '\0'; /* Ensure NULL termination in bad cases */
1502         p = buffer;
1503         /* Print a simple hex dump */
1504         for (i = 0 ; i < fi->length; i++) {
1505             g_snprintf(p, len, "%02x", pd[i]);
1506             p += chars_per_byte;
1507             len -= chars_per_byte;
1508         }
1509         return buffer;
1510     } else {
1511         return NULL;
1512     }
1513 }