print system enhanced, more print ranges and expanded states
[obnox/wireshark/wip.git] / print.c
1 /* print.c
2  * Routines for printing packet analysis trees.
3  *
4  * $Id: print.c,v 1.66 2003/12/09 22:27:28 ulfl Exp $
5  *
6  * Gilbert Ramirez <gram@alumni.rice.edu>
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@ethereal.com>
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
39 #include "print.h"
40 #include "ps.h"
41 #include "util.h"
42 #include "packet-data.h"
43 #include "packet-frame.h"
44
45 #define PDML_VERSION "0"
46
47 static void proto_tree_print_node(proto_node *node, gpointer data);
48 static void proto_tree_print_node_pdml(proto_node *node, gpointer data);
49 static void print_hex_data_buffer(FILE *fh, register const guchar *cp,
50     register guint length, char_enc encoding, gint format);
51 static void ps_clean_string(unsigned char *out, const unsigned char *in,
52                         int outbuf_size);
53
54 typedef struct {
55         int                         level;
56         FILE                    *fh;
57         GSList                  *src_list;
58         print_dissections_e print_dissections;
59         gboolean                print_hex_for_data;
60         char_enc                encoding;
61         gint                    format;
62         epan_dissect_t      *edt;
63 } print_data;
64
65 static void print_pdml_geninfo(proto_tree *tree, print_data *pdata);
66
67 FILE *open_print_dest(int to_file, const char *dest)
68 {
69         FILE    *fh;
70
71         /* Open the file or command for output */
72         if (to_file)
73                 fh = fopen(dest, "w");
74         else
75                 fh = popen(dest, "w");
76
77         return fh;
78 }
79
80 void close_print_dest(int to_file, FILE *fh)
81 {
82         /* Close the file or command */
83         if (to_file)
84                 fclose(fh);
85         else
86                 pclose(fh);
87 }
88
89
90 void
91 proto_tree_print(print_args_t *print_args, epan_dissect_t *edt,
92     FILE *fh)
93 {
94         print_data data;
95
96         /* Create the output */
97         data.level = 0;
98         data.fh = fh;
99         data.src_list = edt->pi.data_src;
100         data.encoding = edt->pi.fd->flags.encoding;
101         data.print_dissections = print_args->print_dissections;
102         data.print_hex_for_data = !print_args->print_hex;
103             /* If we're printing the entire packet in hex, don't
104                print uninterpreted data fields in hex as well. */
105         data.format = print_args->format;
106         data.edt = edt;
107
108         if (data.format == PR_FMT_PDML) {
109
110                 fprintf(fh, "<packet>\n");
111
112                 /* Print a "geninfo" protocol as required by PDML */
113                 print_pdml_geninfo(edt->tree, &data);
114
115                 proto_tree_children_foreach(edt->tree, proto_tree_print_node_pdml, &data);
116
117                 fprintf(fh, "</packet>\n\n");
118         }
119         else {
120                 proto_tree_children_foreach(edt->tree, proto_tree_print_node, &data);
121         }
122 }
123
124 /*
125  * Find the data source for a specified field, and return a pointer
126  * to the data in it.
127  */
128 static const guint8 *
129 get_field_data(GSList *src_list, field_info *fi)
130 {
131         GSList *src_le;
132         data_source *src;
133         tvbuff_t *src_tvb;
134
135         for (src_le = src_list; src_le != NULL; src_le = src_le->next) {
136                 src = src_le->data;
137                 src_tvb = src->tvb;
138                 if (fi->ds_tvb == src_tvb) {
139                         /*
140                          * Found it.
141                          */
142                         return tvb_get_ptr(src_tvb, fi->start, fi->length);
143                 }
144         }
145         g_assert_not_reached();
146         return NULL;    /* not found */
147 }
148
149 #define MAX_INDENT      160
150
151 #define MAX_PS_LINE_LENGTH 256
152
153 /* Print a tree's data, and any child nodes. */
154 static
155 void proto_tree_print_node(proto_node *node, gpointer data)
156 {
157         field_info      *fi = PITEM_FINFO(node);
158         print_data      *pdata = (print_data*) data;
159         const guint8    *pd;
160         gchar           label_str[ITEM_LABEL_LENGTH];
161         gchar           *label_ptr;
162
163         /* Don't print invisible entries. */
164         if (!fi->visible)
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         print_line(pdata->fh, pdata->level, pdata->format, label_ptr);
177
178         /* If it's uninterpreted data, dump it (unless our caller will
179            be printing the entire packet in hex). */
180         if (fi->hfinfo->id == proto_data && pdata->print_hex_for_data) {
181                 /*
182                  * Find the data for this field.
183                  */
184                 pd = get_field_data(pdata->src_list, fi);
185                 print_hex_data_buffer(pdata->fh, pd, fi->length,
186                     pdata->encoding, pdata->format);
187     }
188
189         /* If we're printing all levels, or if this node is one with a
190            subtree and its subtree is expanded, recurse into the subtree,
191            if it exists. */
192         g_assert(fi->tree_type >= -1 && fi->tree_type < num_tree_types);
193         if (pdata->print_dissections == print_dissections_expanded ||
194             (pdata->print_dissections == print_dissections_as_displayed &&
195         fi->tree_type >= 0 && tree_is_expanded[fi->tree_type])) {
196                 if (node->first_child != NULL) {
197                         pdata->level++;
198                         proto_tree_children_foreach(node,
199                                 proto_tree_print_node, pdata);
200                         pdata->level--;
201                 }
202         }
203 }
204
205 /* Print a string, escaping out certain characters that need to
206  * escaped out for XML. */
207 static void
208 print_escaped_xml(FILE *fh, char *unescaped_string)
209 {
210         unsigned char *p;
211
212         for (p = unescaped_string; *p != '\0'; p++) {
213                 switch (*p) {
214                         case '&':
215                                 fputs("&amp;", fh);
216                                 break;
217                         case '<':
218                                 fputs("&lt;", fh);
219                                 break;
220                         case '>':
221                                 fputs("&gt;", fh);
222                                 break;
223                         case '"':
224                                 fputs("&quot;", fh);
225                                 break;
226                         default:
227                                 fputc(*p, fh);
228                 }
229         }
230 }
231
232 static void
233 print_field_hex_value(print_data *pdata, field_info *fi)
234 {
235         int i;
236         const guint8 *pd;
237
238         /* Find the data for this field. */
239         pd = get_field_data(pdata->src_list, fi);
240
241         /* Print a simple hex dump */
242         for (i = 0 ; i < fi->length; i++) {
243                 fprintf(pdata->fh, "%02x", pd[i]);
244         }
245 }
246
247
248 /* Print a tree's data, and any child nodes, as PDML */
249 static void
250 proto_tree_print_node_pdml(proto_node *node, gpointer data)
251 {
252         field_info      *fi = PITEM_FINFO(node);
253         print_data      *pdata = (print_data*) data;
254         gchar           *label_ptr;
255         gchar           label_str[ITEM_LABEL_LENGTH];
256         char            *dfilter_string;
257         int             chop_len;
258         int             i;
259
260         for (i = -1; i < pdata->level; i++) {
261                 fputs("  ", pdata->fh);
262         }
263
264         /* Text label. It's printed as a field with no name. */
265         if (fi->hfinfo->id == hf_text_only) {
266                 /* Get the text */
267                 if (fi->rep) {
268                         label_ptr = fi->rep->representation;
269                 }
270                 else {
271                         label_ptr = "";
272                 }
273
274                 fputs("<field show=\"", pdata->fh);
275                 print_escaped_xml(pdata->fh, label_ptr);
276
277                 fprintf(pdata->fh, "\" size=\"%d", fi->length);
278                 fprintf(pdata->fh, "\" pos=\"%d", fi->start);
279
280                 fputs("\" value=\"", pdata->fh);
281                 print_field_hex_value(pdata, fi);
282
283                 if (node->first_child != NULL) {
284                         fputs("\">\n", pdata->fh);
285                 }
286                 else {
287                         fputs("\"/>\n", pdata->fh);
288                 }
289         }
290         /* Uninterpreted data, i.e., the "Data" protocol, is
291          * printed as a field instead of a protocol. */
292         else if (fi->hfinfo->id == proto_data) {
293
294                 fputs("<field name=\"data\" value=\"", pdata->fh);
295
296                 print_field_hex_value(pdata, fi);
297
298                 fputs("\"/>\n", pdata->fh);
299
300         }
301         /* Normal protocols and fields */
302         else {
303                 if (fi->hfinfo->type == FT_PROTOCOL) {
304                         fputs("<proto name=\"", pdata->fh);
305                 }
306                 else {
307                         fputs("<field name=\"", pdata->fh);
308                 }
309                 print_escaped_xml(pdata->fh, fi->hfinfo->abbrev);
310
311                 if (fi->rep) {
312                         fputs("\" showname=\"", pdata->fh);
313                         print_escaped_xml(pdata->fh, fi->rep->representation);
314                 }
315                 else {
316                         label_ptr = label_str;
317                         proto_item_fill_label(fi, label_str);
318                         fputs("\" showname=\"", pdata->fh);
319                         print_escaped_xml(pdata->fh, label_ptr);
320                 }
321
322                 fprintf(pdata->fh, "\" size=\"%d", fi->length);
323                 fprintf(pdata->fh, "\" pos=\"%d", fi->start);
324 /*              fprintf(pdata->fh, "\" id=\"%d", fi->hfinfo->id);*/
325
326                 if (fi->hfinfo->type != FT_PROTOCOL) {
327                         /* Field */
328
329                         /* XXX - this is a hack until we can juse call
330                          * fvalue_to_string_repr() for *all* FT_* types. */
331                         dfilter_string = proto_construct_dfilter_string(fi,
332                                         pdata->edt);
333                         if (dfilter_string != NULL) {
334                                 chop_len = strlen(fi->hfinfo->abbrev) + 4; /* for " == " */
335
336                                 /* XXX - Remove double-quotes. Again, once we
337                                  * can call fvalue_to_string_repr(), we can
338                                  * ask it not to produce the version for
339                                  * display-filters, and thus, no
340                                  * double-quotes. */
341                                 if (dfilter_string[strlen(dfilter_string)-1] == '"') {
342                                         dfilter_string[strlen(dfilter_string)-1] = '\0';
343                                         chop_len++;
344                                 }
345
346                                 fputs("\" show=\"", pdata->fh);
347                                 print_escaped_xml(pdata->fh, &dfilter_string[chop_len]);
348                         }
349                         if (fi->length > 0) {
350                                 fputs("\" value=\"", pdata->fh);
351                                 print_field_hex_value(pdata, fi);
352                         }
353                 }
354
355                 if (node->first_child != NULL) {
356                         fputs("\">\n", pdata->fh);
357                 }
358                 else if (fi->hfinfo->id == proto_data) {
359                         fputs("\">\n", pdata->fh);
360                 }
361                 else {
362                         fputs("\"/>\n", pdata->fh);
363                 }
364         }
365
366         /* We always pring all levels for PDML. Recurse here. */
367         if (node->first_child != NULL) {
368                 pdata->level++;
369                 proto_tree_children_foreach(node,
370                                 proto_tree_print_node_pdml, pdata);
371                 pdata->level--;
372         }
373
374         if (node->first_child != NULL) {
375                 for (i = -1; i < pdata->level; i++) {
376                         fputs("  ", pdata->fh);
377                 }
378                 if (fi->hfinfo->type == FT_PROTOCOL) {
379                         fputs("</proto>\n", pdata->fh);
380                 }
381                 else {
382                         fputs("</field>\n", pdata->fh);
383                 }
384         }
385 }
386
387 /* Print info for a 'geninfo' pseudo-protocol. This is required by
388  * the PDML spec. The information is contained in Ethereal's 'frame' protocol,
389  * but we produce a 'geninfo' protocol in the PDML to conform to spec.
390  * The 'frame' protocol follows the 'geninfo' protocol in the PDML. */
391 static void
392 print_pdml_geninfo(proto_tree *tree, print_data *pdata)
393 {
394         guint32 num, len, caplen;
395         nstime_t *timestamp;
396         GPtrArray *finfo_array;
397         field_info *frame_finfo;
398
399         /* Get frame protocol's finfo. */
400         finfo_array = proto_find_finfo(tree, proto_frame);
401         if (g_ptr_array_len(finfo_array) < 1) {
402                 return;
403         }
404         frame_finfo = finfo_array->pdata[0];
405         g_ptr_array_free(finfo_array, FALSE);
406
407         /* frame.number --> geninfo.num */
408         finfo_array = proto_find_finfo(tree, hf_frame_number);
409         if (g_ptr_array_len(finfo_array) < 1) {
410                 return;
411         }
412         num = fvalue_get_integer(&((field_info*)finfo_array->pdata[0])->value);
413         g_ptr_array_free(finfo_array, FALSE);
414
415         /* frame.pkt_len --> geninfo.len */
416         finfo_array = proto_find_finfo(tree, hf_frame_packet_len);
417         if (g_ptr_array_len(finfo_array) < 1) {
418                 return;
419         }
420         len = fvalue_get_integer(&((field_info*)finfo_array->pdata[0])->value);
421         g_ptr_array_free(finfo_array, FALSE);
422
423         /* frame.cap_len --> geninfo.caplen */
424         finfo_array = proto_find_finfo(tree, hf_frame_capture_len);
425         if (g_ptr_array_len(finfo_array) < 1) {
426                 return;
427         }
428         caplen = fvalue_get_integer(&((field_info*)finfo_array->pdata[0])->value);
429         g_ptr_array_free(finfo_array, FALSE);
430
431         /* frame.time --> geninfo.timestamp */
432         finfo_array = proto_find_finfo(tree, hf_frame_arrival_time);
433         if (g_ptr_array_len(finfo_array) < 1) {
434                 return;
435         }
436         timestamp = fvalue_get(&((field_info*)finfo_array->pdata[0])->value);
437         g_ptr_array_free(finfo_array, FALSE);
438
439         /* Print geninfo start */
440         fprintf(pdata->fh,
441 "  <proto name=\"geninfo\" pos=\"1\" showname=\"General information\" size=\"%u\">\n",
442                 frame_finfo->length);
443
444         /* Print geninfo.num */
445         fprintf(pdata->fh,
446 "    <field name=\"num\" pos=\"1\" show=\"%u\" showname=\"Number\" value=\"%x\" size=\"%u\"/>\n",
447                 num, num, frame_finfo->length);
448
449         /* Print geninfo.len */
450         fprintf(pdata->fh,
451 "    <field name=\"len\" pos=\"1\" show=\"%u\" showname=\"Packet Length\" value=\"%x\" size=\"%u\"/>\n",
452                 len, len, frame_finfo->length);
453
454         /* Print geninfo.caplen */
455         fprintf(pdata->fh,
456 "    <field name=\"caplen\" pos=\"1\" show=\"%u\" showname=\"Captured Length\" value=\"%x\" size=\"%u\"/>\n",
457                 caplen, caplen, frame_finfo->length);
458
459         /* Print geninfo.timestamp */
460         fprintf(pdata->fh,
461 "    <field name=\"timestamp\" pos=\"1\" show=\"%s\" showname=\"Captured Time\" value=\"%d.%09d\" size=\"%u\"/>\n",
462                 abs_time_to_str(timestamp), (int) timestamp->secs, timestamp->nsecs, frame_finfo->length);
463
464         /* Print geninfo end */
465         fprintf(pdata->fh,
466 "  </proto>\n");
467 }
468
469
470 void print_hex_data(FILE *fh, gint format, epan_dissect_t *edt)
471 {
472         gboolean multiple_sources;
473         GSList *src_le;
474         data_source *src;
475         tvbuff_t *tvb;
476         char *name;
477         char *line;
478         const guchar *cp;
479         guint length;
480
481         /*
482          * Set "multiple_sources" iff this frame has more than one
483          * data source; if it does, we need to print the name of
484          * the data source before printing the data from the
485          * data source.
486          */
487         multiple_sources = (edt->pi.data_src->next != NULL);
488
489         for (src_le = edt->pi.data_src; src_le != NULL;
490             src_le = src_le->next) {
491                 src = src_le->data;
492                 tvb = src->tvb;
493                 if (multiple_sources) {
494                         name = src->name;
495                         print_line(fh, 0, format, "");
496                         line = g_malloc(strlen(name) + 2);      /* <name>:\0 */
497                         strcpy(line, name);
498                         strcat(line, ":");
499                         print_line(fh, 0, format, line);
500                         g_free(line);
501                 }
502                 length = tvb_length(tvb);
503                 cp = tvb_get_ptr(tvb, 0, length);
504                 print_hex_data_buffer(fh, cp, length,
505                     edt->pi.fd->flags.encoding, format);
506         }
507 }
508
509 /*
510  * This routine is based on a routine created by Dan Lasley
511  * <DLASLEY@PROMUS.com>.
512  *
513  * It was modified for Ethereal by Gilbert Ramirez and others.
514  */
515
516 #define MAX_OFFSET_LEN  8       /* max length of hex offset of bytes */
517 #define BYTES_PER_LINE  16      /* max byte values printed on a line */
518 #define HEX_DUMP_LEN    (BYTES_PER_LINE*3)
519                                 /* max number of characters hex dump takes -
520                                    2 digits plus trailing blank */
521 #define DATA_DUMP_LEN   (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
522                                 /* number of characters those bytes take;
523                                    3 characters per byte of hex dump,
524                                    2 blanks separating hex from ASCII,
525                                    1 character per byte of ASCII dump */
526 #define MAX_LINE_LEN    (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
527                                 /* number of characters per line;
528                                    offset, 2 blanks separating offset
529                                    from data dump, data dump */
530
531 static void
532 print_hex_data_buffer(FILE *fh, register const guchar *cp,
533     register guint length, char_enc encoding, gint format)
534 {
535         register unsigned int ad, i, j, k, l;
536         guchar c;
537         guchar line[MAX_LINE_LEN + 1];
538         unsigned int use_digits;
539         static guchar binhex[16] = {
540                 '0', '1', '2', '3', '4', '5', '6', '7',
541                 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
542
543         print_line(fh, 0, format, "");
544
545         /*
546          * How many of the leading digits of the offset will we supply?
547          * We always supply at least 4 digits, but if the maximum offset
548          * won't fit in 4 digits, we use as many digits as will be needed.
549          */
550         if (((length - 1) & 0xF0000000) != 0)
551                 use_digits = 8; /* need all 8 digits */
552         else if (((length - 1) & 0x0F000000) != 0)
553                 use_digits = 7; /* need 7 digits */
554         else if (((length - 1) & 0x00F00000) != 0)
555                 use_digits = 6; /* need 6 digits */
556         else if (((length - 1) & 0x000F0000) != 0)
557                 use_digits = 5; /* need 5 digits */
558         else
559                 use_digits = 4; /* we'll supply 4 digits */
560
561         ad = 0;
562         i = 0;
563         j = 0;
564         k = 0;
565         while (i < length) {
566                 if ((i & 15) == 0) {
567                         /*
568                          * Start of a new line.
569                          */
570                         j = 0;
571                         k = 0;
572                         l = use_digits;
573                         do {
574                                 l--;
575                                 c = (ad >> (l*4)) & 0xF;
576                                 line[j++] = binhex[c];
577                         } while (l != 0);
578                         line[j++] = ' ';
579                         line[j++] = ' ';
580                         memset(line+j, ' ', DATA_DUMP_LEN);
581
582                         /*
583                          * Offset in line of ASCII dump.
584                          */
585                         k = j + HEX_DUMP_LEN + 2;
586                 }
587                 c = *cp++;
588                 line[j++] = binhex[c>>4];
589                 line[j++] = binhex[c&0xf];
590                 j++;
591                 if (encoding == CHAR_EBCDIC) {
592                         c = EBCDIC_to_ASCII1(c);
593                 }
594                 line[k++] = c >= ' ' && c < 0x7f ? c : '.';
595                 i++;
596                 if ((i & 15) == 0 || i == length) {
597                         /*
598                          * We'll be starting a new line, or
599                          * we're finished printing this buffer;
600                          * dump out the line we've constructed,
601                          * and advance the offset.
602                          */
603                         line[k] = '\0';
604                         print_line(fh, 0, format, line);
605                         ad += 16;
606                 }
607         }
608 }
609
610 static
611 void ps_clean_string(unsigned char *out, const unsigned char *in,
612                         int outbuf_size)
613 {
614         int rd, wr;
615         char c;
616
617         for (rd = 0, wr = 0 ; wr < outbuf_size; rd++, wr++ ) {
618                 c = in[rd];
619                 switch (c) {
620                         case '(':
621                         case ')':
622                         case '\\':
623                                 out[wr] = '\\';
624                                 out[++wr] = c;
625                                 break;
626
627                         default:
628                                 out[wr] = c;
629                                 break;
630                 }
631
632                 if (c == 0) {
633                         break;
634                 }
635         }
636 }
637
638 /* Some formats need stuff at the beginning of the output */
639 void
640 print_preamble(FILE *fh, gint format)
641 {
642         if (format == PR_FMT_PS)
643                 print_ps_preamble(fh);
644         else if (format == PR_FMT_PDML) {
645                 fputs("<?xml version=\"1.0\"?>\n", fh);
646                 fputs("<pdml version=\"" PDML_VERSION "\" ", fh);
647                 fprintf(fh, "creator=\"%s/%s\">\n", PACKAGE, VERSION);
648         }
649 }
650
651 /* Some formats need stuff at the end of the output */
652 void
653 print_finale(FILE *fh, gint format)
654 {
655         if (format == PR_FMT_PS)
656                 print_ps_finale(fh);
657         else if (format == PR_FMT_PDML) {
658                 fputs("</pdml>\n", fh);
659         }
660 }
661
662 void
663 print_line(FILE *fh, int indent, gint format, char *line)
664 {
665         char            space[MAX_INDENT+1];
666         int             i;
667         int             num_spaces;
668         char            psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */
669
670         if (format == PR_FMT_PS) {
671                 ps_clean_string(psbuffer, line, MAX_PS_LINE_LENGTH);
672                 fprintf(fh, "%d (%s) putline\n", indent, psbuffer);
673         }
674         else if (format == PR_FMT_TEXT) {
675                 /* Prepare the tabs for printing, depending on tree level */
676                 num_spaces = indent * 4;
677                 if (num_spaces > MAX_INDENT) {
678                         num_spaces = MAX_INDENT;
679                 }
680                 for (i = 0; i < num_spaces; i++) {
681                         space[i] = ' ';
682                 }
683                 /* The string is NUL-terminated */
684                 space[num_spaces] = '\0';
685
686                 fputs(space, fh);
687                 fputs(line, fh);
688                 putc('\n', fh);
689         }
690         else {
691                 g_assert_not_reached();
692         }
693 }