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