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];
453 if (a->file == NULL && !ascii_open_page (a))
456 p = render_pager_create (¶ms, table_item);
457 while (render_pager_has_next (p))
463 used = render_pager_draw_next (p, a->length - a->y);
466 /* Make sure that we're not in a loop where the table we're rendering
467 can't be broken to fit on a page. (The check on
468 render_pager_has_next() allows for objects that turn out to be
469 empty when we try to render them.) */
470 assert (a->y > 0 || !render_pager_has_next (p));
472 ascii_close_page (a);
473 if (!ascii_open_page (a))
479 render_pager_destroy (p);
483 ascii_output_text (struct ascii_driver *a, const char *text)
485 struct table_item *table_item;
487 table_item = table_item_create (table_from_string (TAB_LEFT, text),
489 ascii_output_table_item (a, table_item);
490 table_item_unref (table_item);
494 ascii_submit (struct output_driver *driver,
495 const struct output_item *output_item)
497 struct ascii_driver *a = ascii_driver_cast (driver);
499 output_driver_track_current_command (output_item, &a->command_name);
504 if (is_table_item (output_item))
505 ascii_output_table_item (a, to_table_item (output_item));
507 else if (is_chart_item (output_item) && a->chart_file_name != NULL)
509 struct chart_item *chart_item = to_chart_item (output_item);
512 file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
516 if (file_name != NULL)
518 struct text_item *text_item;
520 text_item = text_item_create_format (
521 TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
523 ascii_submit (driver, &text_item->output_item);
524 text_item_unref (text_item);
528 #endif /* HAVE_CAIRO */
529 else if (is_text_item (output_item))
531 const struct text_item *text_item = to_text_item (output_item);
532 enum text_item_type type = text_item_get_type (text_item);
533 const char *text = text_item_get_text (text_item);
537 case TEXT_ITEM_TITLE:
539 a->title = xstrdup (text);
542 case TEXT_ITEM_SUBTITLE:
544 a->subtitle = xstrdup (text);
547 case TEXT_ITEM_COMMAND_OPEN:
548 case TEXT_ITEM_COMMAND_CLOSE:
551 case TEXT_ITEM_BLANK_LINE:
556 case TEXT_ITEM_EJECT_PAGE:
558 ascii_close_page (a);
562 ascii_output_text (a, text);
566 else if (is_message_item (output_item))
568 const struct message_item *message_item = to_message_item (output_item);
569 const struct msg *msg = message_item_get_msg (message_item);
570 char *s = msg_to_string (msg, a->command_name);
571 ascii_output_text (a, s);
576 const struct output_driver_factory txt_driver_factory =
577 { "txt", "-", ascii_create };
578 const struct output_driver_factory list_driver_factory =
579 { "list", "-", ascii_create };
581 static const struct output_driver_class ascii_driver_class =
589 static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
591 static void ascii_layout_cell (struct ascii_driver *,
592 const struct table_cell *,
594 int bb[TABLE_N_AXES][2],
595 int clip[TABLE_N_AXES][2],
596 int *width, int *height);
599 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
600 enum render_line_style styles[TABLE_N_AXES][2])
602 struct ascii_driver *a = a_;
609 /* Clip to the page. */
610 x0 = MAX (bb[H][0] + a->x, 0);
611 y0 = MAX (bb[V][0] + a->y, 0);
612 x1 = MIN (bb[H][1] + a->x, a->width);
613 y1 = MIN (bb[V][1] + a->y, a->length);
614 if (x1 <= 0 || y1 <= 0 || x0 >= a->width || y0 >= a->length)
618 uc = a->box[make_box_index (styles[V][0], styles[V][1],
619 styles[H][0], styles[H][1])];
620 mblen = u8_uctomb (CHAR_CAST (uint8_t *, mbchar), uc, 6);
621 for (y = y0; y < y1; y++)
623 char *p = ascii_reserve (a, y, x0, x1, mblen * (x1 - x0));
624 for (x = x0; x < x1; x++)
626 memcpy (p, mbchar, mblen);
633 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
634 int footnote_idx, int *min_width, int *max_width)
636 struct ascii_driver *a = a_;
637 int bb[TABLE_N_AXES][2];
638 int clip[TABLE_N_AXES][2];
645 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
646 ascii_layout_cell (a, cell, footnote_idx, bb, clip, max_width, &h);
648 if (cell->n_contents != 1
649 || cell->contents[0].n_footnotes
650 || strchr (cell->contents[0].text, ' '))
653 ascii_layout_cell (a, cell, footnote_idx, bb, clip, min_width, &h);
656 *min_width = *max_width;
660 ascii_measure_cell_height (void *a_, const struct table_cell *cell,
661 int footnote_idx, int width)
663 struct ascii_driver *a = a_;
664 int bb[TABLE_N_AXES][2];
665 int clip[TABLE_N_AXES][2];
672 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
673 ascii_layout_cell (a, cell, footnote_idx, bb, clip, &w, &h);
678 ascii_draw_cell (void *a_, const struct table_cell *cell, int footnote_idx,
679 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
681 struct ascii_driver *a = a_;
684 ascii_layout_cell (a, cell, footnote_idx, bb, clip, &w, &h);
688 ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
690 assert (y < a->allocated_lines);
691 return u8_line_reserve (&a->lines[y], x0, x1, n);
695 text_draw (struct ascii_driver *a, unsigned int options,
696 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
697 int y, const uint8_t *string, int n, size_t width)
699 int x0 = MAX (0, clip[H][0] + a->x);
700 int y0 = MAX (0, clip[V][0] + a->y);
701 int x1 = MIN (a->width, clip[H][1] + a->x);
702 int y1 = MIN (a->length, clip[V][1] + a->y);
706 if (y < y0 || y >= y1)
709 switch (options & TAB_ALIGNMENT)
715 x = (bb[H][0] + bb[H][1] - width + 1) / 2;
718 x = bb[H][1] - width;
735 mblen = u8_mbtouc (&uc, string, n);
740 w = uc_width (uc, "UTF-8");
755 for (ofs = 0; ofs < n; )
761 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
763 w = uc_width (uc, "UTF-8");
766 if (width + w > x1 - x)
777 if (!(options & TAB_EMPH) || a->emphasis == EMPH_NONE)
778 memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
786 /* First figure out how many bytes need to be inserted. */
788 for (ofs = 0; ofs < n; ofs += mblen)
793 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
794 w = uc_width (uc, "UTF-8");
797 n_out += a->emphasis == EMPH_UNDERLINE ? 2 : 1 + mblen;
800 /* Then insert them. */
801 out = ascii_reserve (a, y, x, x + width, n_out);
802 for (ofs = 0; ofs < n; ofs += mblen)
807 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
808 w = uc_width (uc, "UTF-8");
812 if (a->emphasis == EMPH_UNDERLINE)
815 out = mempcpy (out, string + ofs, mblen);
818 out = mempcpy (out, string + ofs, mblen);
824 ascii_layout_cell_text (struct ascii_driver *a,
825 const struct cell_contents *contents, int *footnote_idx,
826 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
837 length = strlen (contents->text);
838 if (contents->n_footnotes)
844 ds_extend (&s, length + contents->n_footnotes * 4);
845 ds_put_cstr (&s, contents->text);
846 for (i = 0; i < contents->n_footnotes; i++)
850 str_format_26adic (++*footnote_idx, false, marker, sizeof marker);
851 ds_put_format (&s, "[%s]", marker);
854 length = ds_length (&s);
855 text = ds_steal_cstr (&s);
861 text = contents->text;
864 breaks = xmalloc (length + 1);
865 u8_possible_linebreaks (CHAR_CAST (const uint8_t *, text), length,
867 breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
868 ? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
871 bb_width = bb[H][1] - bb[H][0];
872 for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
874 const uint8_t *line = CHAR_CAST (const uint8_t *, text + pos);
875 const char *b = breaks + pos;
876 size_t n = length - pos;
878 size_t last_break_ofs = 0;
879 int last_break_width = 0;
884 for (ofs = 0; ofs < n; )
890 mblen = u8_mbtouc (&uc, line + ofs, n - ofs);
891 if (b[ofs] == UC_BREAK_MANDATORY)
893 else if (b[ofs] == UC_BREAK_POSSIBLE)
895 last_break_ofs = ofs;
896 last_break_width = width;
899 w = uc_width (uc, "UTF-8");
902 if (width + w > bb_width)
904 if (isspace (line[ofs]))
906 else if (last_break_ofs != 0)
908 ofs = last_break_ofs;
909 width = last_break_width;
918 /* Trim any trailing spaces off the end of the text to be drawn. */
919 for (graph_ofs = ofs; graph_ofs > 0; graph_ofs--)
920 if (!isspace (line[graph_ofs - 1]))
922 width -= ofs - graph_ofs;
925 text_draw (a, contents->options, bb, clip, y, line, graph_ofs, width);
927 /* If a new-line ended the line, just skip the new-line. Otherwise, skip
928 past any spaces past the end of the line (but not past a new-line). */
929 if (b[ofs] == UC_BREAK_MANDATORY)
932 while (ofs < n && isspace (line[ofs]) && b[ofs] != UC_BREAK_MANDATORY)
941 if (text != contents->text)
942 free (CONST_CAST (char *, text));
948 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
950 int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
951 int *widthp, int *heightp)
953 int bb[TABLE_N_AXES][2];
959 memcpy (bb, bb_, sizeof bb);
960 for (i = 0; i < cell->n_contents && bb[V][0] < bb[V][1]; i++)
962 const struct cell_contents *contents = &cell->contents[i];
964 /* Put a blank line between contents. */
968 if (bb[V][0] >= bb[V][1])
972 bb[V][0] = ascii_layout_cell_text (a, contents, &footnote_idx,
975 *heightp = bb[V][0] - bb_[V][0];
979 ascii_test_write (struct output_driver *driver,
980 const char *s, int x, int y, unsigned int options)
982 struct ascii_driver *a = ascii_driver_cast (driver);
983 struct cell_contents contents;
984 struct table_cell cell;
985 int bb[TABLE_N_AXES][2];
988 if (a->file == NULL && !ascii_open_page (a))
992 contents.options = options | TAB_LEFT;
993 contents.text = CONST_CAST (char *, s);
994 contents.n_footnotes = 0;
996 memset (&cell, 0, sizeof cell);
997 cell.contents = &contents;
1000 bb[TABLE_HORZ][0] = x;
1001 bb[TABLE_HORZ][1] = a->width;
1002 bb[TABLE_VERT][0] = y;
1003 bb[TABLE_VERT][1] = a->length;
1005 ascii_layout_cell (a, &cell, 0, bb, bb, &width, &height);
1011 ascii_test_set_length (struct output_driver *driver, int y, int length)
1013 struct ascii_driver *a = ascii_driver_cast (driver);
1015 if (a->file == NULL && !ascii_open_page (a))
1018 if (y < 0 || y >= a->length)
1020 u8_line_set_length (&a->lines[y], length);
1023 /* ascii_close_page () and support routines. */
1025 #if HAVE_DECL_SIGWINCH
1026 static struct ascii_driver *the_driver;
1029 winch_handler (int signum UNUSED)
1031 update_page_size (the_driver, false);
1036 ascii_open_page (struct ascii_driver *a)
1043 if (a->file == NULL)
1045 a->file = fn_open (a->handle, a->append ? "a" : "w");
1046 if (a->file != NULL)
1048 if ( isatty (fileno (a->file)))
1050 #if HAVE_DECL_SIGWINCH
1051 struct sigaction action;
1052 sigemptyset (&action.sa_mask);
1053 action.sa_flags = 0;
1054 action.sa_handler = winch_handler;
1056 sigaction (SIGWINCH, &action, NULL);
1058 a->auto_width = true;
1059 a->auto_length = true;
1064 msg_error (errno, _("ascii: opening output file `%s'"),
1065 fh_get_file_name (a->handle));
1073 reallocate_lines (a);
1075 for (i = 0; i < a->length; i++)
1076 u8_line_clear (&a->lines[i]);
1082 output_title_line (FILE *out, int width, const char *left, const char *right)
1084 struct string s = DS_EMPTY_INITIALIZER;
1085 ds_put_byte_multiple (&s, ' ', width);
1088 size_t length = MIN (strlen (left), width);
1089 memcpy (ds_end (&s) - width, left, length);
1093 size_t length = MIN (strlen (right), width);
1094 memcpy (ds_end (&s) - length, right, length);
1096 ds_put_byte (&s, '\n');
1097 fputs (ds_cstr (&s), out);
1102 ascii_close_page (struct ascii_driver *a)
1108 if (a->file == NULL)
1111 if (!a->top_margin && !a->bottom_margin && a->squeeze_blank_lines
1112 && !a->paginate && a->page_number > 1)
1113 putc ('\n', a->file);
1115 for (i = 0; i < a->top_margin; i++)
1116 putc ('\n', a->file);
1121 r1 = xasprintf (_("%s - Page %d"), get_start_date (), a->page_number);
1122 r2 = xasprintf ("%s - %s" , version, host_system);
1124 output_title_line (a->file, a->width, a->title, r1);
1125 output_title_line (a->file, a->width, a->subtitle, r2);
1126 putc ('\n', a->file);
1133 for (y = 0; y < a->allocated_lines; y++)
1135 struct u8_line *line = &a->lines[y];
1137 if (a->squeeze_blank_lines && y > 0 && line->width == 0)
1143 putc ('\n', a->file);
1147 while (ds_chomp_byte (&line->s, ' '))
1149 fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
1150 putc ('\n', a->file);
1153 if (!a->squeeze_blank_lines)
1154 for (y = a->allocated_lines; y < a->length; y++)
1155 putc ('\n', a->file);
1157 for (i = 0; i < a->bottom_margin; i++)
1158 putc ('\n', a->file);
1160 putc ('\f', a->file);