Coverage Report

Created: 2025-12-27 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/print_stream.c
Line
Count
Source
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
 * SPDX-License-Identifier: GPL-2.0-or-later
11
 */
12
13
#include "config.h"
14
15
#include <stdio.h>
16
#include <string.h>
17
18
#ifdef _WIN32
19
#include <windows.h>
20
21
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
22
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
23
#endif /* ENABLE_VIRTUAL_TERMINAL_PROCESSING */
24
#else
25
#include <stdlib.h> /* for getenv() */
26
#include <unistd.h> /* for isatty() */
27
#endif
28
29
#include <glib.h>
30
31
#include <epan/print_stream.h>
32
33
#include <epan/ps.h>
34
35
#include <wsutil/file_util.h>
36
37
#define TERM_SGR_RESET "\x1B[0m"  /* SGR - reset */
38
#define TERM_CSI_EL    "\x1B[K"   /* EL - Erase in Line (to end of line) */
39
40
typedef enum {
41
    COLOR_NONE,
42
#ifdef _WIN32
43
    COLOR_CONSOLE,
44
#endif
45
    COLOR_24BIT_ESCAPE
46
} color_type_t;
47
48
typedef struct {
49
    bool         to_file;
50
    FILE        *fh;
51
    bool         isatty;
52
    const char  *to_codeset;
53
    color_type_t color_type;
54
#ifdef _WIN32
55
    WORD         csb_attrs;
56
    DWORD        console_mode;
57
#endif
58
} output_text;
59
60
#ifdef _WIN32
61
/*
62
 * The classic Windows Console offers 1-bit color, so you can't set
63
 * the red, green, or blue intensities, you can only set
64
 * "{foreground, background} contains {red, green, blue}". So
65
 * include red, green or blue if the numeric intensity is high
66
 * enough.
67
 */
68
static void
69
set_color_console(FILE *fh, const color_t *fg, const color_t *bg)
70
{
71
    /* default to white foreground, black background */
72
    WORD win_fg_color = FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN;
73
    WORD win_bg_color = 0;
74
75
    if (fg) {
76
        if (((fg->red >> 8) & 0xff) >= 0x80)
77
        {
78
            win_fg_color |= FOREGROUND_RED;
79
        }
80
        else
81
        {
82
            win_fg_color &= (~FOREGROUND_RED);
83
        }
84
        if (((fg->green >> 8) & 0xff) >= 0x80)
85
        {
86
            win_fg_color |= FOREGROUND_GREEN;
87
        }
88
        else
89
        {
90
            win_fg_color &= (~FOREGROUND_GREEN);
91
        }
92
        if (((fg->blue >> 8) & 0xff) >= 0x80)
93
        {
94
            win_fg_color |= FOREGROUND_BLUE;
95
        }
96
        else
97
        {
98
            win_fg_color &= (~FOREGROUND_BLUE);
99
        }
100
    }
101
102
    if (bg) {
103
        if (((bg->red >> 8) & 0xff) >= 0x80)
104
        {
105
            win_bg_color |= BACKGROUND_RED;
106
        }
107
        else
108
        {
109
            win_bg_color &= (~BACKGROUND_RED);
110
        }
111
        if (((bg->green >> 8) & 0xff) >= 0x80)
112
        {
113
            win_bg_color |= BACKGROUND_GREEN;
114
        }
115
        else
116
        {
117
            win_bg_color &= (~BACKGROUND_GREEN);
118
        }
119
        if (((bg->blue >> 8) & 0xff) >= 0x80)
120
        {
121
            win_bg_color |= BACKGROUND_BLUE;
122
        }
123
        else
124
        {
125
            win_bg_color &= (~BACKGROUND_BLUE);
126
        }
127
    }
128
129
    SetConsoleTextAttribute((HANDLE)_get_osfhandle(_fileno(fh)), win_fg_color|win_bg_color);
130
}
131
#endif
132
133
/*
134
 * Use the SGR escape sequences to specify a 24-bit color.
135
 */
