1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
30 #include "data/file-name.h"
31 #include "data/file-handle-def.h"
32 #include "data/settings.h"
33 #include "libpspp/assertion.h"
34 #include "libpspp/cast.h"
35 #include "libpspp/compiler.h"
36 #include "libpspp/message.h"
37 #include "libpspp/start-date.h"
38 #include "libpspp/string-map.h"
39 #include "libpspp/u8-line.h"
40 #include "libpspp/version.h"
41 #include "output/ascii.h"
42 #include "output/cairo.h"
43 #include "output/chart-item-provider.h"
44 #include "output/driver-provider.h"
45 #include "output/message-item.h"
46 #include "output/options.h"
47 #include "output/render.h"
48 #include "output/tab.h"
49 #include "output/table-item.h"
50 #include "output/text-item.h"
52 #include "gl/minmax.h"
53 #include "gl/xalloc.h"
56 #define _(msgid) gettext (msgid)
58 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
62 #define N_BOX (RENDER_N_LINES * RENDER_N_LINES \
63 * RENDER_N_LINES * RENDER_N_LINES)
65 static const ucs4_t ascii_box_chars[N_BOX] =
96 static const ucs4_t unicode_box_chars[N_BOX] =
98 0x0020, 0x2575, 0x2551,
99 0x2574, 0x256f, 0x255c,
100 0x2550, 0x255b, 0x255d,
101 0x2577, 0x2502, 0x2551,
102 0x256e, 0x2524, 0x2562,
103 0x2555, 0x2561, 0x2563,
104 0x2551, 0x2551, 0x2551,
105 0x2556, 0x2562, 0x2562,
106 0x2557, 0x2563, 0x2563,
107 0x2576, 0x2570, 0x2559,
108 0x2500, 0x2534, 0x2568,
109 0x2550, 0x2567, 0x2569,
110 0x256d, 0x251c, 0x255f,
111 0x252c, 0x253c, 0x256a,
112 0x2564, 0x256a, 0x256c,
113 0x2553, 0x255f, 0x255f,
114 0x2565, 0x256b, 0x256b,
115 0x2566, 0x256c, 0x256c,
116 0x2550, 0x2558, 0x255a,
117 0x2550, 0x2567, 0x2569,
118 0x2550, 0x2567, 0x2569,
119 0x2552, 0x255e, 0x2560,
120 0x2564, 0x256a, 0x256c,
121 0x2564, 0x256a, 0x256c,
122 0x2554, 0x2560, 0x2560,
123 0x2560, 0x256c, 0x256c,
124 0x2566, 0x256c, 0x256c,
128 make_box_index (int left, int right, int top, int bottom)
130 int start_side = left;
131 int end_side = right;
132 if (render_direction_rtl ())
138 return ((end_side * RENDER_N_LINES + bottom) * RENDER_N_LINES + start_side) * RENDER_N_LINES + top;
141 /* How to emphasize text. */
144 EMPH_BOLD, /* Overstrike for bold. */
145 EMPH_UNDERLINE, /* Overstrike for underlining. */
146 EMPH_NONE /* No emphasis. */
149 /* ASCII output driver. */
152 struct output_driver driver;
154 /* User parameters. */
155 bool append; /* Append if output file already exists? */
156 bool headers; /* Print headers at top of page? */
157 bool paginate; /* Insert formfeeds? */
158 bool squeeze_blank_lines; /* Squeeze multiple blank lines into one? */
159 enum emphasis_style emphasis; /* How to emphasize text. */
160 char *chart_file_name; /* Name of files used for charts. */
163 /* Colours for charts */
168 int width; /* Page width. */
169 int length; /* Page length minus margins and header. */
170 bool auto_width; /* Use viewwidth as page width? */
171 bool auto_length; /* Use viewlength as page width? */
173 int top_margin; /* Top margin in lines. */
174 int bottom_margin; /* Bottom margin in lines. */
176 int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
178 const ucs4_t *box; /* Line & box drawing characters. */
180 /* Internal state. */
184 struct file_handle *handle;
185 FILE *file; /* Output file. */
186 bool error; /* Output error? */
187 int page_number; /* Current page number. */
188 struct u8_line *lines; /* Page content. */
189 int allocated_lines; /* Number of lines allocated. */
190 int chart_cnt; /* Number of charts so far. */
194 static const struct output_driver_class ascii_driver_class;
196 static void ascii_submit (struct output_driver *, const struct output_item *);
198 static int vertical_margins (const struct ascii_driver *);
200 static bool update_page_size (struct ascii_driver *, bool issue_error);
201 static int parse_page_size (struct driver_option *);
203 static void ascii_close_page (struct ascii_driver *);
204 static bool ascii_open_page (struct ascii_driver *);
206 static void ascii_draw_line (void *, int bb[TABLE_N_AXES][2],
207 enum render_line_style styles[TABLE_N_AXES][2]);
208 static void ascii_measure_cell_width (void *, const struct table_cell *,
209 int footnote_idx, int *min, int *max);
210 static int ascii_measure_cell_height (void *, const struct table_cell *,
211 int footnote_idx, int width);
212 static void ascii_draw_cell (void *, const struct table_cell *,
213 int footnote_idx, int bb[TABLE_N_AXES][2],
214 int clip[TABLE_N_AXES][2]);
217 reallocate_lines (struct ascii_driver *a)
219 if (a->length > a->allocated_lines)
222 a->lines = xnrealloc (a->lines, a->length, sizeof *a->lines);
223 for (i = a->allocated_lines; i < a->length; i++)
224 u8_line_init (&a->lines[i]);
225 a->allocated_lines = a->length;
230 static struct ascii_driver *
231 ascii_driver_cast (struct output_driver *driver)
233 assert (driver->class == &ascii_driver_class);
234 return UP_CAST (driver, struct ascii_driver, driver);
237 static struct driver_option *
238 opt (struct output_driver *d, struct string_map *options, const char *key,
239 const char *default_value)
241 return driver_option_get (d, options, key, default_value);
244 static struct output_driver *
245 ascii_create (struct file_handle *fh, enum settings_output_devices device_type,
246 struct string_map *o)
248 enum { BOX_ASCII, BOX_UNICODE } box;
249 int min_break[TABLE_N_AXES];
250 struct output_driver *d;
251 struct ascii_driver *a;
254 a = xzalloc (sizeof *a);
256 output_driver_init (&a->driver, &ascii_driver_class, fh_get_file_name (fh), device_type);
257 a->append = parse_boolean (opt (d, o, "append", "false"));
258 a->headers = parse_boolean (opt (d, o, "headers", "false"));
259 a->paginate = parse_boolean (opt (d, o, "paginate", "false"));
260 a->squeeze_blank_lines = parse_boolean (opt (d, o, "squeeze", "true"));
261 a->emphasis = parse_enum (opt (d, o, "emphasis", "none"),
263 "underline", EMPH_UNDERLINE,
267 a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", fh_get_file_name (fh)));
270 a->top_margin = parse_int (opt (d, o, "top-margin", "0"), 0, INT_MAX);
271 a->bottom_margin = parse_int (opt (d, o, "bottom-margin", "0"), 0, INT_MAX);
273 min_break[H] = parse_int (opt (d, o, "min-hbreak", "-1"), -1, INT_MAX);
274 min_break[V] = parse_int (opt (d, o, "min-vbreak", "-1"), -1, INT_MAX);
276 a->width = parse_page_size (opt (d, o, "width", "79"));
277 paper_length = parse_page_size (opt (d, o, "length", "66"));
278 a->auto_width = a->width < 0;
279 a->auto_length = paper_length < 0;
280 a->length = paper_length - vertical_margins (a);
281 a->min_break[H] = min_break[H] >= 0 ? min_break[H] : a->width / 2;
282 a->min_break[V] = min_break[V] >= 0 ? min_break[V] : a->length / 2;
284 parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &a->bg);
285 parse_color (d, o, "foreground-color", "#000000000000", &a->fg);
287 box = parse_enum (opt (d, o, "box", "ascii"),
289 "unicode", BOX_UNICODE,
291 a->box = box == BOX_ASCII ? ascii_box_chars : unicode_box_chars;
293 a->command_name = NULL;
294 a->title = xstrdup ("");
295 a->subtitle = xstrdup ("");
300 a->allocated_lines = 0;
303 if (!update_page_size (a, true))
309 output_driver_destroy (d);
314 parse_page_size (struct driver_option *option)
316 int dim = atol (option->default_value);
318 if (option->value != NULL)
320 if (!strcmp (option->value, "auto"))
328 value = strtol (option->value, &tail, 0);
329 if (dim >= 1 && errno != ERANGE && *tail == '\0')
332 msg (MW, _("%s: %s must be positive integer or `auto'"),
333 option->driver_name, option->name);
337 driver_option_destroy (option);
343 vertical_margins (const struct ascii_driver *a)
345 return a->top_margin + a->bottom_margin + (a->headers ? 3 : 0);
348 /* Re-calculates the page width and length based on settings,
349 margins, and, if "auto" is set, the size of the user's
350 terminal window or GUI output window. */
352 update_page_size (struct ascii_driver *a, bool issue_error)
354 enum { MIN_WIDTH = 6, MIN_LENGTH = 6 };
358 a->width = settings_get_viewwidth ();
359 a->min_break[H] = a->width / 2;
363 a->length = settings_get_viewlength () - vertical_margins (a);
364 a->min_break[V] = a->length / 2;
367 if (a->width < MIN_WIDTH || a->length < MIN_LENGTH)
371 _("ascii: page excluding margins and headers "
372 "must be at least %d characters wide by %d lines long, but "
373 "as configured is only %d characters by %d lines"),
374 MIN_WIDTH, MIN_LENGTH,
375 a->width, a->length);
376 if (a->width < MIN_WIDTH)
377 a->width = MIN_WIDTH;
378 if (a->length < MIN_LENGTH)
379 a->length = MIN_LENGTH;
383 reallocate_lines (a);
389 ascii_destroy (struct output_driver *driver)
391 struct ascii_driver *a = ascii_driver_cast (driver);
395 ascii_close_page (a);
398 fn_close (a->handle, a->file);
399 fh_unref (a->handle);
400 free (a->command_name);
403 free (a->chart_file_name);
404 for (i = 0; i < a->allocated_lines; i++)
405 u8_line_destroy (&a->lines[i]);
411 ascii_flush (struct output_driver *driver)
413 struct ascii_driver *a = ascii_driver_cast (driver);
416 ascii_close_page (a);
418 if (fn_close (a->handle, a->file) != 0)
419 msg_error (errno, _("ascii: closing output file `%s'"), fh_get_file_name (a->handle));
425 ascii_output_table_item (struct ascii_driver *a,
426 const struct table_item *table_item)
428 struct render_params params;
429 struct render_pager *p;
432 update_page_size (a, false);
434 params.draw_line = ascii_draw_line;
435 params.measure_cell_width = ascii_measure_cell_width;
436 params.measure_cell_height = ascii_measure_cell_height;
437 params.adjust_break = NULL;
438 params.draw_cell = ascii_draw_cell;
440 params.size[H] = a->width;
441 params.size[V] = a->length;
442 params.font_size[H] = 1;
443 params.font_size[V] = 1;
444 for (i = 0; i < RENDER_N_LINES; i++)
446 int width = i == RENDER_LINE_NONE ? 0 : 1;
447 params.line_widths[H][i] = width;
448 params.line_widths[V][i] = width;
450 for (i = 0; i < TABLE_N_AXES; i++)
451 params.min_break[i] = a->min_break[i];
452 params.supports_margins = false;
454 if (a->file == NULL && !ascii_open_page (a))
457 p = render_pager_create (¶ms, table_item);
458 while (render_pager_has_next (p))
464 used = render_pager_draw_next (p, a->length - a->y);
467 /* Make sure that we're not in a loop where the table we're rendering
468 can't be broken to fit on a page. (The check on
469 render_pager_has_next() allows for objects that turn out to be
470 empty when we try to render them.) */
471 assert (a->y > 0 || !render_pager_has_next (p));
473 ascii_close_page (a);
474 if (!ascii_open_page (a))
480 render_pager_destroy (p);
484 ascii_output_text (struct ascii_driver *a, const char *text)
486 struct table_item *table_item;
488 table_item = table_item_create (table_from_string (TAB_LEFT, text),
490 ascii_output_table_item (a, table_item);
491 table_item_unref (table_item);
495 ascii_submit (struct output_driver *driver,
496 const struct output_item *output_item)
498 struct ascii_driver *a = ascii_driver_cast (driver);
500 output_driver_track_current_command (output_item, &a->command_name);
505 if (is_table_item (output_item))
506 ascii_output_table_item (a, to_table_item (output_item));
508 else if (is_chart_item (output_item) && a->chart_file_name != NULL)
510 struct chart_item *chart_item = to_chart_item (output_item);
513 file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
517 if (file_name != NULL)
519 struct text_item *text_item;
521 text_item = text_item_create_format (
522 TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
524 ascii_submit (driver, &text_item->output_item);
525 text_item_unref (text_item);
529 #endif /* HAVE_CAIRO */
530 else if (is_text_item (output_item))
532 const struct text_item *text_item = to_text_item (output_item);
533 enum text_item_type type = text_item_get_type (text_item);
534 const char *text = text_item_get_text (text_item);
538 case TEXT_ITEM_TITLE:
540 a->title = xstrdup (text);
543 case TEXT_ITEM_SUBTITLE:
545 a->subtitle = xstrdup (text);
548 case TEXT_ITEM_COMMAND_OPEN:
549 case TEXT_ITEM_COMMAND_CLOSE:
552 case TEXT_ITEM_BLANK_LINE:
557 case TEXT_ITEM_EJECT_PAGE:
559 ascii_close_page (a);
563 ascii_output_text (a, text);
567 else if (is_message_item (output_item))
569 const struct message_item *message_item = to_message_item (output_item);
570 const struct msg *msg = message_item_get_msg (message_item);
571 char *s = msg_to_string (msg, a->command_name);
572 ascii_output_text (a, s);
577 const struct output_driver_factory txt_driver_factory =
578 { "txt", "-", ascii_create };
579 const struct output_driver_factory list_driver_factory =
580 { "list", "-", ascii_create };
582 static const struct output_driver_class ascii_driver_class =
590 static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
592 static void ascii_layout_cell (struct ascii_driver *,
593 const struct table_cell *,
595 int bb[TABLE_N_AXES][2],
596 int clip[TABLE_N_AXES][2],
597 int *width, int *height);
600 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
601 enum render_line_style styles[TABLE_N_AXES][2])
603 struct ascii_driver *a = a_;
610 /* Clip to the page. */
611 x0 = MAX (bb[H][0] + a->x, 0);
612 y0 = MAX (bb[V][0] + a->y, 0);
613 x1 = MIN (bb[H][1] + a->x, a->width);
614 y1 = MIN (bb[V][1] + a->y, a->length);
615 if (x1 <= 0 || y1 <= 0 || x0 >= a->width || y0 >= a->length)
619 uc = a->box[make_box_index (styles[V][0], styles[V][1],
620 styles[H][0], styles[H][1])];
621 mblen = u8_uctomb (CHAR_CAST (uint8_t *, mbchar), uc, 6);
622 for (y = y0; y < y1; y++)
624 char *p = ascii_reserve (a, y, x0, x1, mblen * (x1 - x0));
625 for (x = x0; x < x1; x++)
627 memcpy (p, mbchar, mblen);
634 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
635 int footnote_idx, int *min_width, int *max_width)
637 struct ascii_driver *a = a_;
638 int bb[TABLE_N_AXES][2];
639 int clip[TABLE_N_AXES][2];
646 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
647 ascii_layout_cell (a, cell, footnote_idx, bb, clip, max_width, &h);
649 if (cell->n_contents != 1
650 || cell->contents[0].n_footnotes
651 || strchr (cell->contents[0].text, ' '))
654 ascii_layout_cell (a, cell, footnote_idx, bb, clip, min_width, &h);
657 *min_width = *max_width;
661 ascii_measure_cell_height (void *a_, const struct table_cell *cell,
662 int footnote_idx, int width)
664 struct ascii_driver *a = a_;
665 int bb[TABLE_N_AXES][2];
666 int clip[TABLE_N_AXES][2];
673 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
674 ascii_layout_cell (a, cell, footnote_idx, bb, clip, &w, &h);
679 ascii_draw_cell (void *a_, const struct table_cell *cell, int footnote_idx,
680 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
682 struct ascii_driver *a = a_;
685 ascii_layout_cell (a, cell, footnote_idx, bb, clip, &w, &h);
689 ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
691 assert (y < a->allocated_lines);
692 return u8_line_reserve (&a->lines[y], x0, x1, n);
696 text_draw (struct ascii_driver *a, unsigned int options,
697 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
698 int y, const uint8_t *string, int n, size_t width)
700 int x0 = MAX (0, clip[H][0] + a->x);
701 int y0 = MAX (0, clip[V][0] + a->y);
702 int x1 = MIN (a->width, clip[H][1] + a->x);
703 int y1 = MIN (a->length, clip[V][1] + a->y);
707 if (y < y0 || y >= y1)
710 switch (options & TAB_ALIGNMENT)
716 x = (bb[H][0] + bb[H][1] - width + 1) / 2;
719 x = bb[H][1] - width;
736 mblen = u8_mbtouc (&uc, string, n);
741 w = uc_width (uc, "UTF-8");
756 for (ofs = 0; ofs < n; )
762 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
764 w = uc_width (uc, "UTF-8");
767 if (width + w > x1 - x)
778 if (!(options & TAB_EMPH) || a->emphasis == EMPH_NONE)
779 memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
787 /* First figure out how many bytes need to be inserted. */
789 for (ofs = 0; ofs < n; ofs += mblen)
794 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
795 w = uc_width (uc, "UTF-8");
798 n_out += a->emphasis == EMPH_UNDERLINE ? 2 : 1 + mblen;
801 /* Then insert them. */
802 out = ascii_reserve (a, y, x, x + width, n_out);
803 for (ofs = 0; ofs < n; ofs += mblen)
808 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
809 w = uc_width (uc, "UTF-8");
813 if (a->emphasis == EMPH_UNDERLINE)
816 out = mempcpy (out, string + ofs, mblen);
819 out = mempcpy (out, string + ofs, mblen);
825 ascii_layout_cell_text (struct ascii_driver *a,
826 const struct cell_contents *contents, int *footnote_idx,
827 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
838 length = strlen (contents->text);
839 if (contents->n_footnotes)
845 ds_extend (&s, length + contents->n_footnotes * 4);
846 ds_put_cstr (&s, contents->text);
847 for (i = 0; i < contents->n_footnotes; i++)
851 str_format_26adic (++*footnote_idx, false, marker, sizeof marker);
852 ds_put_format (&s, "[%s]", marker);
855 length = ds_length (&s);
856 text = ds_steal_cstr (&s);
862 text = contents->text;
865 breaks = xmalloc (length + 1);
866 u8_possible_linebreaks (CHAR_CAST (const uint8_t *, text), length,
868 breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
869 ? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
872 bb_width = bb[H][1] - bb[H][0];
873 for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
875 const uint8_t *line = CHAR_CAST (const uint8_t *, text + pos);
876 const char *b = breaks + pos;
877 size_t n = length - pos;
879 size_t last_break_ofs = 0;
880 int last_break_width = 0;
885 for (ofs = 0; ofs < n; )
891 mblen = u8_mbtouc (&uc, line + ofs, n - ofs);
892 if (b[ofs] == UC_BREAK_MANDATORY)
894 else if (b[ofs] == UC_BREAK_POSSIBLE)
896 last_break_ofs = ofs;
897 last_break_width = width;
900 w = uc_width (uc, "UTF-8");
903 if (width + w > bb_width)
905 if (isspace (line[ofs]))
907 else if (last_break_ofs != 0)
909 ofs = last_break_ofs;
910 width = last_break_width;
919 /* Trim any trailing spaces off the end of the text to be drawn. */
920 for (graph_ofs = ofs; graph_ofs > 0; graph_ofs--)
921 if (!isspace (line[graph_ofs - 1]))
923 width -= ofs - graph_ofs;
926 text_draw (a, contents->options, bb, clip, y, line, graph_ofs, width);
928 /* If a new-line ended the line, just skip the new-line. Otherwise, skip
929 past any spaces past the end of the line (but not past a new-line). */
930 if (b[ofs] == UC_BREAK_MANDATORY)
933 while (ofs < n && isspace (line[ofs]) && b[ofs] != UC_BREAK_MANDATORY)
942 if (text != contents->text)
943 free (CONST_CAST (char *, text));
949 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
951 int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
952 int *widthp, int *heightp)
954 int bb[TABLE_N_AXES][2];
960 memcpy (bb, bb_, sizeof bb);
961 for (i = 0; i < cell->n_contents && bb[V][0] < bb[V][1]; i++)
963 const struct cell_contents *contents = &cell->contents[i];
965 /* Put a blank line between contents. */
969 if (bb[V][0] >= bb[V][1])
973 bb[V][0] = ascii_layout_cell_text (a, contents, &footnote_idx,
976 *heightp = bb[V][0] - bb_[V][0];
980 ascii_test_write (struct output_driver *driver,
981 const char *s, int x, int y, unsigned int options)
983 struct ascii_driver *a = ascii_driver_cast (driver);
984 struct cell_contents contents;
985 struct table_cell cell;
986 int bb[TABLE_N_AXES][2];
989 if (a->file == NULL && !ascii_open_page (a))
993 contents.options = options | TAB_LEFT;
994 contents.text = CONST_CAST (char *, s);
995 contents.n_footnotes = 0;
997 memset (&cell, 0, sizeof cell);
998 cell.contents = &contents;
1001 bb[TABLE_HORZ][0] = x;
1002 bb[TABLE_HORZ][1] = a->width;
1003 bb[TABLE_VERT][0] = y;
1004 bb[TABLE_VERT][1] = a->length;
1006 ascii_layout_cell (a, &cell, 0, bb, bb, &width, &height);
1012 ascii_test_set_length (struct output_driver *driver, int y, int length)
1014 struct ascii_driver *a = ascii_driver_cast (driver);
1016 if (a->file == NULL && !ascii_open_page (a))
1019 if (y < 0 || y >= a->length)
1021 u8_line_set_length (&a->lines[y], length);
1024 /* ascii_close_page () and support routines. */
1026 #if HAVE_DECL_SIGWINCH
1027 static struct ascii_driver *the_driver;
1030 winch_handler (int signum UNUSED)
1032 update_page_size (the_driver, false);
1037 ascii_open_page (struct ascii_driver *a)
1044 if (a->file == NULL)
1046 a->file = fn_open (a->handle, a->append ? "a" : "w");
1047 if (a->file != NULL)
1049 if ( isatty (fileno (a->file)))
1051 #if HAVE_DECL_SIGWINCH
1052 struct sigaction action;
1053 sigemptyset (&action.sa_mask);
1054 action.sa_flags = 0;
1055 action.sa_handler = winch_handler;
1057 sigaction (SIGWINCH, &action, NULL);
1059 a->auto_width = true;
1060 a->auto_length = true;
1065 msg_error (errno, _("ascii: opening output file `%s'"),
1066 fh_get_file_name (a->handle));
1074 reallocate_lines (a);
1076 for (i = 0; i < a->length; i++)
1077 u8_line_clear (&a->lines[i]);
1083 output_title_line (FILE *out, int width, const char *left, const char *right)
1085 struct string s = DS_EMPTY_INITIALIZER;
1086 ds_put_byte_multiple (&s, ' ', width);
1089 size_t length = MIN (strlen (left), width);
1090 memcpy (ds_end (&s) - width, left, length);
1094 size_t length = MIN (strlen (right), width);
1095 memcpy (ds_end (&s) - length, right, length);
1097 ds_put_byte (&s, '\n');
1098 fputs (ds_cstr (&s), out);
1103 ascii_close_page (struct ascii_driver *a)
1109 if (a->file == NULL)
1112 if (!a->top_margin && !a->bottom_margin && a->squeeze_blank_lines
1113 && !a->paginate && a->page_number > 1)
1114 putc ('\n', a->file);
1116 for (i = 0; i < a->top_margin; i++)
1117 putc ('\n', a->file);
1122 r1 = xasprintf (_("%s - Page %d"), get_start_date (), a->page_number);
1123 r2 = xasprintf ("%s - %s" , version, host_system);
1125 output_title_line (a->file, a->width, a->title, r1);
1126 output_title_line (a->file, a->width, a->subtitle, r2);
1127 putc ('\n', a->file);
1134 for (y = 0; y < a->allocated_lines; y++)
1136 struct u8_line *line = &a->lines[y];
1138 if (a->squeeze_blank_lines && y > 0 && line->width == 0)
1144 putc ('\n', a->file);
1148 while (ds_chomp_byte (&line->s, ' '))
1150 fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
1151 putc ('\n', a->file);
1154 if (!a->squeeze_blank_lines)
1155 for (y = a->allocated_lines; y < a->length; y++)
1156 putc ('\n', a->file);
1158 for (i = 0; i < a->bottom_margin; i++)
1159 putc ('\n', a->file);
1161 putc ('\f', a->file);