I added the byte count to the Data section. The print routines had to
[obnox/wireshark/wip.git] / print.c
1 /* print.c
2  * Routines for printing packet analysis trees.
3  *
4  * $Id: print.c,v 1.7 1998/10/22 19:10:17 gram Exp $
5  *
6  * Gilbert Ramirez <gram@verdict.uthscsa.edu>
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@zing.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * 
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  * 
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  * 
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <gtk/gtk.h>
33 #include <stdio.h>
34 #include <string.h>
35
36 #ifdef HAVE_SYS_TYPES_H
37 # include <sys/types.h>
38 #endif
39
40 #include "ethereal.h"
41 #include "packet.h"
42 #include "print.h"
43 #include "ps.h"
44
45 static void printer_opts_file_cb(GtkWidget *w, gpointer te);
46 static void printer_opts_fs_cancel_cb(GtkWidget *w, gpointer data);
47 static void printer_opts_fs_ok_cb(GtkWidget *w, gpointer data);
48 static void printer_opts_toggle_format(GtkWidget *widget, gpointer data);
49 static void printer_opts_toggle_dest(GtkWidget *widget, gpointer data);
50 static void dumpit (FILE *fh, register const u_char *cp, register u_int length);
51 static void dumpit_ps (FILE *fh, register const u_char *cp, register u_int length);
52 static void ps_clean_string(unsigned char *out, const unsigned char *in,
53                         int outbuf_size);
54
55 /* #include "ps.c" */
56
57 pr_opts printer_opts;
58
59 /* Key for gtk_object_set_data */
60 const gchar *print_prefs_key = "printer_prefs_data";
61 GtkWidget * printer_prefs_show()
62 {
63         GtkWidget       *main_vb, *button;
64         GtkWidget       *format_hb, *format_lb;
65         GtkWidget       *dest_hb, *dest_lb;
66         GtkWidget       *cmd_hb, *cmd_lb, *cmd_te;
67         GtkWidget       *file_hb, *file_bt, *file_te;
68         GSList          *format_grp, *dest_grp;
69         pr_opts         *temp_pr_opts = g_malloc(sizeof(pr_opts));
70
71         /* Make a working copy of the printer data */
72         memcpy(temp_pr_opts, &printer_opts, sizeof(pr_opts));
73 /*      temp_pr_opts->cmd = g_strdup(printer_opts->cmd);
74         temp_pr_opts->file = g_strdup(printer_opts->file);*/
75
76         /* Container for each row of widgets */
77         main_vb = gtk_vbox_new(FALSE, 3);
78         gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
79         gtk_widget_show(main_vb);
80         gtk_object_set_data(GTK_OBJECT(main_vb), print_prefs_key,
81           temp_pr_opts);
82
83         /* Output format */
84         format_hb = gtk_hbox_new(FALSE, 1);
85         gtk_container_add(GTK_CONTAINER(main_vb), format_hb);
86         gtk_widget_show(format_hb);
87
88         format_lb = gtk_label_new("Format:");
89         gtk_box_pack_start(GTK_BOX(format_hb), format_lb, FALSE, FALSE, 3);
90         gtk_widget_show(format_lb);
91
92         button = gtk_radio_button_new_with_label(NULL, "Plain Text");
93         if (printer_opts.output_format == 0) {
94                 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
95         }
96         format_grp = gtk_radio_button_group(GTK_RADIO_BUTTON(button));
97         gtk_box_pack_start(GTK_BOX(format_hb), button, TRUE, TRUE, 0);
98         gtk_widget_show(button);
99
100         button = gtk_radio_button_new_with_label(format_grp, "PostScript");
101         if (printer_opts.output_format == 1) {
102                 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
103         }
104         gtk_signal_connect(GTK_OBJECT(button), "toggled",
105                         GTK_SIGNAL_FUNC(printer_opts_toggle_format),
106                         (gpointer)temp_pr_opts);
107         gtk_box_pack_start(GTK_BOX(format_hb), button, TRUE, TRUE, 0);
108         gtk_widget_show(button);
109
110         /* Output destination */
111         dest_hb = gtk_hbox_new(FALSE, 1);
112         gtk_container_add(GTK_CONTAINER(main_vb), dest_hb);
113         gtk_widget_show(dest_hb);
114
115         dest_lb = gtk_label_new("Print to:");
116         gtk_box_pack_start(GTK_BOX(dest_hb), dest_lb, FALSE, FALSE, 3);
117         gtk_widget_show(dest_lb);
118
119         button = gtk_radio_button_new_with_label(NULL, "Command");
120         if (printer_opts.output_dest == 0) {
121                 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
122         }
123         dest_grp = gtk_radio_button_group(GTK_RADIO_BUTTON(button));
124         gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
125         gtk_box_pack_start(GTK_BOX(dest_hb), button, TRUE, TRUE, 0);
126         gtk_widget_show(button);
127
128         button = gtk_radio_button_new_with_label(dest_grp, "File");
129         if (printer_opts.output_dest == 1) {
130                 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), TRUE);
131         }
132         gtk_signal_connect(GTK_OBJECT(button), "toggled",
133                         GTK_SIGNAL_FUNC(printer_opts_toggle_dest),
134                         (gpointer)temp_pr_opts);
135         gtk_box_pack_start(GTK_BOX(dest_hb), button, TRUE, TRUE, 0);
136         gtk_widget_show(button);
137
138         /* Command text entry */
139         cmd_hb = gtk_hbox_new(FALSE, 1);
140         gtk_container_add(GTK_CONTAINER(main_vb), cmd_hb);
141         gtk_widget_show(cmd_hb);
142
143         cmd_lb = gtk_label_new("Command:");
144         gtk_box_pack_start(GTK_BOX(cmd_hb), cmd_lb, FALSE, FALSE, 3);
145         gtk_widget_show(cmd_lb);
146
147         cmd_te = gtk_entry_new();
148         temp_pr_opts->cmd_te = cmd_te;
149         gtk_entry_set_text(GTK_ENTRY(cmd_te), printer_opts.cmd);
150         gtk_box_pack_start(GTK_BOX(cmd_hb), cmd_te, TRUE, TRUE, 3);
151         gtk_widget_show(cmd_te);
152
153         /* File button and text entry */
154         file_hb = gtk_hbox_new(FALSE, 1);
155         gtk_container_add(GTK_CONTAINER(main_vb), file_hb);
156         gtk_widget_show(file_hb);
157
158         file_bt = gtk_button_new_with_label("File:");
159         gtk_box_pack_start(GTK_BOX(file_hb), file_bt, FALSE, FALSE, 3);
160         gtk_widget_show(file_bt);
161
162         file_te = gtk_entry_new();
163         temp_pr_opts->file_te = file_te;
164         gtk_entry_set_text(GTK_ENTRY(file_te), printer_opts.file);
165         gtk_box_pack_start(GTK_BOX(file_hb), file_te, TRUE, TRUE, 3);
166         gtk_widget_show(file_te);
167
168         gtk_signal_connect_object(GTK_OBJECT(file_bt), "clicked",
169                         GTK_SIGNAL_FUNC(printer_opts_file_cb), GTK_OBJECT(file_te));
170
171  
172         return(main_vb);
173 }
174
175
176 static void
177 printer_opts_file_cb(GtkWidget *w, gpointer te) {
178   GtkWidget *fs, **w_list;
179
180   w_list = g_malloc(2 * sizeof(GtkWidget *));
181   
182   fs = gtk_file_selection_new ("Ethereal: Print to a File");
183   w_list[0] = fs;
184   w_list[1] = (GtkWidget *) te;
185
186   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION(fs)->ok_button),
187     "clicked", (GtkSignalFunc) printer_opts_fs_ok_cb, w_list);
188
189   /* Connect the cancel_button to destroy the widget */
190   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION(fs)->cancel_button),
191     "clicked", (GtkSignalFunc) printer_opts_fs_cancel_cb, w_list);
192
193   gtk_widget_show(fs);
194 }
195
196 static void
197 printer_opts_fs_ok_cb(GtkWidget *w, gpointer data) {
198         GtkWidget **w_list = (GtkWidget **) data;
199           
200         gtk_entry_set_text(GTK_ENTRY(w_list[1]),
201                 gtk_file_selection_get_filename (GTK_FILE_SELECTION(w_list[0])));
202         printer_opts_fs_cancel_cb(w, data);
203 }
204
205 static void
206 printer_opts_fs_cancel_cb(GtkWidget *w, gpointer data) {
207         GtkWidget **w_list = (GtkWidget **) data;
208           
209         gtk_widget_destroy(w_list[0]);
210         g_free(data);
211
212
213 void
214 printer_prefs_ok(GtkWidget *w)
215 {
216         pr_opts *data = gtk_object_get_data(GTK_OBJECT(w), print_prefs_key);
217         
218         printer_opts.output_format = ((pr_opts*)data)->output_format;
219         printer_opts.output_dest = ((pr_opts*)data)->output_dest;
220
221         free(printer_opts.cmd);
222         printer_opts.cmd =
223                 g_strdup(gtk_entry_get_text(GTK_ENTRY(((pr_opts*)data)->cmd_te)));
224
225         free(printer_opts.file);
226         printer_opts.file =
227                 g_strdup(gtk_entry_get_text(GTK_ENTRY(((pr_opts*)data)->file_te)));
228
229         g_free(data);
230 }
231
232 void
233 printer_prefs_cancel(GtkWidget *w)
234 {
235         pr_opts *data = gtk_object_get_data(GTK_OBJECT(w), print_prefs_key);
236         
237         g_free(data);
238 }
239
240 static void
241 printer_opts_toggle_format(GtkWidget *widget, gpointer data)
242 {
243                 if (GTK_TOGGLE_BUTTON (widget)->active) {
244                         ((pr_opts*)data)->output_format = 1;
245                         /* toggle file/cmd */
246                 }
247                 else {
248                         ((pr_opts*)data)->output_format = 0;
249                         /* toggle file/cmd */
250                 }
251 }
252
253 static void
254 printer_opts_toggle_dest(GtkWidget *widget, gpointer data)
255 {
256                 if (GTK_TOGGLE_BUTTON (widget)->active) {
257                         ((pr_opts*)data)->output_dest = 1;
258                 }
259                 else {
260                         ((pr_opts*)data)->output_dest = 0;
261                 }
262 }
263
264 /* ========================================================== */
265 void print_tree(const u_char *pd, frame_data *fd, GtkTree *tree)
266 {
267         FILE    *fh;
268         char    *out;
269
270         /* Open the file or command for output */
271         if (printer_opts.output_dest == 0) {
272                 out = printer_opts.cmd;
273                 fh = popen(printer_opts.cmd, "w");
274         }
275         else {
276                 out = printer_opts.file;
277                 fh = fopen(printer_opts.file, "w");
278         }
279
280         if (!fh) {
281                 g_error("Cannot open %s for output.\n", out);
282                 return;
283         }
284
285         /* Create the output */
286         if (printer_opts.output_format == 0) {
287                 print_tree_text(fh, pd, fd, tree);
288         }
289         else {
290                 print_ps_preamble(fh);
291                 print_tree_ps(fh, pd, fd, tree);
292                 print_ps_finale(fh);
293         }
294
295         /* Close the file or command */
296         if (printer_opts.output_dest == 0) {
297                 pclose(fh);
298         }
299         else {
300                 fclose(fh);
301         }
302 }
303
304 /* Print a tree's data in plain text */
305 void print_tree_text(FILE *fh, const u_char *pd, frame_data *fd, GtkTree *tree)
306 {
307         GList           *children, *child, *widgets, *label;
308         GtkWidget       *subtree;
309         int              num_children, i;
310         char            *text;
311         int              num_spaces;
312         char            space[41];
313         gint            data_start, data_len;
314
315         /* Prepare the tabs for printing, depending on tree level */
316         num_spaces = tree->level * 4;
317         if (num_spaces > 40) {
318                 num_spaces = 40;
319         }
320         for (i = 0; i < num_spaces; i++) {
321                 space[i] = ' ';
322         }
323         /* The string is NUL-terminated */
324         space[num_spaces] = 0;
325
326         /* Get the children of this tree */
327         children = tree->children;
328         num_children = g_list_length(children);
329
330         for (i = 0; i < num_children; i++) {
331                 /* Each child of the tree is a widget container */
332                 child = g_list_nth(children, i);
333                 widgets = gtk_container_children(GTK_CONTAINER(child->data));
334
335                 /* And the container holds a label object, which holds text */
336                 label = g_list_nth(widgets, 0);
337                 gtk_label_get(GTK_LABEL(label->data), &text);
338
339                 /* Print the text */
340                 fprintf(fh, "%s%s\n", space, text);
341
342                 /* Recurse into the subtree, if it exists */
343                 subtree = (GTK_TREE_ITEM(child->data))->subtree;
344                 if (subtree) {
345                                 print_tree_text(fh, pd, fd, GTK_TREE(subtree));
346                 }
347                 else if (strncmp("Data (", text, 6) == 0) {
348                         data_start = (gint) gtk_object_get_data(GTK_OBJECT(child->data),
349         E_TREEINFO_START_KEY);
350                         data_len = (gint) gtk_object_get_data(GTK_OBJECT(child->data),
351         E_TREEINFO_LEN_KEY);
352                         dumpit(fh, &pd[data_start], data_len);
353                 }
354         }
355 }
356
357 /* This routine was created by Dan Lasley <DLASLEY@PROMUS.com>, and
358 only slightly modified for ethereal by Gilbert Ramirez. */
359 static
360 void dumpit (FILE *fh, register const u_char *cp, register u_int length)
361 {
362         register int ad, i, j, k;
363         u_char c;
364         u_char line[60];
365                 static u_char binhex[16] = {
366                         '0', '1', '2', '3', '4', '5', '6', '7',
367                         '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
368
369         memset (line, ' ', sizeof line);
370         line[sizeof (line)-1] = 0;
371         for (ad=i=j=k=0; i<length; i++) {
372                 c = *cp++;
373                 line[j++] = binhex[c>>4];
374                 line[j++] = binhex[c&0xf];
375                 if (i&1) j++;
376                 line[42+k++] = c >= ' ' && c < 0x7f ? c : '.';
377                 if ((i & 15) == 15) {
378                         fprintf (fh, "\n%4x  %s", ad, line);
379                         /*if (i==15) printf (" %d", length);*/
380                         memset (line, ' ', sizeof line);
381                         line[sizeof (line)-1] = j = k = 0;
382                         ad += 16;
383                 }
384         }
385
386         if (line[0] != ' ') fprintf (fh, "\n%4x  %s", ad, line);
387         fprintf(fh, "\n");
388         return;
389
390 }
391
392 #define MAX_LINE_LENGTH 256
393
394 static
395 void dumpit_ps (FILE *fh, register const u_char *cp, register u_int length)
396 {
397         register int ad, i, j, k;
398         u_char c;
399         u_char line[60];
400                 static u_char binhex[16] = {
401                         '0', '1', '2', '3', '4', '5', '6', '7',
402                         '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
403                 u_char psline[MAX_LINE_LENGTH];
404
405         memset (line, ' ', sizeof line);
406         line[sizeof (line)-1] = 0;
407         for (ad=i=j=k=0; i<length; i++) {
408                 c = *cp++;
409                 line[j++] = binhex[c>>4];
410                 line[j++] = binhex[c&0xf];
411                 if (i&1) j++;
412                 line[42+k++] = c >= ' ' && c < 0x7f ? c : '.';
413                 if ((i & 15) == 15) {
414                                                 ps_clean_string(psline, line, MAX_LINE_LENGTH);
415                         fprintf (fh, "(%4x  %s) hexdump\n", ad, psline);
416                         memset (line, ' ', sizeof line);
417                         line[sizeof (line)-1] = j = k = 0;
418                         ad += 16;
419                 }
420         }
421
422         if (line[0] != ' ') {
423                         ps_clean_string(psline, line, MAX_LINE_LENGTH);
424                         fprintf (fh, "(%4x  %s) hexdump\n", ad, psline);
425                 }
426         return;
427
428 }
429
430 /* Print a tree's data in PostScript */
431 void print_tree_ps(FILE *fh, const u_char *pd, frame_data *fd, GtkTree *tree)
432 {
433         GList           *children, *child, *widgets, *label;
434         GtkWidget       *subtree;
435         int             num_children, i;
436         char            *text;
437         gint            data_start, data_len;
438         char            psbuffer[MAX_LINE_LENGTH]; /* static sized buffer! */
439
440         /* Get the children of this tree */
441         children = tree->children;
442         num_children = g_list_length(children);
443
444         for (i = 0; i < num_children; i++) {
445                 /* Each child of the tree is a widget container */
446                 child = g_list_nth(children, i);
447                 widgets = gtk_container_children(GTK_CONTAINER(child->data));
448
449                 /* And the container holds a label object, which holds text */
450                 label = g_list_nth(widgets, 0);
451                 gtk_label_get(GTK_LABEL(label->data), &text);
452
453                 /* Print the text */
454                 ps_clean_string(psbuffer, text, MAX_LINE_LENGTH);
455                 fprintf(fh, "%d (%s) putline\n", tree->level, psbuffer);
456
457                 /* Recurse into the subtree, if it exists */
458                 subtree = (GTK_TREE_ITEM(child->data))->subtree;
459                 if (subtree) {
460                                 print_tree_ps(fh, pd, fd, GTK_TREE(subtree));
461                 }
462                 else if (strncmp("Data (", text, 6) == 0) {
463                         data_start = (gint) gtk_object_get_data(GTK_OBJECT(child->data),
464         E_TREEINFO_START_KEY);
465                         data_len = (gint) gtk_object_get_data(GTK_OBJECT(child->data),
466         E_TREEINFO_LEN_KEY);
467                         print_ps_hex(fh);
468                         dumpit_ps(fh, &pd[data_start], data_len);
469                 }
470         }
471 }
472
473 static
474 void ps_clean_string(unsigned char *out, const unsigned char *in,
475                         int outbuf_size)
476 {
477         int rd, wr;
478         char c;
479
480         for (rd = 0, wr = 0 ; wr < outbuf_size; rd++, wr++ ) {
481                 c = in[rd];
482                 switch (c) {
483                         case '(':
484                         case ')':
485                         case '\\':
486                                 out[wr] = '\\';
487                                 out[++wr] = c;
488                                 break;
489
490                         default:
491                                 out[wr] = c;
492                                 break;
493                 }
494
495                 if (c == 0) {
496                         break;
497                 }
498         }
499 }