136
static void
137
set_color_24bit_escape(FILE *fh, const color_t *fg, const color_t *bg)
138
0
{
139
    /*
140
     * Use the "select character foreground colour" and "select character
141
     * background colour" options to the Select Graphic Rendition control
142
     * sequence; those are reserved in ECMA-48, and are specified in ISO
143
     * standard 8613-6/ITU-T Recommendation T.416, "Open Document Architecture
144
     * (ODA) and Interchange Format: Character Content Architectures",
145
     * section 13.1.8 "Select Graphic Rendition (SGR)".  We use the
146
     * "direct colour in RGB space" option, with a parameter value of 2.
147
     *
148
     * Those sequences are supported by some UN*X terminal emulators; some
149
     * support either : or ; as a separator, others require a ;.
150
     *
151
     * For more than you ever wanted to know about all of this, see
152
     *
153
     *    https://github.com/termstandard/colors
154
     *
155
     * and
156
     *
157
     *    https://gist.github.com/XVilka/8346728
158
     *
159
     * including the discussion following it, and
160
     *
161
     *    https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
162
     *
163
     * They are also supported by versions of the Windows Console that
164
     * allow setting the ENABLE_VIRTUAL_TERMINAL_PROCESSING mode; that
165
     * mode tells the console to interpret escape sequences written
166
     * to it.
167
     */
168
0
    if (fg) {
169
0
        fprintf(fh, "\x1B[38;2;%u;%u;%um",
170
0
                (fg->red   >> 8) & 0xff,
171
0
                (fg->green >> 8) & 0xff,
172
0
                (fg->blue  >> 8) & 0xff);
173
0
    }
174
175
0
    if (bg) {
176
0
        fprintf(fh, "\x1B[48;2;%u;%u;%um",
177
0
                (bg->red   >> 8) & 0xff,
178
0
                (bg->green >> 8) & 0xff,
179
0
                (bg->blue  >> 8) & 0xff);
180
0
    }
181
0
}
182
183
#ifdef _WIN32
184
static void
185
do_color_eol_console(print_stream_t *self)
186
{
187
    output_text *output = (output_text *)self->data;
188
    FILE *fh = output->fh;
189
190
    SetConsoleTextAttribute((HANDLE)_get_osfhandle(_fileno(fh)), output->csb_attrs);
191
    fprintf(fh, "\n");
192
}
193
#endif
194
195
static void
196
do_color_eol_24bit_escape(print_stream_t *self)
197
0
{
198
0
    output_text *output = (output_text *)self->data;
199
0
    FILE *fh = output->fh;
200
201
    /*
202
     * Emit CSI EL to extend current background color all the way to EOL,
203
     * otherwise we get a ragged right edge of color wherever the newline
204
     * occurs.  It's not perfect in every terminal emulator, but it generally
205
     * works.
206
     */
207
0
    fprintf(fh, "%s\n%s", TERM_CSI_EL, TERM_SGR_RESET);
208
0
}
209
210
static FILE *
211
open_print_dest(bool to_file, const char *dest)
212
0
{
213
0
    FILE *fh;
214
215
    /* Open the file or command for output */
216
0
    if (to_file)
217
0
        fh = ws_fopen(dest, "w");
218
0
    else
219
0
        fh = popen(dest, "w");
220
221
0
    return fh;
222
0
}
223
224
static bool
225
close_print_dest(bool to_file, FILE *fh)
226
0
{
227
    /* Close the file or command */
228
0
    if (to_file)
229
0
        return (fclose(fh) == 0);
230
0
    else
231
0
        return (pclose(fh) == 0);
232
0
}
233
234
/* Some formats need stuff at the beginning of the output */
235
bool
236
print_preamble(print_stream_t *self, char *filename, const char *version_string)
237
0
{
238
0
    return self->ops->print_preamble ? (self->ops->print_preamble)(self, filename, version_string) : true;
239
0
}
240
241
bool
242
print_line(print_stream_t *self, int indent, const char *line)
243
0
{
244
0
    return (self->ops->print_line)(self, indent, line);
245
0
}
246
247
bool
248
print_line_color(print_stream_t *self, int indent, const char *line, const color_t *fg, const color_t *bg)
249
0
{
250
0
    if (self->ops->print_line_color)
251
0
        return (self->ops->print_line_color)(self, indent, line, fg, bg);
252
0
    else
253
0
        return (self->ops->print_line)(self, indent, line);
254
0
}
255
256
/* Insert bookmark */
257
bool
258
print_bookmark(print_stream_t *self, const char *name, const char *title)
259
0
{
260
0
    return self->ops->print_bookmark ? (self->ops->print_bookmark)(self, name, title) : true;
261
0
}
262
263
bool
264
new_page(print_stream_t *self)
265
0
{
266
0
    return self->ops->new_page ? (self->ops->new_page)(self) : true;
267
0
}
268
269
/* Some formats need stuff at the end of the output */
270
bool
271
print_finale(print_stream_t *self)
272
0
{
273
0
    return self->ops->print_finale ? (self->ops->print_finale)(self) : true;
274
0
}
275
276
bool
277
destroy_print_stream(print_stream_t *self)
278
0
{
279
0
    return (self && self->ops && self->ops->destroy) ? (self->ops->destroy)(self) : true;
280
0
}
281
282
0
#define MAX_INDENT    1024
283
284
/* returns true if the print succeeded, false if there was an error */
285
static bool
286
print_line_color_text(print_stream_t *self, int indent, const char *line, const color_t *fg, const color_t *bg)
287
0
{
288
0
    static char spaces[MAX_INDENT];
289
0
    size_t ret;
290
0
    output_text *output = (output_text *)self->data;
291
0
    unsigned int num_spaces;
292
0
    bool emit_color = output->isatty && (fg != NULL || bg != NULL);
293
294
    /* should be space, if NUL -> initialize */
295
0
    if (!spaces[0])
296
0
        memset(spaces, ' ', sizeof(spaces));
297
298
0
    if (emit_color) {
299
0
        switch (output->color_type) {
300
301
0
        case COLOR_NONE:
302
0
            break;
303
304
#ifdef _WIN32
305
        case COLOR_CONSOLE:
306
            set_color_console(output->fh, fg, bg);
307
            break;
308
#endif
309
310
0
        case COLOR_24BIT_ESCAPE:
311
0
            set_color_24bit_escape(output->fh, fg, bg);
312
0
            if (ferror(output->fh))
313
0
                return false;
314
0
            break;
315
0
        }
316
0
    }
317
318
    /* Prepare the tabs for printing, depending on tree level */
319
0
    num_spaces = indent * 4;
320
0
    if (num_spaces > MAX_INDENT)
321
0
        num_spaces = MAX_INDENT;
322
323
0
    ret = fwrite(spaces, 1, num_spaces, output->fh);
324
0
    if (ret == num_spaces) {
325
0
        if (output->isatty && output->to_codeset) {
326
            /* XXX Allocating a fresh buffer every line probably isn't the
327
             * most efficient way to do this. However, this has the side
328
             * effect of scrubbing invalid output.
329
             */
330
0
            char *tty_out;
331
332
0
            tty_out = g_convert_with_fallback(line, -1, output->to_codeset, "UTF-8", "?", NULL, NULL, NULL);
333
334
0
            if (tty_out) {
335
#ifdef _WIN32
336
                /*
337
                 * We mapped to little-endian UTF-16, so write to the
338
                 * console using the Unicode API.
339
                 */
340
                DWORD out_len = (DWORD) wcslen((wchar_t *) tty_out);
341
                WriteConsoleW((HANDLE)_get_osfhandle(_fileno(output->fh)), tty_out, out_len, &out_len, NULL);
342
#else
343
0
                fputs(tty_out, output->fh);
344
0
#endif
345
0
                g_free(tty_out);
346
0
            } else {
347
0
                fputs(line, output->fh);
348
0
            }
349
0
        } else {
350
            /*
351
             * Either we're not writing to a terminal/console or we are
352
             * but we're just writing UTF-8 there.
353
             */
354
0
            fputs(line, output->fh);
355
0
        }
356
357
358
0
        if (emit_color) {
359
0
            switch (output->color_type) {
360
361
0
            case COLOR_NONE:
362
0
                putc('\n', output->fh);
363
0
                break;
364
365
#ifdef _WIN32
366
            case COLOR_CONSOLE:
367
                do_color_eol_console(self);
368
                break;
369
#endif
370
371
0
            case COLOR_24BIT_ESCAPE:
372
0
                do_color_eol_24bit_escape(self);
373
0
                break;
374
0
            }
375
0
        } else
376
0
            putc('\n', output->fh);
377
0
    }
378
379
0
    return !ferror(output->fh);
380
0
}
381
382
static bool
383
print_line_text(print_stream_t *self, int indent, const char *line)
384
0
{
385
0
    return print_line_color_text(self, indent, line, NULL, NULL);
386
0
}
387
388
static bool
389
new_page_text(print_stream_t *self)
390
0
{
391
0
    output_text *output = (output_text *)self->data;
392
393
0
    fputs("\f", output->fh);
394
0
    return !ferror(output->fh);
395
0
}
396
397
static bool
398
destroy_text(print_stream_t *self)
399
0
{
400
0
    output_text *output = (output_text *)self->data;
401
0
    bool         ret;
402
403
0
    switch (output->color_type) {
404
405
0
    case COLOR_NONE:
406
0
        break;
407
408
#ifdef _WIN32
409
    case COLOR_CONSOLE:
410
        /* Restore the default text attribute. */
411
        SetConsoleTextAttribute((HANDLE)_get_osfhandle(_fileno(output->fh)), output->csb_attrs);
412
        break;
413
#endif
414
415
0
    case COLOR_24BIT_ESCAPE:
416
        /* Reset the color to the default */
417
0
        fprintf(output->fh, "%s", TERM_SGR_RESET);
418
0
        fflush(output->fh);
419
420
#ifdef _WIN32
421
        /*
422
         * Restore the console mode before we changed it.
423
         * We must do that *after* sending escape sequences,
424
         * as this may disable escape sequence processing.
425
         */
426
        SetConsoleMode((HANDLE)_get_osfhandle(_fileno(output->fh)), output->console_mode);
427
#endif
428
0
        break;
429
0
    }
430
431
0
    ret = close_print_dest(output->to_file, output->fh);
432
0
    g_free(output);
433
0
    g_free(self);
434
0
    return ret;
435
0
}
436
437
static const print_stream_ops_t print_text_ops = {
438
    NULL,            /* preamble */
439
    print_line_text,
440
    print_line_color_text,
441
    NULL,            /* bookmark */
442
    new_page_text,
443
    NULL,            /* finale */
444
    destroy_text,
445
};
446
447
static print_stream_t *
448
print_stream_text_alloc(bool to_file, FILE *fh)
449
0
{
450
0
    print_stream_t *stream;
451
0
    output_text    *output;
452
453
0
    output          = (output_text *)g_malloc(sizeof *output);
454
0
    output->to_file = to_file;
455
0
    output->fh      = fh;
456
457
#ifdef _WIN32
458
    /*
459
     * On Windows, "_isatty()", which is what ws_isatty() wraps,
460
     * "determines whether fd is associated with a character device
461
     * (a terminal, console, printer, or serial port)".
462
     *
463
     * We specifically want to know if it's associated with a *console*,
464
     * as, if it is, we'll be using console-specific APIs.
465
     */
466
    CONSOLE_SCREEN_BUFFER_INFO csb_info;
467
    DWORD console_mode;
468
469
    if (GetConsoleScreenBufferInfo((HANDLE)_get_osfhandle(_fileno(fh)),
470
        &csb_info)) {
471
        /*
472
         * The console-specific API GetConsoleScreenBufferInfo() succeeded,
473
         * so we'll assume this is a console.
474
         */
475
        output->isatty = true;
476
        output->csb_attrs = csb_info.wAttributes;
477
478
        /*
479
         * Map to little-endian UTF-16; we'll be doing Unicode-API
480
         * writes to the console, and that expects the standard flavor
481
         * of Unicode on Windows, which is little-endian UTF-16.
482
         */
483
        output->to_codeset = "UTF-16LE";
484
485
        /*
486
         * As indicated above, the classic Windows Console only offers
487
         * 1-bit color, set through special console APIs.
488
         *
489
         * The console in Windows 10 version 1511 (TH2), build 10586, and
490
         * later supports SGR escape sequences:
491
         *
492
         *  http://www.nivot.org/blog/post/2016/02/04/Windows-10-TH2-(v1511)-Console-Host-Enhancements
493
         *
494
         * but only supports 16 colors.  The "undocumented" 0x04 bit to
495
         * which they refer is documented in the current version of the
496
         * SetConsoleMode() documentation:
497
         *
498
         *   https://docs.microsoft.com/en-us/windows/console/setconsolemode
499
         *
500
         * as ENABLE_VIRTUAL_TERMINAL_PROCESSING, saying
501
         *
502
         *   When writing with WriteFile or WriteConsole, characters are
503
         *   parsed for VT100 and similar control character sequences that
504
         *   control cursor movement, color/font mode, and other operations
505
         *   that can also be performed via the existing Console APIs. For
506
         *   more information, see Console Virtual Terminal Sequences.
507
         *
508
         * Console Virtual Terminal Sequences:
509
         *
510
         *   https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
511
         *
512
         * documents all the escape sequences the Console supports.  It
513
         * currently seems to indicate that the ODA versions with 24-bit
514
         * color are supported but select the closest color from the
515
         * 16-color palette.
516
         *
517
         * The console in Windows 10 builds 14931 (a preview version of
518
         * Windows 10 version 1703) and later supports SGR RGB sequences:
519
         *
520
         *   https://devblogs.microsoft.com/commandline/24-bit-color-in-the-windows-console/
521
         *
522
         * That page says:
523
         *
524
         *   Thanks to our ability to run Linux apps and scripts using our
525
         *   new Bash on Ubuntu on Windows environment atop the Windows
526
         *   Subsystem for Linux (WSL), we can use some Linux scripts and
527
         *   tools to demonstrate the Console's new 24-bit color support:
528
         *
529
         * which suggests that, with that version, whatever escape sequences
530
         * work on UN*Xes also work on Windows, so maybe they support full
531
         * 24-bit color with the ODA sequences.
532
         *
533
         * So, if ENABLE_VIRTUAL_TERMINAL_PROCESSING is already set on
534
         * the console, or if it isn't but we can set it, we use the SGR
535
         * sequences to set colors, otherwise, we just use the
536
         * SetConsoleTextAttribute calls.
537
         */
538
        GetConsoleMode((HANDLE)_get_osfhandle(_fileno(fh)), &output->console_mode);
539
        if (output->console_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) {
540
            /*
541
             * It's already enabled; assume that means we can use the
542
             * 24-bit color escape sequences (although the console might
543
             * not support full 24-bit color, and would map the 24-bit
544
             * color to the closest color in a smaller palette).
545
             */
546
            output->color_type = COLOR_24BIT_ESCAPE;
547
        } else {
548
            /*
549
             * See if we can enable it.
550
             */
551
            console_mode = output->console_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
552
            if (!SetConsoleMode((HANDLE)_get_osfhandle(_fileno(fh)), console_mode)) {
553
                /*
554
                 * We can't - use console-mode color.
555
                 *
556
                 * It's not documented which error is returned if
557
                 * you try to set a mode bit that's not supported,
558
                 * but, at least on Windows 7, ERROR_INVALID_PARAMETER
559
                 * is returned if you try to set
560
                 * ENABLE_VIRTUAL_TERMINAL_PROCESSING.
561
                 *
562
                 * We could check for that error and report other
563
                 * errors as failures.
564
                 */
565
                output->color_type = COLOR_CONSOLE;
566
            } else {
567
                /* We can - use 24-bit color */
568
                output->color_type = COLOR_24BIT_ESCAPE;
569
            }
570
        }
571
    } else {
572
        /*
573
         * GetConsoleScreenBufferInfo() failed; it's not documented
574
         * whether a particular error means "not a console", so we'll
575
         * just assume this means it's not a console.
576
         *
577
         * The error we see on Windows 7 is ERROR_INVALID_HANDLE, but
578
         * "invalid" is vague enough that I'm not sure we should
579
         * treat that as meaning "not a console and everything else
580
         * as being an error that we should report.
581
         */
582
        output->isatty = false;
583
584
        /*
585
         * This is not used if we're not on a console, as we're not doing
586
         * coloring.
587
         */
588
        output->csb_attrs = 0;
589
    }
590
#else
591
    /*
592
     * On UN*X, isatty() tests "whether fildes, an open file descriptor,
593
     * is associated with a terminal device", to quote the Single UNIX
594
     * Specification, and the documentation for UN*Xes that haven't
595
     * been tested against the SUS validation suite say similar things.
596
     * It does *not* just test whether it's associated with a character
597
     * device that may or may not be a terminal device, so it's what we
598
     * want on UN*X.
599
     */
600
0
    output->isatty = isatty(ws_fileno(fh));
601
0
    if (output->isatty) {
602
0
        const char *charset;
603
0
        bool is_utf8;
604
605
        /* Is there a more reliable way to do this? */
606
0
        is_utf8 = g_get_charset(&charset);
607
0
        if (!is_utf8) {
608
            /*
609
             * The local character set isn't UTF-8, so arrange to
610
             * map from UTF-8 to that character set before printing
611
             * on the terminal.
612
             */
613
0
            output->to_codeset = charset;
614
0
        } else {
615
            /*
616
             * The local character set is UTF-8, so no mapping is
617
             * necessary.
618
             */
619
0
            output->to_codeset = NULL;
620
0
        }
621
622
        /*
623
         * Not all UN*X terminal emulators support the 24-bit color SGR
624
         * sequences (for example, macOS Terminal currently doesn't).
625
         *
626
         * As per
627
         *
628
         *    https://github.com/termstandard/colors
629
         *
630
         * terminfo currently doesn't have a flag to indicate 24-bit
631
         * color support - a future release will - so we can't use
632
         * that to determine if the terminal emulator (or terminal)
633
         * supports it.
634
         *
635
         * That page notes that some terminal emulators set the
636
         * COLORTERM environment variable either to "truecolor"
637
         * or "24bit" if 24-bit color is supported; we use that
638
         * test for now.
639
         *
640
         * XXX - if there are terminal emulators that use the 24-bit
641
         * color escape sequences but don't set COLORTERM, add code
642
         * here to look at other environment variables to try to
643
         * recognize them.
644
         *
645
         * XXX - fall back on 8-color or 256-color support if we can
646
         * somehow determine that 24-bit color support isn't available
647
         * but 8-color or 256-color support is?
648
         */
649
0
        char *colorterm = getenv("COLORTERM");
650
0
        if (colorterm != NULL &&
651
0
            (strcmp(colorterm, "truecolor") == 0 || strcmp(colorterm, "24bit") == 0))
652
0
            output->color_type = COLOR_24BIT_ESCAPE;
653
0
        else
654
0
            output->color_type = COLOR_NONE;
655
0
    }
656
0
#endif
657
0
    if (!output->isatty) {
658
        /*
659
         * OK, this was determined *not* to be a terminal, so we won't
660
         * be doing coloring or mapping from UTF-8 to a local character
661
         * set.
662
         */
663
0
        output->to_codeset = NULL;
664
0
        output->color_type = COLOR_NONE;
665
0
    }
666
667
0
    stream          = g_new(print_stream_t, 1);
668
0
    stream->ops     = &print_text_ops;
669
0
    stream->data    = output;
670
671
0
    return stream;
672
0
}
673
674
print_stream_t *
675
print_stream_text_new(bool to_file, const char *dest)
676
0
{
677
0
    FILE *fh;
678
679
0
    fh = open_print_dest(to_file, dest);
680
0
    if (fh == NULL)
681
0
        return NULL;
682
683
0
    return print_stream_text_alloc(to_file, fh);
684
0
}
685
686
print_stream_t *
687
print_stream_text_stdio_new(FILE *fh)
688
0
{
689
0
    return print_stream_text_alloc(true, fh);
690
0
}
691
692
typedef struct {
693
    bool      to_file;
694
    FILE     *fh;
695
} output_ps;
696
697
0
#define MAX_PS_LINE_LENGTH 256
698
699
static
700
void ps_clean_string(char *out, const char *in, int outbuf_size)
701
0
{
702
0
    int  rd, wr;
703
0
    char c;
704
705
0
    if (in == NULL) {
706
0
        out[0] = '\0';
707
0
        return;
708
0
    }
709
710
0
    for (rd = 0, wr = 0 ; wr < outbuf_size; rd++, wr++ ) {
711
0
        c = in[rd];
712
0
        switch (c) {
713
0
        case '(':
714
0
        case ')':
715
0
        case '\\':
716
0
            out[wr] = '\\';
717
0
            out[++wr] = c;
718
0
            break;
719
720
0
        default:
721
0
            out[wr] = c;
722
0
            break;
723
0
        }
724
725
0
        if (c == 0) {
726
0
            break;
727
0
        }
728
0
    }
729
0
}
730
731
static bool
732
print_preamble_ps(print_stream_t *self, char *filename, const char *version_string)
733
0
{
734
0
    output_ps *output = (output_ps *)self->data;
735
0
    char       psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */
736
737
0
    print_ps_preamble(output->fh);
738
739
0
    fputs("%% the page title\n", output->fh);
740
0
    ps_clean_string(psbuffer, filename, MAX_PS_LINE_LENGTH);
741
0
    fprintf(output->fh, "/ws_pagetitle (%s - Wireshark %s) def\n", psbuffer, version_string);
742
0
    fputs("\n", output->fh);
743
0
    return !ferror(output->fh);
744
0
}
745
746
static bool
747
print_line_ps(print_stream_t *self, int indent, const char *line)
748
0
{
749
0
    output_ps *output = (output_ps *)self->data;
750
0
    char       psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */
751
752
0
    ps_clean_string(psbuffer, line, MAX_PS_LINE_LENGTH);
753
0
    fprintf(output->fh, "%d (%s) putline\n", indent, psbuffer);
754
0
    return !ferror(output->fh);
755
0
}
756
757
static bool
758
print_bookmark_ps(print_stream_t *self, const char *name, const char *title)
759
0
{
760
0
    output_ps *output = (output_ps *)self->data;
761
0
    char       psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */
762
763
    /*
764
     * See the Adobe "pdfmark reference":
765
     *
766
     *  http://partners.adobe.com/asn/acrobat/docs/pdfmark.pdf
767
     *
768
     * The pdfmark stuff tells code that turns PostScript into PDF
769
     * things that it should do.
770
     *
771
     * The /OUT stuff creates a bookmark that goes to the
772
     * destination with "name" as the name and "title" as the title.
773
     *
774
     * The "/DEST" creates the destination.
775
     */
776
0
    ps_clean_string(psbuffer, title, MAX_PS_LINE_LENGTH);
777
0
    fprintf(output->fh, "[/Dest /%s /Title (%s)   /OUT pdfmark\n", name,
778
0
          psbuffer);
779
0
    fputs("[/View [/XYZ -4 currentpoint matrix currentmatrix matrix defaultmatrix\n",
780
0
          output->fh);
781
0
    fputs("matrix invertmatrix matrix concatmatrix transform exch pop 20 add null]\n",
782
0
          output->fh);
783
0
    fprintf(output->fh, "/Dest /%s /DEST pdfmark\n", name);
784
0
    return !ferror(output->fh);
785
0
}
786
787
static bool
788
new_page_ps(print_stream_t *self)
789
0
{
790
0
    output_ps *output = (output_ps *)self->data;
791
792
0
    fputs("formfeed\n", output->fh);
793
0
    return !ferror(output->fh);
794
0
}
795
796
static bool
797
print_finale_ps(print_stream_t *self)
798
0
{
799
0
    output_ps *output = (output_ps *)self->data;
800
801
0
    print_ps_finale(output->fh);
802
0
    return !ferror(output->fh);
803
0
}
804
805
static bool
806
destroy_ps(print_stream_t *self)
807
0
{
808
0
    output_ps *output = (output_ps *)self->data;
809
0
    bool       ret;
810
811
0
    ret = close_print_dest(output->to_file, output->fh);
812
0
    g_free(output);
813
0
    g_free(self);
814
0
    return ret;
815
0
}
816
817
static const print_stream_ops_t print_ps_ops = {
818
    print_preamble_ps,
819
    print_line_ps,
820
    NULL, /* print_line_color */
821
    print_bookmark_ps,
822
    new_page_ps,
823
    print_finale_ps,
824
    destroy_ps,
825
};
826
827
static print_stream_t *
828
print_stream_ps_alloc(bool to_file, FILE *fh)
829
0
{
830
0
    print_stream_t *stream;
831
0
    output_ps      *output;
832
833
0
    output          = (output_ps *)g_malloc(sizeof *output);
834
0
    output->to_file = to_file;
835
0
    output->fh      = fh;
836
837
0
    stream          = g_new(print_stream_t, 1);
838
0
    stream->ops     = &print_ps_ops;
839
0
    stream->data    = output;
840
841
0
    return stream;
842
0
}
843
844
print_stream_t *
845
print_stream_ps_new(bool to_file, const char *dest)
846
0
{
847
0
    FILE *fh;
848
849
0
    fh = open_print_dest(to_file, dest);
850
0
    if (fh == NULL)
851
0
        return NULL;
852
853
0
    return print_stream_ps_alloc(to_file, fh);
854
0
}
855
856
print_stream_t *
857
print_stream_ps_stdio_new(FILE *fh)
858
0
{
859
    return print_stream_ps_alloc(true, fh);
860
0
}
861
862
/*
863
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
864
 *
865
 * Local variables:
866
 * c-basic-offset: 4
867
 * tab-width: 8
868
 * indent-tabs-mode: nil
869
 * End:
870
 *
871
 * vi: set shiftwidth=4 tabstop=8 expandtab:
872
 * :indentSize=4:tabSize=8:noTabs=true:
873
 */