740773af2eb4a6124587b6a1fe309ef5b1696fb1
[metze/wireshark/wip.git] / epan / print_stream.c
1 /* print_stream.c
2  * Routines for print streams.
3  *
4  * Gilbert Ramirez <gram@alumni.rice.edu>
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24
25 #include "config.h"
26
27 #include <stdio.h>
28
29 #ifdef _WIN32
30 #include <windows.h>
31 #else
32 #include <string.h>
33 #endif
34
35 #include <glib.h>
36
37 #include <epan/print_stream.h>
38
39 #include <epan/ps.h>
40
41 #include <wsutil/file_util.h>
42
43 static FILE *
44 open_print_dest(gboolean to_file, const char *dest)
45 {
46     FILE *fh;
47
48     /* Open the file or command for output */
49     if (to_file)
50         fh = ws_fopen(dest, "w");
51     else
52         fh = popen(dest, "w");
53
54     return fh;
55 }
56
57 static gboolean
58 close_print_dest(gboolean to_file, FILE *fh)
59 {
60     /* Close the file or command */
61     if (to_file)
62         return (fclose(fh) == 0);
63     else
64         return (pclose(fh) == 0);
65 }
66
67 /* Some formats need stuff at the beginning of the output */
68 gboolean
69 print_preamble(print_stream_t *self, gchar *filename, const char *version_string)
70 {
71     return self->ops->print_preamble ? (self->ops->print_preamble)(self, filename, version_string) : TRUE;
72 }
73
74 gboolean
75 print_line(print_stream_t *self, int indent, const char *line)
76 {
77     return (self->ops->print_line)(self, indent, line);
78 }
79
80 /* Insert bookmark */
81 gboolean
82 print_bookmark(print_stream_t *self, const gchar *name, const gchar *title)
83 {
84     return self->ops->print_bookmark ? (self->ops->print_bookmark)(self, name, title) : TRUE;
85 }
86
87 gboolean
88 new_page(print_stream_t *self)
89 {
90     return self->ops->new_page ? (self->ops->new_page)(self) : TRUE;
91 }
92
93 /* Some formats need stuff at the end of the output */
94 gboolean
95 print_finale(print_stream_t *self)
96 {
97     return self->ops->print_finale ? (self->ops->print_finale)(self) : TRUE;
98 }
99
100 gboolean
101 destroy_print_stream(print_stream_t *self)
102 {
103     return self->ops->destroy ? (self->ops->destroy)(self) : TRUE;
104 }
105
106 typedef struct {
107     gboolean  to_file;
108     FILE     *fh;
109 } output_text;
110
111 #define MAX_INDENT    160
112
113 #ifdef _WIN32
114 static char *to_codeset = "UTF-16LE";
115 #else
116 static char *tty_codeset = NULL;
117 static char *to_codeset = NULL;
118 #endif
119
120 static gboolean
121 print_line_text(print_stream_t *self, int indent, const char *line)
122 {
123     static char  spaces[MAX_INDENT];
124     size_t ret;
125
126     output_text *output = (output_text *)self->data;
127     unsigned int num_spaces;
128
129     /* should be space, if NUL -> initialize */
130     if (!spaces[0]) {
131         int i;
132
133         for (i = 0; i < MAX_INDENT; i++)
134             spaces[i] = ' ';
135     }
136
137     /* Prepare the tabs for printing, depending on tree level */
138     num_spaces = indent * 4;
139     if (num_spaces > MAX_INDENT)
140         num_spaces = MAX_INDENT;
141
142     ret = fwrite(spaces, 1, num_spaces, output->fh);
143     if (ret == num_spaces) {
144         gchar *tty_out = NULL;
145
146 #ifndef _WIN32
147         /* Is there a more reliable way to do this? */
148         if (!tty_codeset) {
149             gchar *upper_codeset;
150
151             tty_codeset = g_get_codeset();
152             upper_codeset = g_ascii_strup(tty_codeset, -1);
153             if (!strstr(upper_codeset, "UTF-8") && !strstr(upper_codeset, "UTF8")) {
154                 to_codeset = tty_codeset;
155             }
156             g_free(upper_codeset);
157         }
158 #endif
159
160         if (ws_isatty(ws_fileno(output->fh)) && to_codeset) {
161             /* XXX Allocating a fresh buffer every line probably isn't the
162              * most efficient way to do this. However, this has the side
163              * effect of scrubbing invalid output.
164              */
165             tty_out = g_convert_with_fallback(line, -1, to_codeset, "UTF-8", "?", NULL, NULL, NULL);
166         }
167
168         if (tty_out) {
169 #ifdef _WIN32
170             DWORD out_len = (DWORD) wcslen((wchar_t *) tty_out);
171             WriteConsoleW((HANDLE)_get_osfhandle(_fileno(output->fh)), tty_out, out_len, &out_len, NULL);
172 #else
173             fputs(tty_out, output->fh);
174 #endif
175             g_free(tty_out);
176         } else {
177             fputs(line, output->fh);
178         }
179         putc('\n', output->fh);
180     }
181     return !ferror(output->fh);
182 }
183
184 static gboolean
185 new_page_text(print_stream_t *self)
186 {
187     output_text *output = (output_text *)self->data;
188
189     fputs("\f", output->fh);
190     return !ferror(output->fh);
191 }
192
193 static gboolean
194 destroy_text(print_stream_t *self)
195 {
196     output_text *output = (output_text *)self->data;
197     gboolean     ret;
198
199     ret = close_print_dest(output->to_file, output->fh);
200     g_free(output);
201     g_free(self);
202     return ret;
203 }
204
205 static const print_stream_ops_t print_text_ops = {
206     NULL,            /* preamble */
207     print_line_text,
208     NULL,            /* bookmark */
209     new_page_text,
210     NULL,            /* finale */
211     destroy_text
212 };
213
214 static print_stream_t *
215 print_stream_text_alloc(gboolean to_file, FILE *fh)
216 {
217     print_stream_t *stream;
218     output_text    *output;
219
220     output          = (output_text *)g_malloc(sizeof *output);
221     output->to_file = to_file;
222     output->fh      = fh;
223     stream          = (print_stream_t *)g_malloc(sizeof (print_stream_t));
224     stream->ops     = &print_text_ops;
225     stream->data    = output;
226
227     return stream;
228 }
229
230 print_stream_t *
231 print_stream_text_new(gboolean to_file, const char *dest)
232 {
233     FILE *fh;
234
235     fh = open_print_dest(to_file, dest);
236     if (fh == NULL)
237         return NULL;
238
239     return print_stream_text_alloc(to_file, fh);
240 }
241
242 print_stream_t *
243 print_stream_text_stdio_new(FILE *fh)
244 {
245     return print_stream_text_alloc(TRUE, fh);
246 }
247
248 typedef struct {
249     gboolean  to_file;
250     FILE     *fh;
251 } output_ps;
252
253 #define MAX_PS_LINE_LENGTH 256
254
255 static
256 void ps_clean_string(char *out, const char *in, int outbuf_size)
257 {
258     int  rd, wr;
259     char c;
260
261     if (in == NULL) {
262         out[0] = '\0';
263         return;
264     }
265
266     for (rd = 0, wr = 0 ; wr < outbuf_size; rd++, wr++ ) {
267         c = in[rd];
268         switch (c) {
269         case '(':
270         case ')':
271         case '\\':
272             out[wr] = '\\';
273             out[++wr] = c;
274             break;
275
276         default:
277             out[wr] = c;
278             break;
279         }
280
281         if (c == 0) {
282             break;
283         }
284     }
285 }
286
287 static gboolean
288 print_preamble_ps(print_stream_t *self, gchar *filename, const char *version_string)
289 {
290     output_ps *output = (output_ps *)self->data;
291     char       psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */
292
293     print_ps_preamble(output->fh);
294
295     fputs("%% the page title\n", output->fh);
296     ps_clean_string(psbuffer, filename, MAX_PS_LINE_LENGTH);
297     fprintf(output->fh, "/ws_pagetitle (%s - Wireshark %s) def\n", psbuffer, version_string);
298     fputs("\n", output->fh);
299     return !ferror(output->fh);
300 }
301
302 static gboolean
303 print_line_ps(print_stream_t *self, int indent, const char *line)
304 {
305     output_ps *output = (output_ps *)self->data;
306     char       psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */
307
308     ps_clean_string(psbuffer, line, MAX_PS_LINE_LENGTH);
309     fprintf(output->fh, "%d (%s) putline\n", indent, psbuffer);
310     return !ferror(output->fh);
311 }
312
313 static gboolean
314 print_bookmark_ps(print_stream_t *self, const gchar *name, const gchar *title)
315 {
316     output_ps *output = (output_ps *)self->data;
317     char       psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */
318
319     /*
320      * See the Adobe "pdfmark reference":
321      *
322      *  http://partners.adobe.com/asn/acrobat/docs/pdfmark.pdf
323      *
324      * The pdfmark stuff tells code that turns PostScript into PDF
325      * things that it should do.
326      *
327      * The /OUT stuff creates a bookmark that goes to the
328      * destination with "name" as the name and "title" as the title.
329      *
330      * The "/DEST" creates the destination.
331      */
332     ps_clean_string(psbuffer, title, MAX_PS_LINE_LENGTH);
333     fprintf(output->fh, "[/Dest /%s /Title (%s)   /OUT pdfmark\n", name,
334           psbuffer);
335     fputs("[/View [/XYZ -4 currentpoint matrix currentmatrix matrix defaultmatrix\n",
336           output->fh);
337     fputs("matrix invertmatrix matrix concatmatrix transform exch pop 20 add null]\n",
338           output->fh);
339     fprintf(output->fh, "/Dest /%s /DEST pdfmark\n", name);
340     return !ferror(output->fh);
341 }
342
343 static gboolean
344 new_page_ps(print_stream_t *self)
345 {
346     output_ps *output = (output_ps *)self->data;
347
348     fputs("formfeed\n", output->fh);
349     return !ferror(output->fh);
350 }
351
352 static gboolean
353 print_finale_ps(print_stream_t *self)
354 {
355     output_ps *output = (output_ps *)self->data;
356
357     print_ps_finale(output->fh);
358     return !ferror(output->fh);
359 }
360
361 static gboolean
362 destroy_ps(print_stream_t *self)
363 {
364     output_ps *output = (output_ps *)self->data;
365     gboolean   ret;
366
367     ret = close_print_dest(output->to_file, output->fh);
368     g_free(output);
369     g_free(self);
370     return ret;
371 }
372
373 static const print_stream_ops_t print_ps_ops = {
374     print_preamble_ps,
375     print_line_ps,
376     print_bookmark_ps,
377     new_page_ps,
378     print_finale_ps,
379     destroy_ps
380 };
381
382 static print_stream_t *
383 print_stream_ps_alloc(gboolean to_file, FILE *fh)
384 {
385     print_stream_t *stream;
386     output_ps      *output;
387
388     output          = (output_ps *)g_malloc(sizeof *output);
389     output->to_file = to_file;
390     output->fh      = fh;
391     stream          = (print_stream_t *)g_malloc(sizeof (print_stream_t));
392     stream->ops     = &print_ps_ops;
393     stream->data    = output;
394
395     return stream;
396 }
397
398 print_stream_t *
399 print_stream_ps_new(gboolean to_file, const char *dest)
400 {
401     FILE *fh;
402
403     fh = open_print_dest(to_file, dest);
404     if (fh == NULL)
405         return NULL;
406
407     return print_stream_ps_alloc(to_file, fh);
408 }
409
410 print_stream_t *
411 print_stream_ps_stdio_new(FILE *fh)
412 {
413     return print_stream_ps_alloc(TRUE, fh);
414 }
415
416 /*
417  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
418  *
419  * Local variables:
420  * c-basic-offset: 4
421  * tab-width: 8
422  * indent-tabs-mode: nil
423  * End:
424  *
425  * vi: set shiftwidth=4 tabstop=8 expandtab:
426  * :indentSize=4:tabSize=8:noTabs=true:
427  */