/* PSPP - a program for statistical analysis.
- Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
+ Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <ctype.h>
#include <errno.h>
#include <limits.h>
+#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
-#include <signal.h>
-#include <unistd.h>
#include <unilbrk.h>
+#include <unistd.h>
#include <unistr.h>
#include <uniwidth.h>
+#ifdef HAVE_TERMIOS_H
+# include <sys/ioctl.h>
+# include <termios.h>
+#endif
+
#include "data/file-name.h"
+#include "data/file-handle-def.h"
#include "data/settings.h"
#include "libpspp/assertion.h"
#include "libpspp/cast.h"
#include "libpspp/message.h"
#include "libpspp/start-date.h"
#include "libpspp/string-map.h"
+#include "libpspp/u8-line.h"
#include "libpspp/version.h"
#include "output/ascii.h"
-#include "output/cairo.h"
-#include "output/chart-item-provider.h"
+#include "output/cairo-chart.h"
+#include "output/chart-provider.h"
#include "output/driver-provider.h"
-#include "output/message-item.h"
#include "output/options.h"
+#include "output/pivot-output.h"
+#include "output/pivot-table.h"
#include "output/render.h"
-#include "output/tab.h"
-#include "output/table-item.h"
-#include "output/text-item.h"
+#include "output/output-item.h"
-#include "gl/error.h"
#include "gl/minmax.h"
#include "gl/xalloc.h"
+#include "gl/xsize.h"
#include "gettext.h"
#define _(msgid) gettext (msgid)
#define H TABLE_HORZ
#define V TABLE_VERT
-#define N_BOX (RENDER_N_LINES * RENDER_N_LINES \
- * RENDER_N_LINES * RENDER_N_LINES)
-
-static const ucs4_t ascii_box_chars[N_BOX] =
+enum
{
- ' ', '|', '#',
- '-', '+', '#',
- '=', '#', '#',
- '|', '|', '#',
- '+', '+', '#',
- '#', '#', '#',
- '#', '#', '#',
- '#', '#', '#',
- '#', '#', '#',
- '-', '+', '#',
- '-', '+', '#',
- '#', '#', '#',
- '+', '+', '#',
- '+', '+', '#',
- '#', '#', '#',
- '#', '#', '#',
- '#', '#', '#',
- '#', '#', '#',
- '=', '#', '#',
- '#', '#', '#',
- '=', '#', '#',
- '#', '#', '#',
- '#', '#', '#',
- '#', '#', '#',
- '#', '#', '#',
- '#', '#', '#',
- '#', '#', '#',
+ ASCII_LINE_NONE,
+ ASCII_LINE_DASHED,
+ ASCII_LINE_SINGLE,
+ ASCII_LINE_DOUBLE,
+ ASCII_N_LINES
};
-static const ucs4_t unicode_box_chars[N_BOX] =
+struct box_chars
{
- 0x0020, 0x2575, 0x2551,
- 0x2574, 0x256f, 0x255c,
- 0x2550, 0x255b, 0x255d,
- 0x2577, 0x2502, 0x2551,
- 0x256e, 0x2524, 0x2562,
- 0x2555, 0x2561, 0x2563,
- 0x2551, 0x2551, 0x2551,
- 0x2556, 0x2562, 0x2562,
- 0x2557, 0x2563, 0x2563,
- 0x2576, 0x2570, 0x2559,
- 0x2500, 0x2534, 0x2568,
- 0x2550, 0x2567, 0x2569,
- 0x256d, 0x251c, 0x255f,
- 0x252c, 0x253c, 0x256a,
- 0x2564, 0x256a, 0x256c,
- 0x2553, 0x255f, 0x255f,
- 0x2565, 0x256b, 0x256b,
- 0x2566, 0x256c, 0x256c,
- 0x2550, 0x2558, 0x255a,
- 0x2550, 0x2567, 0x2569,
- 0x2550, 0x2567, 0x2569,
- 0x2552, 0x255e, 0x2560,
- 0x2564, 0x256a, 0x256c,
- 0x2564, 0x256a, 0x256c,
- 0x2554, 0x2560, 0x2560,
- 0x2566, 0x256c, 0x256c,
+ ucs4_t c[ASCII_N_LINES][ASCII_N_LINES][ASCII_N_LINES][ASCII_N_LINES];
};
-static inline int
-make_box_index (int left, int right, int top, int bottom)
+static const struct box_chars *
+get_ascii_box (void)
{
- return ((right * 3 + bottom) * 3 + left) * 3 + top;
+ enum {
+ _ = ASCII_LINE_NONE,
+ d = ASCII_LINE_DASHED,
+ S = ASCII_LINE_SINGLE,
+ D = ASCII_LINE_DOUBLE,
+ };
+
+ static const struct box_chars ascii_box =
+ {
+ /* r b l t: _ d S D */
+ .c[_][_][_] = { ' ', '|', '|', '#', },
+ .c[_][_][d] = { '-', '+', '+', '#', },
+ .c[_][_][S] = { '-', '+', '+', '#', },
+ .c[_][_][D] = { '=', '#', '#', '#', },
+ .c[_][d][_] = { '|', '|', '|', '#', },
+ .c[_][d][d] = { '+', '+', '+', '#', },
+ .c[_][d][S] = { '+', '+', '+', '#', },
+ .c[_][d][D] = { '#', '#', '#', '#', },
+ .c[_][S][_] = { '|', '|', '|', '#', },
+ .c[_][S][d] = { '+', '+', '+', '#', },
+ .c[_][S][S] = { '+', '+', '+', '#', },
+ .c[_][S][D] = { '#', '#', '#', '#', },
+ .c[_][D][_] = { '#', '#', '#', '#', },
+ .c[_][D][d] = { '#', '#', '#', '#', },
+ .c[_][D][S] = { '#', '#', '#', '#', },
+ .c[_][D][D] = { '#', '#', '#', '#', },
+ .c[d][_][_] = { '-', '+', '+', '#', },
+ .c[d][_][d] = { '-', '+', '+', '#', },
+ .c[d][_][S] = { '-', '+', '+', '#', },
+ .c[d][_][D] = { '#', '#', '#', '#', },
+ .c[d][d][_] = { '+', '+', '+', '#', },
+ .c[d][d][d] = { '+', '+', '+', '#', },
+ .c[d][d][S] = { '+', '+', '+', '#', },
+ .c[d][d][D] = { '#', '#', '#', '#', },
+ .c[d][S][_] = { '+', '+', '+', '#', },
+ .c[d][S][d] = { '+', '+', '+', '#', },
+ .c[d][S][S] = { '+', '+', '+', '#', },
+ .c[d][S][D] = { '#', '#', '#', '#', },
+ .c[d][D][_] = { '#', '#', '#', '#', },
+ .c[d][D][d] = { '#', '#', '#', '#', },
+ .c[d][D][S] = { '#', '#', '#', '#', },
+ .c[d][D][D] = { '#', '#', '#', '#', },
+ .c[S][_][_] = { '-', '+', '+', '#', },
+ .c[S][_][d] = { '-', '+', '+', '#', },
+ .c[S][_][S] = { '-', '+', '+', '#', },
+ .c[S][_][D] = { '#', '#', '#', '#', },
+ .c[S][d][_] = { '+', '+', '+', '#', },
+ .c[S][d][d] = { '+', '+', '+', '#', },
+ .c[S][d][S] = { '+', '+', '+', '#', },
+ .c[S][d][D] = { '#', '#', '#', '#', },
+ .c[S][S][_] = { '+', '+', '+', '#', },
+ .c[S][S][d] = { '+', '+', '+', '#', },
+ .c[S][S][S] = { '+', '+', '+', '#', },
+ .c[S][S][D] = { '#', '#', '#', '#', },
+ .c[S][D][_] = { '#', '#', '#', '#', },
+ .c[S][D][d] = { '#', '#', '#', '#', },
+ .c[S][D][S] = { '#', '#', '#', '#', },
+ .c[S][D][D] = { '#', '#', '#', '#', },
+ .c[D][_][_] = { '=', '#', '#', '#', },
+ .c[D][_][d] = { '#', '#', '#', '#', },
+ .c[D][_][S] = { '#', '#', '#', '#', },
+ .c[D][_][D] = { '=', '#', '#', '#', },
+ .c[D][d][_] = { '#', '#', '#', '#', },
+ .c[D][d][d] = { '#', '#', '#', '#', },
+ .c[D][d][S] = { '#', '#', '#', '#', },
+ .c[D][d][D] = { '#', '#', '#', '#', },
+ .c[D][S][_] = { '#', '#', '#', '#', },
+ .c[D][S][d] = { '#', '#', '#', '#', },
+ .c[D][S][S] = { '#', '#', '#', '#', },
+ .c[D][S][D] = { '#', '#', '#', '#', },
+ .c[D][D][_] = { '#', '#', '#', '#', },
+ .c[D][D][d] = { '#', '#', '#', '#', },
+ .c[D][D][S] = { '#', '#', '#', '#', },
+ .c[D][D][D] = { '#', '#', '#', '#', },
+ };
+ return &ascii_box;
}
-/* A line of text. */
-struct ascii_line
- {
- struct string s; /* Content, in UTF-8. */
- size_t width; /* Display width, in character positions. */
+static const struct box_chars *
+get_unicode_box (void)
+{
+ enum {
+ _ = ASCII_LINE_NONE,
+ d = ASCII_LINE_DASHED,
+ S = ASCII_LINE_SINGLE,
+ D = ASCII_LINE_DOUBLE,
};
-/* How to emphasize text. */
-enum emphasis_style
- {
- EMPH_BOLD, /* Overstrike for bold. */
- EMPH_UNDERLINE, /* Overstrike for underlining. */
- EMPH_NONE /* No emphasis. */
- };
+ static const struct box_chars unicode_box =
+ {
+ /* r b l t: _ d S D */
+ .c[_][_][_] = { 0x0020, 0x2575, 0x2575, 0x2551, }, /* ╵╵║ */
+ .c[_][_][d] = { 0x2574, 0x256f, 0x256f, 0x255c, }, /* ╴╯╯╜ */
+ .c[_][_][S] = { 0x2574, 0x256f, 0x256f, 0x255c, }, /* ╴╯╯╜ */
+ .c[_][_][D] = { 0x2550, 0x255b, 0x255b, 0x255d, }, /* ═╛╛╝ */
+ .c[_][S][_] = { 0x2577, 0x2502, 0x2502, 0x2551, }, /* ╷││║ */
+ .c[_][S][d] = { 0x256e, 0x2524, 0x2524, 0x2562, }, /* ╮┤┤╢ */
+ .c[_][S][S] = { 0x256e, 0x2524, 0x2524, 0x2562, }, /* ╮┤┤╢ */
+ .c[_][S][D] = { 0x2555, 0x2561, 0x2561, 0x2563, }, /* ╕╡╡╣ */
+ .c[_][d][_] = { 0x2577, 0x250a, 0x2502, 0x2551, }, /* ╷┊│║ */
+ .c[_][d][d] = { 0x256e, 0x2524, 0x2524, 0x2562, }, /* ╮┤┤╢ */
+ .c[_][d][S] = { 0x256e, 0x2524, 0x2524, 0x2562, }, /* ╮┤┤╢ */
+ .c[_][d][D] = { 0x2555, 0x2561, 0x2561, 0x2563, }, /* ╕╡╡╣ */
+ .c[_][D][_] = { 0x2551, 0x2551, 0x2551, 0x2551, }, /* ║║║║ */
+ .c[_][D][d] = { 0x2556, 0x2562, 0x2562, 0x2562, }, /* ╖╢╢╢ */
+ .c[_][D][S] = { 0x2556, 0x2562, 0x2562, 0x2562, }, /* ╖╢╢╢ */
+ .c[_][D][D] = { 0x2557, 0x2563, 0x2563, 0x2563, }, /* ╗╣╣╣ */
+ .c[d][_][_] = { 0x2576, 0x2570, 0x2570, 0x2559, }, /* ╶╰╰╙ */
+ .c[d][_][d] = { 0x254c, 0x2534, 0x2534, 0x2568, }, /* ╌┴┴╨ */
+ .c[d][_][S] = { 0x2500, 0x2534, 0x2534, 0x2568, }, /* ─┴┴╨ */
+ .c[d][_][D] = { 0x2550, 0x2567, 0x2567, 0x2569, }, /* ═╧╧╩ */
+ .c[d][d][_] = { 0x256d, 0x251c, 0x251c, 0x255f, }, /* ╭├├╟ */
+ .c[d][d][d] = { 0x252c, 0x002b, 0x253c, 0x256a, }, /* ┬+┼╪ */
+ .c[d][d][S] = { 0x252c, 0x253c, 0x253c, 0x256a, }, /* ┬┼┼╪ */
+ .c[d][d][D] = { 0x2564, 0x256a, 0x256a, 0x256c, }, /* ╤╪╪╬ */
+ .c[d][S][_] = { 0x256d, 0x251c, 0x251c, 0x255f, }, /* ╭├├╟ */
+ .c[d][S][d] = { 0x252c, 0x253c, 0x253c, 0x256a, }, /* ┬┼┼╪ */
+ .c[d][S][S] = { 0x252c, 0x253c, 0x253c, 0x256a, }, /* ┬┼┼╪ */
+ .c[d][S][D] = { 0x2564, 0x256a, 0x256a, 0x256c, }, /* ╤╪╪╬ */
+ .c[d][D][_] = { 0x2553, 0x255f, 0x255f, 0x255f, }, /* ╓╟╟╟ */
+ .c[d][D][d] = { 0x2565, 0x256b, 0x256b, 0x256b, }, /* ╥╫╫╫ */
+ .c[d][D][S] = { 0x2565, 0x256b, 0x256b, 0x256b, }, /* ╥╫╫╫ */
+ .c[d][D][D] = { 0x2566, 0x256c, 0x256c, 0x256c, }, /* ╦╬╬╬ */
+ .c[S][_][_] = { 0x2576, 0x2570, 0x2570, 0x2559, }, /* ╶╰╰╙ */
+ .c[S][_][d] = { 0x2500, 0x2534, 0x2534, 0x2568, }, /* ─┴┴╨ */
+ .c[S][_][S] = { 0x2500, 0x2534, 0x2534, 0x2568, }, /* ─┴┴╨ */
+ .c[S][_][D] = { 0x2550, 0x2567, 0x2567, 0x2569, }, /* ═╧╧╩ */
+ .c[S][d][_] = { 0x256d, 0x251c, 0x251c, 0x255f, }, /* ╭├├╟ */
+ .c[S][d][d] = { 0x252c, 0x253c, 0x253c, 0x256a, }, /* ┬┼┼╪ */
+ .c[S][d][S] = { 0x252c, 0x253c, 0x253c, 0x256a, }, /* ┬┼┼╪ */
+ .c[S][d][D] = { 0x2564, 0x256a, 0x256a, 0x256c, }, /* ╤╪╪╬ */
+ .c[S][S][_] = { 0x256d, 0x251c, 0x251c, 0x255f, }, /* ╭├├╟ */
+ .c[S][S][d] = { 0x252c, 0x253c, 0x253c, 0x256a, }, /* ┬┼┼╪ */
+ .c[S][S][S] = { 0x252c, 0x253c, 0x253c, 0x256a, }, /* ┬┼┼╪ */
+ .c[S][S][D] = { 0x2564, 0x256a, 0x256a, 0x256c, }, /* ╤╪╪╬ */
+ .c[S][D][_] = { 0x2553, 0x255f, 0x255f, 0x255f, }, /* ╓╟╟╟ */
+ .c[S][D][d] = { 0x2565, 0x256b, 0x256b, 0x256b, }, /* ╥╫╫╫ */
+ .c[S][D][S] = { 0x2565, 0x256b, 0x256b, 0x256b, }, /* ╥╫╫╫ */
+ .c[S][D][D] = { 0x2566, 0x256c, 0x256c, 0x256c, }, /* ╦╬╬╬ */
+ .c[D][_][_] = { 0x2550, 0x2558, 0x2558, 0x255a, }, /* ═╘╘╚ */
+ .c[D][_][d] = { 0x2550, 0x2567, 0x2567, 0x2569, }, /* ═╧╧╩ */
+ .c[D][_][S] = { 0x2550, 0x2567, 0x2567, 0x2569, }, /* ═╧╧╩ */
+ .c[D][_][D] = { 0x2550, 0x2567, 0x2567, 0x2569, }, /* ═╧╧╩ */
+ .c[D][d][_] = { 0x2552, 0x255e, 0x255e, 0x2560, }, /* ╒╞╞╠ */
+ .c[D][d][d] = { 0x2564, 0x256a, 0x256a, 0x256c, }, /* ╤╪╪╬ */
+ .c[D][d][S] = { 0x2564, 0x256a, 0x256a, 0x256c, }, /* ╤╪╪╬ */
+ .c[D][d][D] = { 0x2564, 0x256a, 0x256a, 0x256c, }, /* ╤╪╪╬ */
+ .c[D][S][_] = { 0x2552, 0x255e, 0x255e, 0x2560, }, /* ╒╞╞╠ */
+ .c[D][S][d] = { 0x2564, 0x256a, 0x256a, 0x256c, }, /* ╤╪╪╬ */
+ .c[D][S][S] = { 0x2564, 0x256a, 0x256a, 0x256c, }, /* ╤╪╪╬ */
+ .c[D][S][D] = { 0x2564, 0x256a, 0x256a, 0x256c, }, /* ╤╪╪╬ */
+ .c[D][D][_] = { 0x2554, 0x2560, 0x2560, 0x2560, }, /* ╔╠╠╠ */
+ .c[D][D][d] = { 0x2560, 0x256c, 0x256c, 0x256c, }, /* ╠╬╬╬ */
+ .c[D][D][S] = { 0x2560, 0x256c, 0x256c, 0x256c, }, /* ╠╬╬╬ */
+ .c[D][D][D] = { 0x2566, 0x256c, 0x256c, 0x256c, }, /* ╦╬╬╬ */
+ };
+ return &unicode_box;
+}
+
+static int
+ascii_line_from_render_line (int render_line)
+{
+ switch (render_line)
+ {
+ case RENDER_LINE_NONE:
+ return ASCII_LINE_NONE;
+
+ case RENDER_LINE_DASHED:
+ return ASCII_LINE_DASHED;
+
+ case RENDER_LINE_SINGLE:
+ case RENDER_LINE_THICK:
+ case RENDER_LINE_THIN:
+ return ASCII_LINE_SINGLE;
+
+ case RENDER_LINE_DOUBLE:
+ return ASCII_LINE_DOUBLE;
+
+ default:
+ return ASCII_LINE_NONE;
+ }
+
+}
+
+static ucs4_t
+box_get (const struct box_chars *box,
+ int left_, int right_, int top_, int bottom_)
+{
+ bool rtl = render_direction_rtl ();
+ int left = ascii_line_from_render_line (rtl ? right_ : left_);
+ int right = ascii_line_from_render_line (rtl ? left_ : right_);
+ int top = ascii_line_from_render_line (top_);
+ int bottom = ascii_line_from_render_line (bottom_);
+
+ return box->c[right][bottom][left][top];
+}
/* ASCII output driver. */
struct ascii_driver
/* User parameters. */
bool append; /* Append if output file already exists? */
- bool headers; /* Print headers at top of page? */
- bool paginate; /* Insert formfeeds? */
- bool squeeze_blank_lines; /* Squeeze multiple blank lines into one? */
- enum emphasis_style emphasis; /* How to emphasize text. */
+ bool emphasis; /* Enable bold and underline in output? */
char *chart_file_name; /* Name of files used for charts. */
+ /* Colours for charts */
+ struct cell_color fg;
+ struct cell_color bg;
+
+ /* How the page width is determined: */
+ enum {
+ FIXED_WIDTH, /* Specified by configuration. */
+ VIEW_WIDTH, /* From SET WIDTH. */
+ TERMINAL_WIDTH /* From the terminal's width. */
+ } width_mode;
int width; /* Page width. */
- int length; /* Page length minus margins and header. */
- bool auto_width; /* Use viewwidth as page width? */
- bool auto_length; /* Use viewlength as page width? */
- int top_margin; /* Top margin in lines. */
- int bottom_margin; /* Bottom margin in lines. */
+ int min_hbreak; /* Min cell size to break across pages. */
- const ucs4_t *box; /* Line & box drawing characters. */
+ const struct box_chars *box; /* Line & box drawing characters. */
/* Internal state. */
- char *command_name;
- char *title;
- char *subtitle;
- char *file_name; /* Output file name. */
+ struct file_handle *handle;
FILE *file; /* Output file. */
bool error; /* Output error? */
- int page_number; /* Current page number. */
- struct ascii_line *lines; /* Page content. */
+ struct u8_line *lines; /* Page content. */
int allocated_lines; /* Number of lines allocated. */
- int chart_cnt; /* Number of charts so far. */
- int y;
+ int n_charts; /* Number of charts so far. */
+ int n_objects; /* Number of objects so far. */
+ const struct pivot_table *pt;
+ struct render_params params;
};
static const struct output_driver_class ascii_driver_class;
static void ascii_submit (struct output_driver *, const struct output_item *);
-static int vertical_margins (const struct ascii_driver *);
+static int get_terminal_width (void);
static bool update_page_size (struct ascii_driver *, bool issue_error);
static int parse_page_size (struct driver_option *);
-static void ascii_close_page (struct ascii_driver *);
-static bool ascii_open_page (struct ascii_driver *);
-
static void ascii_draw_line (void *, int bb[TABLE_N_AXES][2],
- enum render_line_style styles[TABLE_N_AXES][2]);
+ enum render_line_style styles[TABLE_N_AXES][2],
+ struct cell_color colors[TABLE_N_AXES][2]);
static void ascii_measure_cell_width (void *, const struct table_cell *,
int *min, int *max);
static int ascii_measure_cell_height (void *, const struct table_cell *,
int width);
-static void ascii_draw_cell (void *, const struct table_cell *,
- int bb[TABLE_N_AXES][2],
+static void ascii_draw_cell (void *, const struct table_cell *, int color_idx,
+ int bb[TABLE_N_AXES][2], int valign_offset,
+ int spill[TABLE_N_AXES][2],
int clip[TABLE_N_AXES][2]);
static struct ascii_driver *
return driver_option_get (d, options, key, default_value);
}
+/* Return true iff the terminal appears to be an xterm with
+ UTF-8 capabilities */
+static bool
+term_is_utf8_xterm (void)
+{
+ const char *term = getenv ("TERM");
+ const char *xterm_locale = getenv ("XTERM_LOCAL");
+ return (term && xterm_locale
+ && !strcmp (term, "xterm")
+ && (strcasestr (xterm_locale, "utf8")
+ || strcasestr (xterm_locale, "utf-8")));
+}
+
static struct output_driver *
-ascii_create (const char *file_name, enum settings_output_devices device_type,
+ascii_create (struct file_handle *fh, enum settings_output_devices device_type,
struct string_map *o)
{
enum { BOX_ASCII, BOX_UNICODE } box;
struct output_driver *d;
- struct ascii_driver *a;
- int paper_length;
-
- a = xzalloc (sizeof *a);
+ struct ascii_driver *a = XZALLOC (struct ascii_driver);
d = &a->driver;
- output_driver_init (&a->driver, &ascii_driver_class, file_name, device_type);
+ output_driver_init (&a->driver, &ascii_driver_class, fh_get_file_name (fh), device_type);
a->append = parse_boolean (opt (d, o, "append", "false"));
- a->headers = parse_boolean (opt (d, o, "headers", "false"));
- a->paginate = parse_boolean (opt (d, o, "paginate", "false"));
- a->squeeze_blank_lines = parse_boolean (opt (d, o, "squeeze", "true"));
- a->emphasis = parse_enum (opt (d, o, "emphasis", "none"),
- "bold", EMPH_BOLD,
- "underline", EMPH_UNDERLINE,
- "none", EMPH_NONE,
- NULL_SENTINEL);
-
- a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", file_name));
-
- a->top_margin = parse_int (opt (d, o, "top-margin", "0"), 0, INT_MAX);
- a->bottom_margin = parse_int (opt (d, o, "bottom-margin", "0"), 0, INT_MAX);
-
- a->width = parse_page_size (opt (d, o, "width", "79"));
- paper_length = parse_page_size (opt (d, o, "length", "66"));
- a->auto_width = a->width < 0;
- a->auto_length = paper_length < 0;
- a->length = paper_length - vertical_margins (a);
-
- box = parse_enum (opt (d, o, "box", "ascii"),
+ a->emphasis = parse_boolean (opt (d, o, "emphasis", "false"));
+
+ a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", fh_get_file_name (fh)));
+ a->handle = fh;
+
+
+ bool terminal = !strcmp (fh_get_file_name (fh), "-") && isatty (1);
+ a->width = parse_page_size (opt (d, o, "width", "-1"));
+ a->width_mode = (a->width > 0 ? FIXED_WIDTH
+ : terminal ? TERMINAL_WIDTH
+ : VIEW_WIDTH);
+ a->min_hbreak = parse_int (opt (d, o, "min-hbreak", "-1"), -1, INT_MAX);
+
+ a->bg = parse_color (opt (d, o, "background-color", "#FFFFFFFFFFFF"));
+ a->fg = parse_color (opt (d, o, "foreground-color", "#000000000000"));
+
+ const char *default_box = (terminal && (!strcmp (locale_charset (), "UTF-8")
+ || term_is_utf8_xterm ())
+ ? "unicode" : "ascii");
+ box = parse_enum (opt (d, o, "box", default_box),
"ascii", BOX_ASCII,
"unicode", BOX_UNICODE,
NULL_SENTINEL);
- a->box = box == BOX_ASCII ? ascii_box_chars : unicode_box_chars;
+ a->box = box == BOX_ASCII ? get_ascii_box () : get_unicode_box ();
- a->command_name = NULL;
- a->title = xstrdup ("");
- a->subtitle = xstrdup ("");
- a->file_name = xstrdup (file_name);
a->file = NULL;
a->error = false;
- a->page_number = 0;
a->lines = NULL;
a->allocated_lines = 0;
- a->chart_cnt = 1;
+ a->n_charts = 0;
+ a->n_objects = 0;
+
+ static const struct render_ops ascii_render_ops = {
+ .draw_line = ascii_draw_line,
+ .measure_cell_width = ascii_measure_cell_width,
+ .measure_cell_height = ascii_measure_cell_height,
+ .adjust_break = NULL,
+ .draw_cell = ascii_draw_cell,
+ };
+ a->params.ops = &ascii_render_ops;
+ a->params.aux = a;
+ a->params.size[H] = a->width;
+ a->params.size[V] = INT_MAX;
+ a->params.font_size[H] = 1;
+ a->params.font_size[V] = 1;
+
+ static const int ascii_line_widths[RENDER_N_LINES] = {
+ [RENDER_LINE_NONE] = 0,
+ [RENDER_LINE_SINGLE] = 1,
+ [RENDER_LINE_DASHED] = 1,
+ [RENDER_LINE_THICK] = 1,
+ [RENDER_LINE_THIN] = 1,
+ [RENDER_LINE_DOUBLE] = 1,
+ };
+ a->params.line_widths = ascii_line_widths;
+ a->params.supports_margins = false;
+ a->params.rtl = render_direction_rtl ();
+ a->params.printing = true;
if (!update_page_size (a, true))
goto error;
+ a->file = fn_open (a->handle, a->append ? "a" : "w");
+ if (!a->file)
+ {
+ msg_error (errno, _("ascii: opening output file `%s'"),
+ fh_get_file_name (a->handle));
+ goto error;
+ }
+
return d;
error:
errno = 0;
value = strtol (option->value, &tail, 0);
- if (dim >= 1 && errno != ERANGE && *tail == '\0')
+ if (value >= 1 && errno != ERANGE && *tail == '\0')
dim = value;
else
- error (0, 0, _("%s: %s must be positive integer or `auto'"),
+ msg (MW, _("%s: %s must be positive integer or `auto'"),
option->driver_name, option->name);
}
}
return dim;
}
-static int
-vertical_margins (const struct ascii_driver *a)
-{
- return a->top_margin + a->bottom_margin + (a->headers ? 3 : 0);
-}
-
-/* Re-calculates the page width and length based on settings,
- margins, and, if "auto" is set, the size of the user's
- terminal window or GUI output window. */
+/* Re-calculates the page width based on settings, margins, and, if "auto" is
+ set, the size of the user's terminal window or GUI output window. */
static bool
update_page_size (struct ascii_driver *a, bool issue_error)
{
- enum { MIN_WIDTH = 6, MIN_LENGTH = 6 };
-
- if (a->auto_width)
- a->width = settings_get_viewwidth ();
- if (a->auto_length)
- a->length = settings_get_viewlength () - vertical_margins (a);
-
- if (a->width < MIN_WIDTH || a->length < MIN_LENGTH)
- {
- if (issue_error)
- error (0, 0,
- _("ascii: page excluding margins and headers "
- "must be at least %d characters wide by %d lines long, but "
- "as configured is only %d characters by %d lines"),
- MIN_WIDTH, MIN_LENGTH,
- a->width, a->length);
- if (a->width < MIN_WIDTH)
- a->width = MIN_WIDTH;
- if (a->length < MIN_LENGTH)
- a->length = MIN_LENGTH;
- return false;
- }
-
- return true;
+ enum { MIN_WIDTH = 6 };
+
+ int want_width = (a->width_mode == VIEW_WIDTH ? settings_get_viewwidth ()
+ : a->width_mode == TERMINAL_WIDTH ? get_terminal_width ()
+ : a->width);
+ bool ok = want_width >= MIN_WIDTH;
+ if (!ok && issue_error)
+ msg (ME, _("ascii: page must be at least %d characters wide, but "
+ "as configured is only %d characters"),
+ MIN_WIDTH, want_width);
+
+ a->width = ok ? want_width : MIN_WIDTH;
+ a->params.size[H] = a->width;
+ a->params.min_break[H] = a->min_hbreak >= 0 ? a->min_hbreak : a->width / 2;
+
+ return ok;
}
static void
struct ascii_driver *a = ascii_driver_cast (driver);
int i;
- if (a->y > 0)
- ascii_close_page (a);
-
if (a->file != NULL)
- fn_close (a->file_name, a->file);
- free (a->command_name);
- free (a->title);
- free (a->subtitle);
- free (a->file_name);
+ fn_close (a->handle, a->file);
+ fh_unref (a->handle);
free (a->chart_file_name);
for (i = 0; i < a->allocated_lines; i++)
- ds_destroy (&a->lines[i].s);
+ u8_line_destroy (&a->lines[i]);
free (a->lines);
free (a);
}
ascii_flush (struct output_driver *driver)
{
struct ascii_driver *a = ascii_driver_cast (driver);
- if (a->y > 0)
- {
- ascii_close_page (a);
-
- if (fn_close (a->file_name, a->file) != 0)
- error (0, errno, _("ascii: closing output file `%s'"),
- a->file_name);
- a->file = NULL;
- }
+ if (a->file)
+ fflush (a->file);
}
static void
-ascii_init_caption_cell (const char *caption, struct table_cell *cell)
+ascii_output_lines (struct ascii_driver *a, size_t n_lines)
{
- cell->contents = caption;
- cell->options = TAB_LEFT;
- cell->destructor = NULL;
+ for (size_t y = 0; y < n_lines; y++)
+ {
+ if (y < a->allocated_lines)
+ {
+ struct u8_line *line = &a->lines[y];
+
+ while (ds_chomp_byte (&line->s, ' '))
+ continue;
+ fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
+ u8_line_clear (&a->lines[y]);
+ }
+ putc ('\n', a->file);
+ }
}
static void
ascii_output_table_item (struct ascii_driver *a,
- const struct table_item *table_item)
+ const struct output_item *item)
{
- const char *caption = table_item_get_caption (table_item);
- struct render_params params;
- struct render_page *page;
- struct render_break x_break;
- int caption_height;
- int i;
-
update_page_size (a, false);
+ a->pt = item->table;
- if (caption != NULL)
- {
- /* XXX doesn't do well with very large captions */
- struct table_cell cell;
- ascii_init_caption_cell (caption, &cell);
- caption_height = ascii_measure_cell_height (a, &cell, a->width);
- }
- else
- caption_height = 0;
-
- params.draw_line = ascii_draw_line;
- params.measure_cell_width = ascii_measure_cell_width;
- params.measure_cell_height = ascii_measure_cell_height;
- params.draw_cell = ascii_draw_cell,
- params.aux = a;
- params.size[H] = a->width;
- params.size[V] = a->length - caption_height;
- params.font_size[H] = 1;
- params.font_size[V] = 1;
- for (i = 0; i < RENDER_N_LINES; i++)
+ size_t *layer_indexes;
+ PIVOT_OUTPUT_FOR_EACH_LAYER (layer_indexes, item->table, true)
{
- int width = i == RENDER_LINE_NONE ? 0 : 1;
- params.line_widths[H][i] = width;
- params.line_widths[V][i] = width;
- }
-
- if (a->file == NULL && !ascii_open_page (a))
- return;
-
- page = render_page_create (¶ms, table_item_get_table (table_item));
- for (render_break_init (&x_break, page, H);
- render_break_has_next (&x_break); )
- {
- struct render_page *x_slice;
- struct render_break y_break;
-
- x_slice = render_break_next (&x_break, a->width);
- for (render_break_init (&y_break, x_slice, V);
- render_break_has_next (&y_break); )
+ struct render_pager *p = render_pager_create (&a->params, item->table,
+ layer_indexes);
+ for (int i = 0; render_pager_has_next (p); i++)
{
- struct render_page *y_slice;
- int space;
+ if (a->n_objects++)
+ putc ('\n', a->file);
- if (a->y > 0)
- a->y++;
-
- space = a->length - a->y - caption_height;
- if (render_break_next_size (&y_break) > space)
- {
- assert (a->y > 0);
- ascii_close_page (a);
- if (!ascii_open_page (a))
- return;
- continue;
- }
-
- y_slice = render_break_next (&y_break, space);
- if (caption_height)
- {
- struct table_cell cell;
- int bb[TABLE_N_AXES][2];
-
- ascii_init_caption_cell (caption, &cell);
- bb[H][0] = 0;
- bb[H][1] = a->width;
- bb[V][0] = 0;
- bb[V][1] = caption_height;
- ascii_draw_cell (a, &cell, bb, bb);
- a->y += caption_height;
- caption_height = 0;
- }
- render_page_draw (y_slice);
- a->y += render_page_get_size (y_slice, V);
- render_page_unref (y_slice);
+ ascii_output_lines (a, render_pager_draw_next (p, INT_MAX));
}
- render_break_destroy (&y_break);
+ render_pager_destroy (p);
}
- render_break_destroy (&x_break);
+
+ a->pt = NULL;
}
static void
-ascii_output_text (struct ascii_driver *a, const char *text)
+ascii_output_table_item_unref (struct ascii_driver *a,
+ struct output_item *table_item)
{
- struct table_item *table_item;
-
- table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
ascii_output_table_item (a, table_item);
- table_item_unref (table_item);
+ output_item_unref (table_item);
}
static void
-ascii_submit (struct output_driver *driver,
- const struct output_item *output_item)
+ascii_submit (struct output_driver *driver, const struct output_item *item)
{
struct ascii_driver *a = ascii_driver_cast (driver);
-
- output_driver_track_current_command (output_item, &a->command_name);
-
if (a->error)
return;
- if (is_table_item (output_item))
- ascii_output_table_item (a, to_table_item (output_item));
-#ifdef HAVE_CAIRO
- else if (is_chart_item (output_item) && a->chart_file_name != NULL)
+ switch (item->type)
{
- struct chart_item *chart_item = to_chart_item (output_item);
- char *file_name;
+ case OUTPUT_ITEM_TABLE:
+ ascii_output_table_item (a, item);
+ break;
- file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
- a->chart_cnt++);
- if (file_name != NULL)
+ case OUTPUT_ITEM_IMAGE:
+ if (a->chart_file_name != NULL)
{
- struct text_item *text_item;
-
- text_item = text_item_create_format (
- TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
-
- ascii_submit (driver, &text_item->output_item);
- text_item_unref (text_item);
- free (file_name);
+ char *file_name = xr_write_png_image (
+ item->image, a->chart_file_name, ++a->n_charts);
+ if (file_name != NULL)
+ {
+ struct output_item *text_item = text_item_create_nocopy (
+ TEXT_ITEM_LOG,
+ xasprintf (_("See %s for an image."), file_name),
+ NULL);
+
+ ascii_submit (driver, text_item);
+ output_item_unref (text_item);
+ free (file_name);
+ }
}
- }
-#endif /* HAVE_CAIRO */
- else if (is_text_item (output_item))
- {
- const struct text_item *text_item = to_text_item (output_item);
- enum text_item_type type = text_item_get_type (text_item);
- const char *text = text_item_get_text (text_item);
+ break;
- switch (type)
+ case OUTPUT_ITEM_CHART:
+ if (a->chart_file_name != NULL)
{
- case TEXT_ITEM_TITLE:
- free (a->title);
- a->title = xstrdup (text);
- break;
-
- case TEXT_ITEM_SUBTITLE:
- free (a->subtitle);
- a->subtitle = xstrdup (text);
- break;
+ char *file_name = xr_draw_png_chart (
+ item->chart, a->chart_file_name, ++a->n_charts, &a->fg, &a->bg);
+ if (file_name != NULL)
+ {
+ struct output_item *text_item = text_item_create_nocopy (
+ TEXT_ITEM_LOG,
+ xasprintf (_("See %s for a chart."), file_name),
+ NULL);
+
+ ascii_submit (driver, text_item);
+ output_item_unref (text_item);
+ free (file_name);
+ }
+ }
+ break;
- case TEXT_ITEM_COMMAND_OPEN:
- case TEXT_ITEM_COMMAND_CLOSE:
- break;
+ case OUTPUT_ITEM_TEXT:
+ if (item->text.subtype != TEXT_ITEM_PAGE_TITLE)
+ ascii_output_table_item_unref (
+ a, text_item_to_table_item (output_item_ref (item)));
+ break;
- case TEXT_ITEM_BLANK_LINE:
- if (a->y > 0)
- a->y++;
- break;
+ case OUTPUT_ITEM_MESSAGE:
+ ascii_output_table_item_unref (
+ a, text_item_to_table_item (
+ message_item_to_text_item (
+ output_item_ref (item))));
+ break;
- case TEXT_ITEM_EJECT_PAGE:
- if (a->y > 0)
- ascii_close_page (a);
- break;
+ case OUTPUT_ITEM_GROUP:
+ break;
- default:
- ascii_output_text (a, text);
- break;
- }
- }
- else if (is_message_item (output_item))
- {
- const struct message_item *message_item = to_message_item (output_item);
- const struct msg *msg = message_item_get_msg (message_item);
- char *s = msg_to_string (msg, a->command_name);
- ascii_output_text (a, s);
- free (s);
+ case OUTPUT_ITEM_PAGE_BREAK:
+ break;
}
}
const struct output_driver_factory txt_driver_factory =
- { "txt", ascii_create };
+ { "txt", "-", ascii_create };
const struct output_driver_factory list_driver_factory =
- { "list", ascii_create };
+ { "list", "-", ascii_create };
static const struct output_driver_class ascii_driver_class =
{
- "text",
- ascii_destroy,
- ascii_submit,
- ascii_flush,
+ .name = "text",
+ .destroy = ascii_destroy,
+ .submit = ascii_submit,
+ .flush = ascii_flush,
};
\f
static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
static void
ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
- enum render_line_style styles[TABLE_N_AXES][2])
+ enum render_line_style styles[TABLE_N_AXES][2],
+ struct cell_color colors[TABLE_N_AXES][2] UNUSED)
{
struct ascii_driver *a = a_;
char mbchar[6];
- int x0, x1, y1;
+ int x0, y0, x1, y1;
ucs4_t uc;
int mblen;
int x, y;
/* Clip to the page. */
- if (bb[H][0] >= a->width || bb[V][0] + a->y >= a->length)
- return;
- x0 = bb[H][0];
+ x0 = MAX (bb[H][0], 0);
+ y0 = MAX (bb[V][0], 0);
x1 = MIN (bb[H][1], a->width);
- y1 = MIN (bb[V][1] + a->y, a->length);
+ y1 = bb[V][1];
+ if (x1 <= 0 || y1 <= 0 || x0 >= a->width)
+ return;
/* Draw. */
- uc = a->box[make_box_index (styles[V][0], styles[V][1],
- styles[H][0], styles[H][1])];
+ uc = box_get (a->box, styles[V][0], styles[V][1], styles[H][0], styles[H][1]);
mblen = u8_uctomb (CHAR_CAST (uint8_t *, mbchar), uc, 6);
- for (y = bb[V][0] + a->y; y < y1; y++)
+ for (y = y0; y < y1; y++)
{
char *p = ascii_reserve (a, y, x0, x1, mblen * (x1 - x0));
for (x = x0; x < x1; x++)
clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
ascii_layout_cell (a, cell, bb, clip, max_width, &h);
- if (strchr (cell->contents, ' '))
- {
- bb[H][1] = 1;
- ascii_layout_cell (a, cell, bb, clip, min_width, &h);
- }
- else
- *min_width = *max_width;
+ bb[H][1] = 1;
+ ascii_layout_cell (a, cell, bb, clip, min_width, &h);
}
static int
}
static void
-ascii_draw_cell (void *a_, const struct table_cell *cell,
- int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
+ascii_draw_cell (void *a_, const struct table_cell *cell, int color_idx UNUSED,
+ int bb[TABLE_N_AXES][2], int valign_offset,
+ int spill[TABLE_N_AXES][2] UNUSED,
+ int clip[TABLE_N_AXES][2])
{
struct ascii_driver *a = a_;
int w, h;
+ bb[V][0] += valign_offset;
ascii_layout_cell (a, cell, bb, clip, &w, &h);
}
-static int
-u8_mb_to_display (int *wp, const uint8_t *s, size_t n)
-{
- size_t ofs;
- ucs4_t uc;
- int w;
-
- ofs = u8_mbtouc (&uc, s, n);
- if (ofs < n && s[ofs] == '\b')
- {
- ofs++;
- ofs += u8_mbtouc (&uc, s + ofs, n - ofs);
- }
-
- w = uc_width (uc, "UTF-8");
- if (w <= 0)
- {
- *wp = 0;
- return ofs;
- }
-
- while (ofs < n)
- {
- int mblen = u8_mbtouc (&uc, s + ofs, n - ofs);
- if (uc_width (uc, "UTF-8") > 0)
- break;
- ofs += mblen;
- }
-
- *wp = w;
- return ofs;
-}
-
-struct ascii_pos
- {
- int x0;
- int x1;
- size_t ofs0;
- size_t ofs1;
- };
-
-static void
-find_ascii_pos (struct ascii_line *line, int target_x, struct ascii_pos *c)
-{
- const uint8_t *s = CHAR_CAST (const uint8_t *, ds_cstr (&line->s));
- size_t length = ds_length (&line->s);
- size_t ofs;
- int mblen;
- int x;
-
- x = 0;
- for (ofs = 0; ; ofs += mblen)
- {
- int w;
-
- mblen = u8_mb_to_display (&w, s + ofs, length - ofs);
- if (x + w > target_x)
- {
- c->x0 = x;
- c->x1 = x + w;
- c->ofs0 = ofs;
- c->ofs1 = ofs + mblen;
- return;
- }
- x += w;
- }
-}
-
static char *
ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
{
- struct ascii_line *line;
- assert (y < a->allocated_lines);
- line = &a->lines[y];
-
- if (x0 >= line->width)
- {
- /* The common case: adding new characters at the end of a line. */
- ds_put_byte_multiple (&line->s, ' ', x0 - line->width);
- line->width = x1;
- return ds_put_uninit (&line->s, n);
- }
- else if (x0 == x1)
- return NULL;
- else
+ if (y >= a->allocated_lines)
{
- /* An unusual case: overwriting characters in the middle of a line. We
- don't keep any kind of mapping from bytes to display positions, so we
- have to iterate over the whole line starting from the beginning. */
- struct ascii_pos p0, p1;
- char *s;
-
- /* Find the positions of the first and last character. We must find the
- both characters' positions before changing the line, because that
- would prevent finding the other character's position. */
- find_ascii_pos (line, x0, &p0);
- if (x1 < line->width)
- find_ascii_pos (line, x1, &p1);
-
- /* If a double-width character occupies both x0 - 1 and x0, then replace
- its first character width by '?'. */
- s = ds_data (&line->s);
- while (p0.x0 < x0)
- {
- s[p0.ofs0++] = '?';
- p0.x0++;
- }
-
- if (x1 >= line->width)
- {
- ds_truncate (&line->s, p0.ofs0);
- line->width = x1;
- return ds_put_uninit (&line->s, n);
- }
-
- /* If a double-width character occupies both x1 - 1 and x1, then we need
- to replace its second character width by '?'. */
- if (p1.x0 < x1)
- {
- do
- {
- s[--p1.ofs1] = '?';
- p1.x0++;
- }
- while (p1.x0 < x1);
- return ds_splice_uninit (&line->s, p0.ofs0, p1.ofs1 - p0.ofs0, n);
- }
-
- return ds_splice_uninit (&line->s, p0.ofs0, p1.ofs0 - p0.ofs0, n);
+ size_t new_alloc = MAX (25, a->allocated_lines);
+ while (new_alloc <= y)
+ new_alloc = xtimes (new_alloc, 2);
+ a->lines = xnrealloc (a->lines, new_alloc, sizeof *a->lines);
+ for (size_t i = a->allocated_lines; i < new_alloc; i++)
+ u8_line_init (&a->lines[i]);
+ a->allocated_lines = new_alloc;
}
+ return u8_line_reserve (&a->lines[y], x0, x1, n);
}
static void
-text_draw (struct ascii_driver *a, unsigned int options,
+text_draw (struct ascii_driver *a, enum table_halign halign, bool numeric,
+ bool bold, bool underline,
int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
int y, const uint8_t *string, int n, size_t width)
{
int x0 = MAX (0, clip[H][0]);
- int y0 = MAX (0, clip[V][0] + a->y);
- int x1 = clip[H][1];
- int y1 = MIN (a->length, clip[V][1] + a->y);
+ int y0 = MAX (0, clip[V][0]);
+ int x1 = MIN (a->width, clip[H][1]);
+ int y1 = clip[V][1];
int x;
- y += a->y;
if (y < y0 || y >= y1)
return;
- switch (options & TAB_ALIGNMENT)
+ switch (table_halign_interpret (halign, numeric))
{
- case TAB_LEFT:
+ case TABLE_HALIGN_LEFT:
x = bb[H][0];
break;
- case TAB_CENTER:
+ case TABLE_HALIGN_CENTER:
x = (bb[H][0] + bb[H][1] - width + 1) / 2;
break;
- case TAB_RIGHT:
+ case TABLE_HALIGN_RIGHT:
+ case TABLE_HALIGN_DECIMAL:
x = bb[H][1] - width;
break;
default:
int ofs;
ofs = width = 0;
- for (ofs = 0; ofs < n; )
+ for (ofs = 0; ofs < n;)
{
ucs4_t uc;
int mblen;
return;
}
- if (!(options & TAB_EMPH) || a->emphasis == EMPH_NONE)
+ if (!a->emphasis || (!bold && !underline))
memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
else
{
w = uc_width (uc, "UTF-8");
if (w > 0)
- n_out += a->emphasis == EMPH_UNDERLINE ? 2 : 1 + mblen;
+ {
+ if (bold)
+ n_out += 1 + mblen;
+ if (underline)
+ n_out += 2;
+ }
}
/* Then insert them. */
if (w > 0)
{
- if (a->emphasis == EMPH_UNDERLINE)
- *out++ = '_';
- else
- out = mempcpy (out, string + ofs, mblen);
- *out++ = '\b';
+ if (bold)
+ {
+ out = mempcpy (out, string + ofs, mblen);
+ *out++ = '\b';
+ }
+ if (underline)
+ {
+ *out++ = '_';
+ *out++ = '\b';
+ }
}
out = mempcpy (out, string + ofs, mblen);
}
int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
int *widthp, int *heightp)
{
- const char *text = cell->contents;
- size_t length = strlen (text);
- char *breaks;
- int bb_width;
- size_t pos;
- int y;
-
*widthp = 0;
*heightp = 0;
- if (length == 0)
- return;
- breaks = xmalloc (length + 1);
- u8_possible_linebreaks (CHAR_CAST (const uint8_t *, text), length,
- "UTF-8", breaks);
+ struct string body = DS_EMPTY_INITIALIZER;
+ bool numeric = pivot_value_format (cell->value, a->pt, &body);
+
+ /* Calculate length; if it's zero, then there's nothing to do. */
+ if (ds_is_empty (&body))
+ {
+ ds_destroy (&body);
+ return;
+ }
+
+ size_t length = ds_length (&body);
+ const uint8_t *text = CHAR_CAST (uint8_t *, ds_cstr (&body));
+
+ char *breaks = xmalloc (length + 1);
+ u8_possible_linebreaks (text, length, "UTF-8", breaks);
breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
- pos = 0;
- bb_width = bb[H][1] - bb[H][0];
- for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
+ size_t pos = 0;
+ int bb_width = bb[H][1] - bb[H][0];
+ for (int y = bb[V][0]; y < bb[V][1] && pos < length; y++)
{
- const uint8_t *line = CHAR_CAST (const uint8_t *, text + pos);
+ const uint8_t *line = text + pos;
const char *b = breaks + pos;
size_t n = length - pos;
size_t last_break_ofs = 0;
int last_break_width = 0;
int width = 0;
+ size_t graph_ofs;
size_t ofs;
- for (ofs = 0; ofs < n; )
+ for (ofs = 0; ofs < n;)
{
ucs4_t uc;
int mblen;
}
ofs += mblen;
}
- if (b[ofs] != UC_BREAK_MANDATORY)
- {
- while (ofs > 0 && isspace (line[ofs - 1]))
- {
- ofs--;
- width--;
- }
- }
- if (width > *widthp)
- *widthp = width;
+
+ /* Trim any trailing spaces off the end of the text to be drawn. */
+ for (graph_ofs = ofs; graph_ofs > 0; graph_ofs--)
+ if (!isspace (line[graph_ofs - 1]))
+ break;
+ width -= ofs - graph_ofs;
/* Draw text. */
- text_draw (a, cell->options, bb, clip, y, line, ofs, width);
+ text_draw (a, cell->cell_style->halign, numeric,
+ cell->font_style->bold,
+ cell->font_style->underline,
+ bb, clip, y, line, graph_ofs, width);
+
+ /* If a new-line ended the line, just skip the new-line. Otherwise, skip
+ past any spaces past the end of the line (but not past a new-line). */
+ if (b[ofs] == UC_BREAK_MANDATORY)
+ ofs++;
+ else
+ while (ofs < n && isspace (line[ofs]) && b[ofs] != UC_BREAK_MANDATORY)
+ ofs++;
- /* Next line. */
+ if (width > *widthp)
+ *widthp = width;
+ ++*heightp;
pos += ofs;
- if (ofs < n && isspace (line[ofs]))
- pos++;
-
}
- *heightp = y - bb[V][0];
free (breaks);
+ ds_destroy (&body);
}
void
ascii_test_write (struct output_driver *driver,
- const char *s, int x, int y, unsigned int options)
+ const char *s, int x, int y, bool bold, bool underline)
{
struct ascii_driver *a = ascii_driver_cast (driver);
- struct table_cell cell;
int bb[TABLE_N_AXES][2];
int width, height;
- if (a->file == NULL && !ascii_open_page (a))
+ if (!a->file)
return;
- a->y = 0;
- memset (&cell, 0, sizeof cell);
- cell.contents = s;
- cell.options = options | TAB_LEFT;
+ struct cell_style cell_style = { .halign = TABLE_HALIGN_LEFT };
+ struct font_style font_style = {
+ .bold = bold,
+ .underline = underline,
+ };
+ const struct pivot_value value = {
+ .text = {
+ .type = PIVOT_VALUE_TEXT,
+ .local = CONST_CAST (char *, s),
+ .c = CONST_CAST (char *, s),
+ .id = CONST_CAST (char *, s),
+ .user_provided = true,
+ },
+ };
+ struct table_cell cell = {
+ .value = &value,
+ .font_style = &font_style,
+ .cell_style = &cell_style,
+ };
bb[TABLE_HORZ][0] = x;
bb[TABLE_HORZ][1] = a->width;
bb[TABLE_VERT][0] = y;
- bb[TABLE_VERT][1] = a->length;
+ bb[TABLE_VERT][1] = INT_MAX;
+ struct pivot_table pt = {
+ .show_values = SETTINGS_VALUE_SHOW_DEFAULT,
+ .show_variables = SETTINGS_VALUE_SHOW_DEFAULT,
+ };
+ a->pt = &pt;
ascii_layout_cell (a, &cell, bb, bb, &width, &height);
-
- a->y = 1;
+ a->pt = NULL;
}
-\f
-/* ascii_close_page () and support routines. */
-#if HAVE_DECL_SIGWINCH
-static struct ascii_driver *the_driver;
-
-static void
-winch_handler (int signum UNUSED)
-{
- update_page_size (the_driver, false);
-}
-#endif
-
-static bool
-ascii_open_page (struct ascii_driver *a)
+void
+ascii_test_set_length (struct output_driver *driver, int y, int length)
{
- int i;
-
- if (a->error)
- return false;
-
- if (a->file == NULL)
- {
- a->file = fn_open (a->file_name, a->append ? "a" : "w");
- if (a->file != NULL)
- {
- if ( isatty (fileno (a->file)))
- {
-#if HAVE_DECL_SIGWINCH
- struct sigaction action;
- sigemptyset (&action.sa_mask);
- action.sa_flags = 0;
- action.sa_handler = winch_handler;
- the_driver = a;
- sigaction (SIGWINCH, &action, NULL);
-#endif
- a->auto_width = true;
- a->auto_length = true;
- }
- }
- else
- {
- error (0, errno, _("ascii: opening output file `%s'"),
- a->file_name);
- a->error = true;
- return false;
- }
- }
+ struct ascii_driver *a = ascii_driver_cast (driver);
- a->page_number++;
+ if (!a->file)
+ return;
- if (a->length > a->allocated_lines)
- {
- a->lines = xnrealloc (a->lines, a->length, sizeof *a->lines);
- for (i = a->allocated_lines; i < a->length; i++)
- {
- struct ascii_line *line = &a->lines[i];
- ds_init_empty (&line->s);
- line->width = 0;
- }
- a->allocated_lines = a->length;
- }
+ if (y < 0)
+ return;
+ u8_line_set_length (&a->lines[y], length);
+}
- for (i = 0; i < a->length; i++)
- {
- struct ascii_line *line = &a->lines[i];
- ds_clear (&line->s);
- line->width = 0;
- }
+void
+ascii_test_flush (struct output_driver *driver)
+{
+ struct ascii_driver *a = ascii_driver_cast (driver);
- return true;
+ for (size_t i = a->allocated_lines; i-- > 0;)
+ if (a->lines[i].width)
+ {
+ ascii_output_lines (a, i + 1);
+ break;
+ }
}
+\f
+static sig_atomic_t terminal_changed = true;
+static int terminal_width;
+#if HAVE_DECL_SIGWINCH
static void
-output_title_line (FILE *out, int width, const char *left, const char *right)
+winch_handler (int signum UNUSED)
{
- struct string s = DS_EMPTY_INITIALIZER;
- ds_put_byte_multiple (&s, ' ', width);
- if (left != NULL)
- {
- size_t length = MIN (strlen (left), width);
- memcpy (ds_end (&s) - width, left, length);
- }
- if (right != NULL)
- {
- size_t length = MIN (strlen (right), width);
- memcpy (ds_end (&s) - length, right, length);
- }
- ds_put_byte (&s, '\n');
- fputs (ds_cstr (&s), out);
- ds_destroy (&s);
+ terminal_changed = true;
}
+#endif
-static void
-ascii_close_page (struct ascii_driver *a)
+int
+get_terminal_width (void)
{
- bool any_blank;
- int i, y;
-
- a->y = 0;
- if (a->file == NULL)
- return;
-
- if (!a->top_margin && !a->bottom_margin && a->squeeze_blank_lines
- && !a->paginate && a->page_number > 1)
- putc ('\n', a->file);
-
- for (i = 0; i < a->top_margin; i++)
- putc ('\n', a->file);
- if (a->headers)
+#if HAVE_DECL_SIGWINCH
+ static bool setup_signal;
+ if (!setup_signal)
{
- char *r1, *r2;
-
- r1 = xasprintf (_("%s - Page %d"), get_start_date (), a->page_number);
- r2 = xasprintf ("%s - %s" , version, host_system);
+ setup_signal = true;
- output_title_line (a->file, a->width, a->title, r1);
- output_title_line (a->file, a->width, a->subtitle, r2);
- putc ('\n', a->file);
-
- free (r1);
- free (r2);
+ struct sigaction action = { .sa_handler = winch_handler };
+ sigemptyset (&action.sa_mask);
+ sigaction (SIGWINCH, &action, NULL);
}
+#endif
- any_blank = false;
- for (y = 0; y < a->allocated_lines; y++)
+ if (terminal_changed)
{
- struct ascii_line *line = &a->lines[y];
+ terminal_changed = false;
- if (a->squeeze_blank_lines && y > 0 && line->width == 0)
- any_blank = true;
+#ifdef HAVE_TERMIOS_H
+ struct winsize ws;
+ if (!ioctl (0, TIOCGWINSZ, &ws))
+ terminal_width = ws.ws_col;
else
+#endif
{
- if (any_blank)
- {
- putc ('\n', a->file);
- any_blank = false;
- }
-
- while (ds_chomp_byte (&line->s, ' '))
- continue;
- fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
- putc ('\n', a->file);
+ if (getenv ("COLUMNS"))
+ terminal_width = atoi (getenv ("COLUMNS"));
}
+
+ if (terminal_width <= 0 || terminal_width > 1024)
+ terminal_width = 79;
}
- if (!a->squeeze_blank_lines)
- for (y = a->allocated_lines; y < a->length; y++)
- putc ('\n', a->file);
- for (i = 0; i < a->bottom_margin; i++)
- putc ('\n', a->file);
- if (a->paginate)
- putc ('\f', a->file);
+ return terminal_width;
}