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