Make the "human-readable text vs. PSML vs. PDML" choice separate from
[obnox/wireshark/wip.git] / print.c
1 /* print.c
2  * Routines for printing packet analysis trees.
3  *
4  * $Id: print.c,v 1.84 2004/07/08 10:36:27 guy 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 "range.h"
40 #include "print.h"
41 #include "ps.h"
42 #include "util.h"
43 #include "packet-data.h"
44 #include "packet-frame.h"
45
46 #define PDML_VERSION "0"
47 #define PSML_VERSION "0"
48
49 typedef struct {
50         int                     level;
51         FILE                    *fh;
52         GSList                  *src_list;
53         print_dissections_e     print_dissections;
54         gboolean                print_hex_for_data;
55         char_enc                encoding;
56         gint                    format;
57         epan_dissect_t          *edt;
58 } print_data;
59
60 typedef struct {
61         int                     level;
62         FILE                    *fh;
63         GSList                  *src_list;
64         epan_dissect_t          *edt;
65 } write_pdml_data;
66
67 static void proto_tree_print_node(proto_node *node, gpointer data);
68 static void proto_tree_write_node_pdml(proto_node *node, gpointer data);
69 static const guint8 *get_field_data(GSList *src_list, field_info *fi);
70 static void write_pdml_field_hex_value(write_pdml_data *pdata, field_info *fi);
71 static void print_hex_data_buffer(FILE *fh, register const guchar *cp,
72     register guint length, char_enc encoding, print_format_e format);
73 static void ps_clean_string(unsigned char *out, const unsigned char *in,
74                         int outbuf_size);
75 static void print_escaped_xml(FILE *fh, char *unescaped_string);
76
77 static void print_pdml_geninfo(proto_tree *tree, FILE *fh);
78
79 FILE *open_print_dest(int to_file, const char *dest)
80 {
81         FILE    *fh;
82
83         /* Open the file or command for output */
84         if (to_file)
85                 fh = fopen(dest, "w");
86         else
87                 fh = popen(dest, "w");
88
89         return fh;
90 }
91
92 gboolean close_print_dest(int to_file, FILE *fh)
93 {
94         /* Close the file or command */
95         if (to_file)
96                 return (fclose(fh) == 0);
97         else
98                 return (pclose(fh) == 0);
99 }
100
101 #define MAX_PS_LINE_LENGTH 256
102
103 /* Some formats need stuff at the beginning of the output */
104 void
105 print_preamble(FILE *fh, print_format_e format, gchar *filename)
106 {
107         char            psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */
108
109     
110         switch (format) {
111
112         case(PR_FMT_TEXT):
113                 /* do nothing */
114                 break;
115
116         case(PR_FMT_PS):
117                 print_ps_preamble(fh);
118
119                 fputs("%% Set the font to 10 point\n", fh);
120                 fputs("/Courier findfont 10 scalefont setfont\n", fh);
121                 fputs("\n", fh);
122                 fputs("%% the page title\n", fh);
123                 ps_clean_string(psbuffer, filename, MAX_PS_LINE_LENGTH);
124                 fprintf(fh, "/eth_pagetitle (%s - Ethereal) def\n", psbuffer);
125                 fputs("\n", fh);
126                 break;
127
128         default:
129                 g_assert_not_reached();
130         }
131 }
132
133 void
134 proto_tree_print(print_args_t *print_args, epan_dissect_t *edt,
135     FILE *fh)
136 {
137         print_data data;
138
139         /* Create the output */
140         data.level = 0;
141         data.fh = fh;
142         data.src_list = edt->pi.data_src;
143         data.encoding = edt->pi.fd->flags.encoding;
144         data.print_dissections = print_args->print_dissections;
145         /* If we're printing the entire packet in hex, don't
146            print uninterpreted data fields in hex as well. */
147         data.print_hex_for_data = !print_args->print_hex;
148         data.format = print_args->format;
149         data.edt = edt;
150
151         proto_tree_children_foreach(edt->tree, proto_tree_print_node, &data);
152 }
153
154 #define MAX_INDENT      160
155
156 /* Print a tree's data, and any child nodes. */
157 static
158 void proto_tree_print_node(proto_node *node, gpointer data)
159 {
160         field_info      *fi = PITEM_FINFO(node);
161         print_data      *pdata = (print_data*) data;
162         const guint8    *pd;
163         gchar           label_str[ITEM_LABEL_LENGTH];
164         gchar           *label_ptr;
165
166         /* Don't print invisible entries. */
167         if (PROTO_ITEM_IS_HIDDEN(node))
168                 return;
169
170         /* was a free format label produced? */
171         if (fi->rep) {
172                 label_ptr = fi->rep->representation;
173         }
174         else { /* no, make a generic label */
175                 label_ptr = label_str;
176                 proto_item_fill_label(fi, label_str);
177         }
178
179         print_line(pdata->fh, pdata->level, pdata->format, label_ptr);
180
181         /* If it's uninterpreted data, dump it (unless our caller will
182            be printing the entire packet in hex). */
183         if (fi->hfinfo->id == proto_data && pdata->print_hex_for_data) {
184                 /*
185                  * Find the data for this field.
186                  */
187                 pd = get_field_data(pdata->src_list, fi);
188                 if (pd) {
189                         print_hex_data_buffer(pdata->fh, pd, fi->length,
190                             pdata->encoding, pdata->format);
191                 }
192         }
193
194         /* If we're printing all levels, or if this node is one with a
195            subtree and its subtree is expanded, recurse into the subtree,
196            if it exists. */
197         g_assert(fi->tree_type >= -1 && fi->tree_type < num_tree_types);
198         if (pdata->print_dissections == print_dissections_expanded ||
199             (pdata->print_dissections == print_dissections_as_displayed &&
200                 fi->tree_type >= 0 && tree_is_expanded[fi->tree_type])) {
201                 if (node->first_child != NULL) {
202                         pdata->level++;
203                         proto_tree_children_foreach(node,
204                                 proto_tree_print_node, pdata);
205                         pdata->level--;
206                 }
207         }
208 }
209
210 /* Some formats need stuff at the end of the output */
211 void
212 print_finale(FILE *fh, print_format_e format)
213 {
214         switch (format) {
215
216         case(PR_FMT_TEXT):
217                 /* do nothing */
218                 break;
219
220         case(PR_FMT_PS):
221                 print_ps_finale(fh);
222                 break;
223
224         default:
225                 g_assert_not_reached();
226         }
227 }
228
229 void
230 write_pdml_preamble(FILE *fh)
231 {
232         fputs("<?xml version=\"1.0\"?>\n", fh);
233         fputs("<pdml version=\"" PDML_VERSION "\" ", fh);
234         fprintf(fh, "creator=\"%s/%s\">\n", PACKAGE, VERSION);
235 }
236
237 void
238 proto_tree_write_pdml(epan_dissect_t *edt, FILE *fh)
239 {
240         write_pdml_data data;
241
242         /* Create the output */
243         data.level = 0;
244         data.fh = fh;
245         data.src_list = edt->pi.data_src;
246         data.edt = edt;
247
248         fprintf(fh, "<packet>\n");
249
250         /* Print a "geninfo" protocol as required by PDML */
251         print_pdml_geninfo(edt->tree, fh);
252
253         proto_tree_children_foreach(edt->tree, proto_tree_write_node_pdml,
254             &data);
255
256         fprintf(fh, "</packet>\n\n");
257 }
258
259 /* Write out a tree's data, and any child nodes, as PDML */
260 static void
261 proto_tree_write_node_pdml(proto_node *node, gpointer data)
262 {
263         field_info      *fi = PITEM_FINFO(node);
264         write_pdml_data *pdata = (write_pdml_data*) data;
265         gchar           *label_ptr;
266         gchar           label_str[ITEM_LABEL_LENGTH];
267         char            *dfilter_string;
268         int             chop_len;
269         int             i;
270
271         for (i = -1; i < pdata->level; i++) {
272                 fputs("  ", pdata->fh);
273         }
274
275         /* Text label. It's printed as a field with no name. */
276         if (fi->hfinfo->id == hf_text_only) {
277                 /* Get the text */
278                 if (fi->rep) {
279                         label_ptr = fi->rep->representation;
280                 }
281                 else {
282                         label_ptr = "";
283                 }
284
285                 fputs("<field show=\"", pdata->fh);
286                 print_escaped_xml(pdata->fh, label_ptr);
287
288                 fprintf(pdata->fh, "\" size=\"%d", fi->length);
289                 fprintf(pdata->fh, "\" pos=\"%d", fi->start);
290
291                 fputs("\" value=\"", pdata->fh);
292                 write_pdml_field_hex_value(pdata, fi);
293
294                 if (node->first_child != NULL) {
295                         fputs("\">\n", pdata->fh);
296                 }
297                 else {
298                         fputs("\"/>\n", pdata->fh);
299                 }
300         }
301         /* Uninterpreted data, i.e., the "Data" protocol, is
302          * printed as a field instead of a protocol. */
303         else if (fi->hfinfo->id == proto_data) {
304
305                 fputs("<field name=\"data\" value=\"", pdata->fh);
306
307                 write_pdml_field_hex_value(pdata, fi);
308
309                 fputs("\"/>\n", pdata->fh);
310
311         }
312         /* Normal protocols and fields */
313         else {
314                 if (fi->hfinfo->type == FT_PROTOCOL) {
315                         fputs("<proto name=\"", pdata->fh);
316                 }
317                 else {
318                         fputs("<field name=\"", pdata->fh);
319                 }
320                 print_escaped_xml(pdata->fh, fi->hfinfo->abbrev);
321
322 #if 0
323         /* PDML spec, see: 
324          * http://analyzer.polito.it/30alpha/docs/dissectors/PDMLSpec.htm
325          *
326          * the show fields contains things in 'human readable' format
327          * showname: contains only the name of the field
328          * show: contains only the data of the field
329          * showdtl: contains additional details of the field data
330          * showmap: contains mappings of the field data (e.g. the hostname to an IP address)
331          *
332          * XXX - the showname shouldn't contain the field data itself 
333          * (like it's contained in the fi->rep->representation). 
334          * Unfortunately, we don't have the field data representation for 
335          * all fields, so this isn't currently possible */
336                 fputs("\" showname=\"", pdata->fh);
337                 print_escaped_xml(pdata->fh, fi->hfinfo->name);
338 #endif
339
340                 if (fi->rep) {
341                         fputs("\" showname=\"", pdata->fh);
342                         print_escaped_xml(pdata->fh, fi->rep->representation);
343                 }
344                 else {
345                         label_ptr = label_str;
346                         proto_item_fill_label(fi, label_str);
347                         fputs("\" showname=\"", pdata->fh);
348                         print_escaped_xml(pdata->fh, label_ptr);
349                 }
350
351                 if (PROTO_ITEM_IS_HIDDEN(node))
352                         fprintf(pdata->fh, "\" hide=\"yes");
353
354                 fprintf(pdata->fh, "\" size=\"%d", fi->length);
355                 fprintf(pdata->fh, "\" pos=\"%d", fi->start);
356 /*              fprintf(pdata->fh, "\" id=\"%d", fi->hfinfo->id);*/
357
358                 if (fi->hfinfo->type != FT_PROTOCOL) {
359                         /* Field */
360
361                         /* XXX - this is a hack until we can just call
362                          * fvalue_to_string_repr() for *all* FT_* types. */
363                         dfilter_string = proto_construct_dfilter_string(fi,
364                                         pdata->edt);
365                         if (dfilter_string != NULL) {
366                                 chop_len = strlen(fi->hfinfo->abbrev) + 4; /* for " == " */
367
368                                 /* XXX - Remove double-quotes. Again, once we
369                                  * can call fvalue_to_string_repr(), we can
370                                  * ask it not to produce the version for
371                                  * display-filters, and thus, no
372                                  * double-quotes. */
373                                 if (dfilter_string[strlen(dfilter_string)-1] == '"') {
374                                         dfilter_string[strlen(dfilter_string)-1] = '\0';
375                                         chop_len++;
376                                 }
377
378                                 fputs("\" show=\"", pdata->fh);
379                                 print_escaped_xml(pdata->fh, &dfilter_string[chop_len]);
380                         }
381                         if (fi->length > 0) {
382                                 fputs("\" value=\"", pdata->fh);
383                                 write_pdml_field_hex_value(pdata, fi);
384                         }
385                 }
386
387                 if (node->first_child != NULL) {
388                         fputs("\">\n", pdata->fh);
389                 }
390                 else if (fi->hfinfo->id == proto_data) {
391                         fputs("\">\n", pdata->fh);
392                 }
393                 else {
394                         fputs("\"/>\n", pdata->fh);
395                 }
396         }
397
398         /* We always print all levels for PDML. Recurse here. */
399         if (node->first_child != NULL) {
400                 pdata->level++;
401                 proto_tree_children_foreach(node,
402                                 proto_tree_write_node_pdml, pdata);
403                 pdata->level--;
404         }
405
406         if (node->first_child != NULL) {
407                 for (i = -1; i < pdata->level; i++) {
408                         fputs("  ", pdata->fh);
409                 }
410                 if (fi->hfinfo->type == FT_PROTOCOL) {
411                         fputs("</proto>\n", pdata->fh);
412                 }
413                 else {
414                         fputs("</field>\n", pdata->fh);
415                 }
416         }
417 }
418
419 /* Print info for a 'geninfo' pseudo-protocol. This is required by
420  * the PDML spec. The information is contained in Ethereal's 'frame' protocol,
421  * but we produce a 'geninfo' protocol in the PDML to conform to spec.
422  * The 'frame' protocol follows the 'geninfo' protocol in the PDML. */
423 static void
424 print_pdml_geninfo(proto_tree *tree, FILE *fh)
425 {
426         guint32 num, len, caplen;
427         nstime_t *timestamp;
428         GPtrArray *finfo_array;
429         field_info *frame_finfo;
430
431         /* Get frame protocol's finfo. */
432         finfo_array = proto_find_finfo(tree, proto_frame);
433         if (g_ptr_array_len(finfo_array) < 1) {
434                 return;
435         }
436         frame_finfo = finfo_array->pdata[0];
437         g_ptr_array_free(finfo_array, FALSE);
438
439         /* frame.number --> geninfo.num */
440         finfo_array = proto_find_finfo(tree, hf_frame_number);
441         if (g_ptr_array_len(finfo_array) < 1) {
442                 return;
443         }
444         num = fvalue_get_integer(&((field_info*)finfo_array->pdata[0])->value);
445         g_ptr_array_free(finfo_array, FALSE);
446
447         /* frame.pkt_len --> geninfo.len */
448         finfo_array = proto_find_finfo(tree, hf_frame_packet_len);
449         if (g_ptr_array_len(finfo_array) < 1) {
450                 return;
451         }
452         len = fvalue_get_integer(&((field_info*)finfo_array->pdata[0])->value);
453         g_ptr_array_free(finfo_array, FALSE);
454
455         /* frame.cap_len --> geninfo.caplen */
456         finfo_array = proto_find_finfo(tree, hf_frame_capture_len);
457         if (g_ptr_array_len(finfo_array) < 1) {
458                 return;
459         }
460         caplen = fvalue_get_integer(&((field_info*)finfo_array->pdata[0])->value);
461         g_ptr_array_free(finfo_array, FALSE);
462
463         /* frame.time --> geninfo.timestamp */
464         finfo_array = proto_find_finfo(tree, hf_frame_arrival_time);
465         if (g_ptr_array_len(finfo_array) < 1) {
466                 return;
467         }
468         timestamp = fvalue_get(&((field_info*)finfo_array->pdata[0])->value);
469         g_ptr_array_free(finfo_array, FALSE);
470
471         /* Print geninfo start */
472         fprintf(fh,
473 "  <proto name=\"geninfo\" pos=\"0\" showname=\"General information\" size=\"%u\">\n",
474                 frame_finfo->length);
475
476         /* Print geninfo.num */
477         fprintf(fh,
478 "    <field name=\"num\" pos=\"0\" show=\"%u\" showname=\"Number\" value=\"%x\" size=\"%u\"/>\n",
479                 num, num, frame_finfo->length);
480
481         /* Print geninfo.len */
482         fprintf(fh,
483 "    <field name=\"len\" pos=\"0\" show=\"%u\" showname=\"Packet Length\" value=\"%x\" size=\"%u\"/>\n",
484                 len, len, frame_finfo->length);
485
486         /* Print geninfo.caplen */
487         fprintf(fh,
488 "    <field name=\"caplen\" pos=\"0\" show=\"%u\" showname=\"Captured Length\" value=\"%x\" size=\"%u\"/>\n",
489                 caplen, caplen, frame_finfo->length);
490
491         /* Print geninfo.timestamp */
492         fprintf(fh,
493 "    <field name=\"timestamp\" pos=\"0\" show=\"%s\" showname=\"Captured Time\" value=\"%d.%09d\" size=\"%u\"/>\n",
494                 abs_time_to_str(timestamp), (int) timestamp->secs, timestamp->nsecs, frame_finfo->length);
495
496         /* Print geninfo end */
497         fprintf(fh,
498 "  </proto>\n");
499 }
500
501 void
502 write_pdml_finale(FILE *fh)
503 {
504         fputs("</pdml>\n", fh);
505 }
506
507 void
508 write_psml_preamble(FILE *fh)
509 {
510         fputs("<?xml version=\"1.0\"?>\n", fh);
511         fputs("<psml version=\"" PSML_VERSION "\" ", fh);
512         fprintf(fh, "creator=\"%s/%s\">\n", PACKAGE, VERSION);
513 }
514
515 void
516 proto_tree_write_psml(epan_dissect_t *edt, FILE *fh)
517 {
518         gint    i;
519
520         /* if this is the first packet, we have to create the PSML structure output */
521         if(edt->pi.fd->num == 1) {
522             fprintf(fh, "<structure>\n");
523
524             for(i=0; i < edt->pi.cinfo->num_cols; i++) {
525                 fprintf(fh, "<section>");
526                 print_escaped_xml(fh, edt->pi.cinfo->col_title[i]);
527                 fprintf(fh, "</section>\n");
528             }
529
530             fprintf(fh, "</structure>\n\n");
531         }
532
533         fprintf(fh, "<packet>\n");
534
535         for(i=0; i < edt->pi.cinfo->num_cols; i++) {
536             fprintf(fh, "<section>");
537             print_escaped_xml(fh, edt->pi.cinfo->col_data[i]);
538             fprintf(fh, "</section>\n");
539         }
540
541         fprintf(fh, "</packet>\n\n");
542 }
543
544 void
545 write_psml_finale(FILE *fh)
546 {
547         fputs("</psml>\n", fh);
548 }
549
550 /*
551  * Find the data source for a specified field, and return a pointer
552  * to the data in it. Returns NULL if the data is out of bounds.
553  */
554 static const guint8 *
555 get_field_data(GSList *src_list, field_info *fi)
556 {
557         GSList *src_le;
558         data_source *src;
559         tvbuff_t *src_tvb;
560         gint length, tvbuff_length;
561
562         for (src_le = src_list; src_le != NULL; src_le = src_le->next) {
563                 src = src_le->data;
564                 src_tvb = src->tvb;
565                 if (fi->ds_tvb == src_tvb) {
566                         /*
567                          * Found it.
568                          *
569                          * XXX - a field can have a length that runs past
570                          * the end of the tvbuff.  Ideally, that should
571                          * be fixed when adding an item to the protocol
572                          * tree, but checking the length when doing
573                          * that could be expensive.  Until we fix that,
574                          * we'll do the check here.
575                          */
576                         tvbuff_length = tvb_length_remaining(src_tvb,
577                             fi->start);
578                         if (tvbuff_length < 0) {
579                                 return NULL;
580                         }
581                         length = fi->length;
582                         if (length > tvbuff_length)
583                                 length = tvbuff_length;
584                         return tvb_get_ptr(src_tvb, fi->start, length);
585                 }
586         }
587         g_assert_not_reached();
588         return NULL;    /* not found */
589 }
590
591 /* Print a string, escaping out certain characters that need to
592  * escaped out for XML. */
593 static void
594 print_escaped_xml(FILE *fh, char *unescaped_string)
595 {
596         unsigned char *p;
597
598         for (p = unescaped_string; *p != '\0'; p++) {
599                 switch (*p) {
600                         case '&':
601                                 fputs("&amp;", fh);
602                                 break;
603                         case '<':
604                                 fputs("&lt;", fh);
605                                 break;
606                         case '>':
607                                 fputs("&gt;", fh);
608                                 break;
609                         case '"':
610                                 fputs("&quot;", fh);
611                                 break;
612                         case '\'':
613                                 fputs("&apos;", fh);
614                                 break;
615                         default:
616                                 fputc(*p, fh);
617                 }
618         }
619 }
620
621 static void
622 write_pdml_field_hex_value(write_pdml_data *pdata, field_info *fi)
623 {
624         int i;
625         const guint8 *pd;
626
627         if (fi->length > tvb_length_remaining(fi->ds_tvb, fi->start)) {
628                 fprintf(pdata->fh, "field length invalid!");
629                 return;
630         }
631
632         /* Find the data for this field. */
633         pd = get_field_data(pdata->src_list, fi);
634
635         if (pd) {
636                 /* Print a simple hex dump */
637                 for (i = 0 ; i < fi->length; i++) {
638                         fprintf(pdata->fh, "%02x", pd[i]);
639                 }
640         }
641 }
642
643
644 void print_hex_data(FILE *fh, print_format_e format, epan_dissect_t *edt)
645 {
646         gboolean multiple_sources;
647         GSList *src_le;
648         data_source *src;
649         tvbuff_t *tvb;
650         char *name;
651         char *line;
652         const guchar *cp;
653         guint length;
654
655         /*
656          * Set "multiple_sources" iff this frame has more than one
657          * data source; if it does, we need to print the name of
658          * the data source before printing the data from the
659          * data source.
660          */
661         multiple_sources = (edt->pi.data_src->next != NULL);
662
663         for (src_le = edt->pi.data_src; src_le != NULL;
664             src_le = src_le->next) {
665                 src = src_le->data;
666                 tvb = src->tvb;
667                 if (multiple_sources) {
668                         name = src->name;
669                         print_line(fh, 0, format, "");
670                         line = g_malloc(strlen(name) + 2);      /* <name>:\0 */
671                         strcpy(line, name);
672                         strcat(line, ":");
673                         print_line(fh, 0, format, line);
674                         g_free(line);
675                 }
676                 length = tvb_length(tvb);
677                 cp = tvb_get_ptr(tvb, 0, length);
678                 print_hex_data_buffer(fh, cp, length,
679                     edt->pi.fd->flags.encoding, format);
680         }
681 }
682
683 /*
684  * This routine is based on a routine created by Dan Lasley
685  * <DLASLEY@PROMUS.com>.
686  *
687  * It was modified for Ethereal by Gilbert Ramirez and others.
688  */
689
690 #define MAX_OFFSET_LEN  8       /* max length of hex offset of bytes */
691 #define BYTES_PER_LINE  16      /* max byte values printed on a line */
692 #define HEX_DUMP_LEN    (BYTES_PER_LINE*3)
693                                 /* max number of characters hex dump takes -
694                                    2 digits plus trailing blank */
695 #define DATA_DUMP_LEN   (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
696                                 /* number of characters those bytes take;
697                                    3 characters per byte of hex dump,
698                                    2 blanks separating hex from ASCII,
699                                    1 character per byte of ASCII dump */
700 #define MAX_LINE_LEN    (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
701                                 /* number of characters per line;
702                                    offset, 2 blanks separating offset
703                                    from data dump, data dump */
704
705 static void
706 print_hex_data_buffer(FILE *fh, register const guchar *cp,
707     register guint length, char_enc encoding, print_format_e format)
708 {
709         register unsigned int ad, i, j, k, l;
710         guchar c;
711         guchar line[MAX_LINE_LEN + 1];
712         unsigned int use_digits;
713         static guchar binhex[16] = {
714                 '0', '1', '2', '3', '4', '5', '6', '7',
715                 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
716
717         print_line(fh, 0, format, "");
718
719         /*
720          * How many of the leading digits of the offset will we supply?
721          * We always supply at least 4 digits, but if the maximum offset
722          * won't fit in 4 digits, we use as many digits as will be needed.
723          */
724         if (((length - 1) & 0xF0000000) != 0)
725                 use_digits = 8; /* need all 8 digits */
726         else if (((length - 1) & 0x0F000000) != 0)
727                 use_digits = 7; /* need 7 digits */
728         else if (((length - 1) & 0x00F00000) != 0)
729                 use_digits = 6; /* need 6 digits */
730         else if (((length - 1) & 0x000F0000) != 0)
731                 use_digits = 5; /* need 5 digits */
732         else
733                 use_digits = 4; /* we'll supply 4 digits */
734
735         ad = 0;
736         i = 0;
737         j = 0;
738         k = 0;
739         while (i < length) {
740                 if ((i & 15) == 0) {
741                         /*
742                          * Start of a new line.
743                          */
744                         j = 0;
745                         k = 0;
746                         l = use_digits;
747                         do {
748                                 l--;
749                                 c = (ad >> (l*4)) & 0xF;
750                                 line[j++] = binhex[c];
751                         } while (l != 0);
752                         line[j++] = ' ';
753                         line[j++] = ' ';
754                         memset(line+j, ' ', DATA_DUMP_LEN);
755
756                         /*
757                          * Offset in line of ASCII dump.
758                          */
759                         k = j + HEX_DUMP_LEN + 2;
760                 }
761                 c = *cp++;
762                 line[j++] = binhex[c>>4];
763                 line[j++] = binhex[c&0xf];
764                 j++;
765                 if (encoding == CHAR_EBCDIC) {
766                         c = EBCDIC_to_ASCII1(c);
767                 }
768                 line[k++] = c >= ' ' && c < 0x7f ? c : '.';
769                 i++;
770                 if ((i & 15) == 0 || i == length) {
771                         /*
772                          * We'll be starting a new line, or
773                          * we're finished printing this buffer;
774                          * dump out the line we've constructed,
775                          * and advance the offset.
776                          */
777                         line[k] = '\0';
778                         print_line(fh, 0, format, line);
779                         ad += 16;
780                 }
781         }
782 }
783
784 static
785 void ps_clean_string(unsigned char *out, const unsigned char *in,
786                         int outbuf_size)
787 {
788         int rd, wr;
789         char c;
790
791         for (rd = 0, wr = 0 ; wr < outbuf_size; rd++, wr++ ) {
792                 c = in[rd];
793                 switch (c) {
794                         case '(':
795                         case ')':
796                         case '\\':
797                                 out[wr] = '\\';
798                                 out[++wr] = c;
799                                 break;
800
801                         default:
802                                 out[wr] = c;
803                                 break;
804                 }
805
806                 if (c == 0) {
807                         break;
808                 }
809         }
810 }
811
812 void
813 print_packet_header(FILE *fh, print_format_e format, guint32 number, gchar *summary) {
814         char            psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */
815
816         switch (format) {
817
818         case(PR_FMT_TEXT):
819                 /* do nothing */
820                 break;
821
822         case(PR_FMT_PS):
823                 ps_clean_string(psbuffer, summary, MAX_PS_LINE_LENGTH);
824                 fprintf(fh, "[/Dest /__frame%u__ /Title (%s)   /OUT pdfmark\n", number, psbuffer);
825                 fputs("[/View [/XYZ -4 currentpoint matrix currentmatrix matrix defaultmatrix\n", fh);
826                 fputs("matrix invertmatrix matrix concatmatrix transform exch pop 20 add null]\n", fh);
827                 fprintf(fh, "/Dest /__frame%u__ /DEST pdfmark\n", number);
828                 break;
829
830         default:
831                 g_assert_not_reached();
832         }
833 }
834
835 void
836 print_formfeed(FILE *fh, print_format_e format)
837 {
838         switch (format) {
839
840         case(PR_FMT_TEXT):
841                 fputs("\f", fh);
842                 break;
843
844         case(PR_FMT_PS):
845                 fputs("formfeed\n", fh);
846                 break;
847
848         default:
849                 g_assert_not_reached();
850         }
851 }
852
853 void
854 print_line(FILE *fh, int indent, print_format_e format, char *line)
855 {
856         char            space[MAX_INDENT+1];
857         int             i;
858         int             num_spaces;
859         char            psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */
860
861         switch (format) {
862
863         case(PR_FMT_TEXT):
864                 /* Prepare the tabs for printing, depending on tree level */
865                 num_spaces = indent * 4;
866                 if (num_spaces > MAX_INDENT) {
867                         num_spaces = MAX_INDENT;
868                 }
869                 for (i = 0; i < num_spaces; i++) {
870                         space[i] = ' ';
871                 }
872                 /* The string is NUL-terminated */
873                 space[num_spaces] = '\0';
874
875                 fputs(space, fh);
876                 fputs(line, fh);
877                 putc('\n', fh);
878                 break;
879
880         case(PR_FMT_PS):
881                 ps_clean_string(psbuffer, line, MAX_PS_LINE_LENGTH);
882                 fprintf(fh, "%d (%s) putline\n", indent, psbuffer);
883                 break;
884
885         default:
886                 g_assert_not_reached();
887     }
888 }