summaryrefslogtreecommitdiffstats
path: root/epan/print_stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/print_stream.c')
-rw-r--r--epan/print_stream.c873
1 files changed, 873 insertions, 0 deletions
diff --git a/epan/print_stream.c b/epan/print_stream.c
new file mode 100644
index 00000000..4ed5e4fe
--- /dev/null
+++ b/epan/print_stream.c
@@ -0,0 +1,873 @@
+/* print_stream.c
+ * Routines for print streams.
+ *
+ * Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef _WIN32
+#include <windows.h>
+
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+#endif /* ENABLE_VIRTUAL_TERMINAL_PROCESSING */
+#else
+#include <stdlib.h> /* for getenv() */
+#include <unistd.h> /* for isatty() */
+#endif
+
+#include <glib.h>
+
+#include <epan/print_stream.h>
+
+#include <epan/ps.h>
+
+#include <wsutil/file_util.h>
+
+#define TERM_SGR_RESET "\x1B[0m" /* SGR - reset */
+#define TERM_CSI_EL "\x1B[K" /* EL - Erase in Line (to end of line) */
+
+typedef enum {
+ COLOR_NONE,
+#ifdef _WIN32
+ COLOR_CONSOLE,
+#endif
+ COLOR_24BIT_ESCAPE
+} color_type_t;
+
+typedef struct {
+ gboolean to_file;
+ FILE *fh;
+ gboolean isatty;
+ const char *to_codeset;
+ color_type_t color_type;
+#ifdef _WIN32
+ WORD csb_attrs;
+ DWORD console_mode;
+#endif
+} output_text;
+
+#ifdef _WIN32
+/*
+ * The classic Windows Console offers 1-bit color, so you can't set
+ * the red, green, or blue intensities, you can only set
+ * "{foreground, background} contains {red, green, blue}". So
+ * include red, green or blue if the numeric intensity is high
+ * enough.
+ */
+static void
+set_color_console(FILE *fh, const color_t *fg, const color_t *bg)
+{
+ /* default to white foreground, black background */
+ WORD win_fg_color = FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN;
+ WORD win_bg_color = 0;
+
+ if (fg) {
+ if (((fg->red >> 8) & 0xff) >= 0x80)
+ {
+ win_fg_color |= FOREGROUND_RED;
+ }
+ else
+ {
+ win_fg_color &= (~FOREGROUND_RED);
+ }
+ if (((fg->green >> 8) & 0xff) >= 0x80)
+ {
+ win_fg_color |= FOREGROUND_GREEN;
+ }
+ else
+ {
+ win_fg_color &= (~FOREGROUND_GREEN);
+ }
+ if (((fg->blue >> 8) & 0xff) >= 0x80)
+ {
+ win_fg_color |= FOREGROUND_BLUE;
+ }
+ else
+ {
+ win_fg_color &= (~FOREGROUND_BLUE);
+ }
+ }
+
+ if (bg) {
+ if (((bg->red >> 8) & 0xff) >= 0x80)
+ {
+ win_bg_color |= BACKGROUND_RED;
+ }
+ else
+ {
+ win_bg_color &= (~BACKGROUND_RED);
+ }
+ if (((bg->green >> 8) & 0xff) >= 0x80)
+ {
+ win_bg_color |= BACKGROUND_GREEN;
+ }
+ else
+ {
+ win_bg_color &= (~BACKGROUND_GREEN);
+ }
+ if (((bg->blue >> 8) & 0xff) >= 0x80)
+ {
+ win_bg_color |= BACKGROUND_BLUE;
+ }
+ else
+ {
+ win_bg_color &= (~BACKGROUND_BLUE);
+ }
+ }
+
+ SetConsoleTextAttribute((HANDLE)_get_osfhandle(_fileno(fh)), win_fg_color|win_bg_color);
+}
+#endif
+
+/*
+ * Use the SGR escape sequences to specify a 24-bit color.
+ */
+static void
+set_color_24bit_escape(FILE *fh, const color_t *fg, const color_t *bg)
+{
+ /*
+ * Use the "select character foreground colour" and "select character
+ * background colour" options to the Select Graphic Rendition control
+ * sequence; those are reserved in ECMA-48, and are specified in ISO
+ * standard 8613-6/ITU-T Recommendation T.416, "Open Document Architecture
+ * (ODA) and Interchange Format: Chararcter Content Architectures",
+ * section 13.1.8 "Select Graphic Rendition (SGR)". We use the
+ * "direct colour in RGB space" option, with a parameter value of 2.
+ *
+ * Those sequences are supported by some UN*X terminal emulators; some
+ * support either : or ; as a separator, others require a ;.
+ *
+ * For more than you ever wanted to know about all of this, see
+ *
+ * https://github.com/termstandard/colors
+ *
+ * and
+ *
+ * https://gist.github.com/XVilka/8346728
+ *
+ * including the discussion following it, and
+ *
+ * https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
+ *
+ * They are also supported by versions of the Windows Console that
+ * allow setting the ENABLE_VIRTUAL_TERMINAL_PROCESSING mode; that
+ * mode tells the console to interpret escape sequences written
+ * to it.
+ */
+ if (fg) {
+ fprintf(fh, "\x1B[38;2;%u;%u;%um",
+ (fg->red >> 8) & 0xff,
+ (fg->green >> 8) & 0xff,
+ (fg->blue >> 8) & 0xff);
+ }
+
+ if (bg) {
+ fprintf(fh, "\x1B[48;2;%u;%u;%um",
+ (bg->red >> 8) & 0xff,
+ (bg->green >> 8) & 0xff,
+ (bg->blue >> 8) & 0xff);
+ }
+}
+
+#ifdef _WIN32
+static void
+do_color_eol_console(print_stream_t *self)
+{
+ output_text *output = (output_text *)self->data;
+ FILE *fh = output->fh;
+
+ SetConsoleTextAttribute((HANDLE)_get_osfhandle(_fileno(fh)), output->csb_attrs);
+ fprintf(fh, "\n");
+}
+#endif
+
+static void
+do_color_eol_24bit_escape(print_stream_t *self)
+{
+ output_text *output = (output_text *)self->data;
+ FILE *fh = output->fh;
+
+ /*
+ * Emit CSI EL to extend current background color all the way to EOL,
+ * otherwise we get a ragged right edge of color wherever the newline
+ * occurs. It's not perfect in every terminal emulator, but it generally
+ * works.
+ */
+ fprintf(fh, "%s\n%s", TERM_CSI_EL, TERM_SGR_RESET);
+}
+
+static FILE *
+open_print_dest(gboolean to_file, const char *dest)
+{
+ FILE *fh;
+
+ /* Open the file or command for output */
+ if (to_file)
+ fh = ws_fopen(dest, "w");
+ else
+ fh = popen(dest, "w");
+
+ return fh;
+}
+
+static gboolean
+close_print_dest(gboolean to_file, FILE *fh)
+{
+ /* Close the file or command */
+ if (to_file)
+ return (fclose(fh) == 0);
+ else
+ return (pclose(fh) == 0);
+}
+
+/* Some formats need stuff at the beginning of the output */
+gboolean
+print_preamble(print_stream_t *self, gchar *filename, const char *version_string)
+{
+ return self->ops->print_preamble ? (self->ops->print_preamble)(self, filename, version_string) : TRUE;
+}
+
+gboolean
+print_line(print_stream_t *self, int indent, const char *line)
+{
+ return (self->ops->print_line)(self, indent, line);
+}
+
+gboolean
+print_line_color(print_stream_t *self, int indent, const char *line, const color_t *fg, const color_t *bg)
+{
+ if (self->ops->print_line_color)
+ return (self->ops->print_line_color)(self, indent, line, fg, bg);
+ else
+ return (self->ops->print_line)(self, indent, line);
+}
+
+/* Insert bookmark */
+gboolean
+print_bookmark(print_stream_t *self, const gchar *name, const gchar *title)
+{
+ return self->ops->print_bookmark ? (self->ops->print_bookmark)(self, name, title) : TRUE;
+}
+
+gboolean
+new_page(print_stream_t *self)
+{
+ return self->ops->new_page ? (self->ops->new_page)(self) : TRUE;
+}
+
+/* Some formats need stuff at the end of the output */
+gboolean
+print_finale(print_stream_t *self)
+{
+ return self->ops->print_finale ? (self->ops->print_finale)(self) : TRUE;
+}
+
+gboolean
+destroy_print_stream(print_stream_t *self)
+{
+ return (self && self->ops && self->ops->destroy) ? (self->ops->destroy)(self) : TRUE;
+}
+
+#define MAX_INDENT 160
+
+/* returns TRUE if the print succeeded, FALSE if there was an error */
+static gboolean
+print_line_color_text(print_stream_t *self, int indent, const char *line, const color_t *fg, const color_t *bg)
+{
+ static char spaces[MAX_INDENT];
+ size_t ret;
+ output_text *output = (output_text *)self->data;
+ unsigned int num_spaces;
+ gboolean emit_color = output->isatty && (fg != NULL || bg != NULL);
+
+ /* should be space, if NUL -> initialize */
+ if (!spaces[0])
+ memset(spaces, ' ', sizeof(spaces));
+
+ if (emit_color) {
+ switch (output->color_type) {
+
+ case COLOR_NONE:
+ break;
+
+#ifdef _WIN32
+ case COLOR_CONSOLE:
+ set_color_console(output->fh, fg, bg);
+ break;
+#endif
+
+ case COLOR_24BIT_ESCAPE:
+ set_color_24bit_escape(output->fh, fg, bg);
+ if (ferror(output->fh))
+ return FALSE;
+ break;
+ }
+ }
+
+ /* Prepare the tabs for printing, depending on tree level */
+ num_spaces = indent * 4;
+ if (num_spaces > MAX_INDENT)
+ num_spaces = MAX_INDENT;
+
+ ret = fwrite(spaces, 1, num_spaces, output->fh);
+ if (ret == num_spaces) {
+ if (output->isatty && output->to_codeset) {
+ /* XXX Allocating a fresh buffer every line probably isn't the
+ * most efficient way to do this. However, this has the side
+ * effect of scrubbing invalid output.
+ */
+ gchar *tty_out;
+
+ tty_out = g_convert_with_fallback(line, -1, output->to_codeset, "UTF-8", "?", NULL, NULL, NULL);
+
+ if (tty_out) {
+#ifdef _WIN32
+ /*
+ * We mapped to little-endian UTF-16, so write to the
+ * console using the Unicode API.
+ */
+ DWORD out_len = (DWORD) wcslen((wchar_t *) tty_out);
+ WriteConsoleW((HANDLE)_get_osfhandle(_fileno(output->fh)), tty_out, out_len, &out_len, NULL);
+#else
+ fputs(tty_out, output->fh);
+#endif
+ g_free(tty_out);
+ } else {
+ fputs(line, output->fh);
+ }
+ } else {
+ /*
+ * Either we're not writing to a terminal/console or we are
+ * but we're just writing UTF-8 there.
+ */
+ fputs(line, output->fh);
+ }
+
+
+ if (emit_color) {
+ switch (output->color_type) {
+
+ case COLOR_NONE:
+ putc('\n', output->fh);
+ break;
+
+#ifdef _WIN32
+ case COLOR_CONSOLE:
+ do_color_eol_console(self);
+ break;
+#endif
+
+ case COLOR_24BIT_ESCAPE:
+ do_color_eol_24bit_escape(self);
+ break;
+ }
+ } else
+ putc('\n', output->fh);
+ }
+
+ return !ferror(output->fh);
+}
+
+static gboolean
+print_line_text(print_stream_t *self, int indent, const char *line)
+{
+ return print_line_color_text(self, indent, line, NULL, NULL);
+}
+
+static gboolean
+new_page_text(print_stream_t *self)
+{
+ output_text *output = (output_text *)self->data;
+
+ fputs("\f", output->fh);
+ return !ferror(output->fh);
+}
+
+static gboolean
+destroy_text(print_stream_t *self)
+{
+ output_text *output = (output_text *)self->data;
+ gboolean ret;
+
+ switch (output->color_type) {
+
+ case COLOR_NONE:
+ break;
+
+#ifdef _WIN32
+ case COLOR_CONSOLE:
+ /* Restore the default text attribute. */
+ SetConsoleTextAttribute((HANDLE)_get_osfhandle(_fileno(output->fh)), output->csb_attrs);
+ break;
+#endif
+
+ case COLOR_24BIT_ESCAPE:
+ /* Reset the color to the default */
+ fprintf(output->fh, "%s", TERM_SGR_RESET);
+ fflush(output->fh);
+
+#ifdef _WIN32
+ /*
+ * Restore the console mode before we changed it.
+ * We must do that *after* sending escape sequences,
+ * as this may disable escape sequence processing.
+ */
+ SetConsoleMode((HANDLE)_get_osfhandle(_fileno(output->fh)), output->console_mode);
+#endif
+ break;
+ }
+
+ ret = close_print_dest(output->to_file, output->fh);
+ g_free(output);
+ g_free(self);
+ return ret;
+}
+
+static const print_stream_ops_t print_text_ops = {
+ NULL, /* preamble */
+ print_line_text,
+ print_line_color_text,
+ NULL, /* bookmark */
+ new_page_text,
+ NULL, /* finale */
+ destroy_text,
+};
+
+static print_stream_t *
+print_stream_text_alloc(gboolean to_file, FILE *fh)
+{
+ print_stream_t *stream;
+ output_text *output;
+
+ output = (output_text *)g_malloc(sizeof *output);
+ output->to_file = to_file;
+ output->fh = fh;
+
+#ifdef _WIN32
+ /*
+ * On Windows, "_isatty()", which is what ws_isatty() wraps,
+ * "determines whether fd is associated with a character device
+ * (a terminal, console, printer, or serial port)".
+ *
+ * We specifically want to know if it's assciated with a *console*,
+ * as, if it is, we'll be using console-specific APIs.
+ */
+ CONSOLE_SCREEN_BUFFER_INFO csb_info;
+ DWORD console_mode;
+
+ if (GetConsoleScreenBufferInfo((HANDLE)_get_osfhandle(_fileno(fh)),
+ &csb_info)) {
+ /*
+ * The console-specific API GetConsoleScreenBufferInfo() succeeded,
+ * so we'll assume this is a console.
+ */
+ output->isatty = TRUE;
+ output->csb_attrs = csb_info.wAttributes;
+
+ /*
+ * Map to little-endian UTF-16; we'll be doing Unicode-API
+ * writes to the console, and that expects the standard flavor
+ * of Unicode on Windows, which is little-endian UTF-16.
+ */
+ output->to_codeset = "UTF-16LE";
+
+ /*
+ * As indicated above, the classic Windows Console only offers
+ * 1-bit color, set through special console APIs.
+ *
+ * The console in Windows 10 version 1511 (TH2), build 10586, and
+ * later supports SGR escape sequences:
+ *
+ * http://www.nivot.org/blog/post/2016/02/04/Windows-10-TH2-(v1511)-Console-Host-Enhancements
+ *
+ * but only supports 16 colors. The "undocumented" 0x04 bit to
+ * which they refer is documented in the current version of the
+ * SetConsoleMode() documentation:
+ *
+ * https://docs.microsoft.com/en-us/windows/console/setconsolemode
+ *
+ * as ENABLE_VIRTUAL_TERMINAL_PROCESSING, saying
+ *
+ * When writing with WriteFile or WriteConsole, characters are
+ * parsed for VT100 and similar control character sequences that
+ * control cursor movement, color/font mode, and other operations
+ * that can also be performed via the existing Console APIs. For
+ * more information, see Console Virtual Terminal Sequences.
+ *
+ * Console Virtual Terminal Sequences:
+ *
+ * https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
+ *
+ * documents all the escape sequences the Console supports. It
+ * currently seems to indicate that the ODA versions with 24-bit
+ * color are supported but select the closest color from the
+ * 16-color palette.
+ *
+ * The console in Windows 10 builds 14931 (a preview version of
+ * Windows 10 version 1703) and later supports SGR RGB sequences:
+ *
+ * https://devblogs.microsoft.com/commandline/24-bit-color-in-the-windows-console/
+ *
+ * That page says:
+ *
+ * Thanks to our ability to run Linux apps and scripts using our
+ * new Bash on Ubuntu on Windows environment atop the Windows
+ * Subsystem for Linux (WSL), we can use some Linux scripts and
+ * tools to demonstrate the Console's new 24-bit color support:
+ *
+ * which suggests that, with that version, whatever escape sequences
+ * work on UN*Xes also work on Windows, so maybe they support full
+ * 24-bit color with the ODA sequences.
+ *
+ * So, if ENABLE_VIRTUAL_TERMINAL_PROCESSING is already set on
+ * the console, or if it isn't but we can set it, we use the SGR
+ * sequences to set colors, otherwise, we just use the
+ * SetConsoleTextAttribute calls.
+ */
+ GetConsoleMode((HANDLE)_get_osfhandle(_fileno(fh)), &output->console_mode);
+ if (output->console_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) {
+ /*
+ * It's already enabled; assume that means we can use the
+ * 24-bit color escape sequences (although the console might
+ * not support full 24-bit color, and would map the 24-bit
+ * color to the closest color in a smaller palette).
+ */
+ output->color_type = COLOR_24BIT_ESCAPE;
+ } else {
+ /*
+ * See if we can enable it.
+ */
+ console_mode = output->console_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+ if (!SetConsoleMode((HANDLE)_get_osfhandle(_fileno(fh)), console_mode)) {
+ /*
+ * We can't - use console-mode color.
+ *
+ * It's not documented which error is returned if
+ * you try to set a mode bit that's not supported,
+ * but, at least on Windows 7, ERROR_INVALID_PARAMETER
+ * is returned if you try to set
+ * ENABLE_VIRTUAL_TERMINAL_PROCESSING.
+ *
+ * We could check for that error and report other
+ * errors as failures.
+ */
+ output->color_type = COLOR_CONSOLE;
+ } else {
+ /* We can - use 24-bit color */
+ output->color_type = COLOR_24BIT_ESCAPE;
+ }
+ }
+ } else {
+ /*
+ * GetConsoleScreenBufferInfo() failed; it's not documented
+ * whether a particular error means "not a console", so we'll
+ * just assume this means it's not a console.
+ *
+ * The error we see on Windows 7 is ERROR_INVALID_HANDLE, but
+ * "invalid" is vague enough that I'm not sure we should
+ * treat that as meaning "not a console and everything else
+ * as being an error that we should report.
+ */
+ output->isatty = FALSE;
+
+ /*
+ * This is not used if we're not on a console, as we're not doing
+ * coloring.
+ */
+ output->csb_attrs = 0;
+ }
+#else
+ /*
+ * On UN*X, isatty() tests "whether fildes, an open file descriptor,
+ * is associated with a terminal device", to quote the Single UNIX
+ * Specification, and the documentation for UN*Xes that haven't
+ * been tested against the SUS validation suite say similar things.
+ * It does *not* just test whether it's associated with a character
+ * device that may or may not be a terminal device, so it's what we
+ * want on UN*X.
+ */
+ output->isatty = isatty(ws_fileno(fh));
+ if (output->isatty) {
+ const gchar *charset;
+ gboolean is_utf8;
+
+ /* Is there a more reliable way to do this? */
+ is_utf8 = g_get_charset(&charset);
+ if (!is_utf8) {
+ /*
+ * The local character set isn't UTF-8, so arrange to
+ * map from UTF-8 to that character set before printing
+ * on the terminal.
+ */
+ output->to_codeset = charset;
+ } else {
+ /*
+ * The local character set is UTF-8, so no mapping is
+ * necessary.
+ */
+ output->to_codeset = NULL;
+ }
+
+ /*
+ * Not all UN*X terminal emulators support the 24-bit color SGR
+ * sequences (for example, macOS Terminal currently doesn't).
+ *
+ * As per
+ *
+ * https://github.com/termstandard/colors
+ *
+ * terminfo currently doesn't have a flag to indicate 24-bit
+ * color support - a future release will - so we can't use
+ * that to determine if the terminal emulator (or terminal)
+ * supports it.
+ *
+ * That page notes that some terminal emulators set the
+ * COLORTERM environment variable either to "truecolor"
+ * or "24bit" if 24-bit color is supported; we use that
+ * test for now.
+ *
+ * XXX - if there are terminal emulators that use the 24-bit
+ * color escape sequences but don't set COLORTERM, add code
+ * here to look at other environment variables to try to
+ * recognize them.
+ *
+ * XXX - fall back on 8-color or 256-color support if we can
+ * somehow determine that 24-bit color support isn't available
+ * but 8-color or 256-color support is?
+ */
+ char *colorterm = getenv("COLORTERM");
+ if (colorterm != NULL &&
+ (strcmp(colorterm, "truecolor") == 0 || strcmp(colorterm, "24bit") == 0))
+ output->color_type = COLOR_24BIT_ESCAPE;
+ else
+ output->color_type = COLOR_NONE;
+ }
+#endif
+ if (!output->isatty) {
+ /*
+ * OK, this was determined *not* to be a terminal, so we won't
+ * be doing coloring or mapping from UTF-8 to a local character
+ * set.
+ */
+ output->to_codeset = NULL;
+ output->color_type = COLOR_NONE;
+ }
+
+ stream = g_new(print_stream_t, 1);
+ stream->ops = &print_text_ops;
+ stream->data = output;
+
+ return stream;
+}
+
+print_stream_t *
+print_stream_text_new(gboolean to_file, const char *dest)
+{
+ FILE *fh;
+
+ fh = open_print_dest(to_file, dest);
+ if (fh == NULL)
+ return NULL;
+
+ return print_stream_text_alloc(to_file, fh);
+}
+
+print_stream_t *
+print_stream_text_stdio_new(FILE *fh)
+{
+ return print_stream_text_alloc(TRUE, fh);
+}
+
+typedef struct {
+ gboolean to_file;
+ FILE *fh;
+} output_ps;
+
+#define MAX_PS_LINE_LENGTH 256
+
+static
+void ps_clean_string(char *out, const char *in, int outbuf_size)
+{
+ int rd, wr;
+ char c;
+
+ if (in == NULL) {
+ out[0] = '\0';
+ return;
+ }
+
+ for (rd = 0, wr = 0 ; wr < outbuf_size; rd++, wr++ ) {
+ c = in[rd];
+ switch (c) {
+ case '(':
+ case ')':
+ case '\\':
+ out[wr] = '\\';
+ out[++wr] = c;
+ break;
+
+ default:
+ out[wr] = c;
+ break;
+ }
+
+ if (c == 0) {
+ break;
+ }
+ }
+}
+
+static gboolean
+print_preamble_ps(print_stream_t *self, gchar *filename, const char *version_string)
+{
+ output_ps *output = (output_ps *)self->data;
+ char psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */
+
+ print_ps_preamble(output->fh);
+
+ fputs("%% the page title\n", output->fh);
+ ps_clean_string(psbuffer, filename, MAX_PS_LINE_LENGTH);
+ fprintf(output->fh, "/ws_pagetitle (%s - Wireshark %s) def\n", psbuffer, version_string);
+ fputs("\n", output->fh);
+ return !ferror(output->fh);
+}
+
+static gboolean
+print_line_ps(print_stream_t *self, int indent, const char *line)
+{
+ output_ps *output = (output_ps *)self->data;
+ char psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */
+
+ ps_clean_string(psbuffer, line, MAX_PS_LINE_LENGTH);
+ fprintf(output->fh, "%d (%s) putline\n", indent, psbuffer);
+ return !ferror(output->fh);
+}
+
+static gboolean
+print_bookmark_ps(print_stream_t *self, const gchar *name, const gchar *title)
+{
+ output_ps *output = (output_ps *)self->data;
+ char psbuffer[MAX_PS_LINE_LENGTH]; /* static sized buffer! */
+
+ /*
+ * See the Adobe "pdfmark reference":
+ *
+ * http://partners.adobe.com/asn/acrobat/docs/pdfmark.pdf
+ *
+ * The pdfmark stuff tells code that turns PostScript into PDF
+ * things that it should do.
+ *
+ * The /OUT stuff creates a bookmark that goes to the
+ * destination with "name" as the name and "title" as the title.
+ *
+ * The "/DEST" creates the destination.
+ */
+ ps_clean_string(psbuffer, title, MAX_PS_LINE_LENGTH);
+ fprintf(output->fh, "[/Dest /%s /Title (%s) /OUT pdfmark\n", name,
+ psbuffer);
+ fputs("[/View [/XYZ -4 currentpoint matrix currentmatrix matrix defaultmatrix\n",
+ output->fh);
+ fputs("matrix invertmatrix matrix concatmatrix transform exch pop 20 add null]\n",
+ output->fh);
+ fprintf(output->fh, "/Dest /%s /DEST pdfmark\n", name);
+ return !ferror(output->fh);
+}
+
+static gboolean
+new_page_ps(print_stream_t *self)
+{
+ output_ps *output = (output_ps *)self->data;
+
+ fputs("formfeed\n", output->fh);
+ return !ferror(output->fh);
+}
+
+static gboolean
+print_finale_ps(print_stream_t *self)
+{
+ output_ps *output = (output_ps *)self->data;
+
+ print_ps_finale(output->fh);
+ return !ferror(output->fh);
+}
+
+static gboolean
+destroy_ps(print_stream_t *self)
+{
+ output_ps *output = (output_ps *)self->data;
+ gboolean ret;
+
+ ret = close_print_dest(output->to_file, output->fh);
+ g_free(output);
+ g_free(self);
+ return ret;
+}
+
+static const print_stream_ops_t print_ps_ops = {
+ print_preamble_ps,
+ print_line_ps,
+ NULL, /* print_line_color */
+ print_bookmark_ps,
+ new_page_ps,
+ print_finale_ps,
+ destroy_ps,
+};
+
+static print_stream_t *
+print_stream_ps_alloc(gboolean to_file, FILE *fh)
+{
+ print_stream_t *stream;
+ output_ps *output;
+
+ output = (output_ps *)g_malloc(sizeof *output);
+ output->to_file = to_file;
+ output->fh = fh;
+
+ stream = g_new(print_stream_t, 1);
+ stream->ops = &print_ps_ops;
+ stream->data = output;
+
+ return stream;
+}
+
+print_stream_t *
+print_stream_ps_new(gboolean to_file, const char *dest)
+{
+ FILE *fh;
+
+ fh = open_print_dest(to_file, dest);
+ if (fh == NULL)
+ return NULL;
+
+ return print_stream_ps_alloc(to_file, fh);
+}
+
+print_stream_t *
+print_stream_ps_stdio_new(FILE *fh)
+{
+ return print_stream_ps_alloc(TRUE, fh);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */