1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011 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/settings.h"
32 #include "libpspp/assertion.h"
33 #include "libpspp/cast.h"
34 #include "libpspp/compiler.h"
35 #include "libpspp/message.h"
36 #include "libpspp/start-date.h"
37 #include "libpspp/string-map.h"
38 #include "libpspp/version.h"
39 #include "output/ascii.h"
40 #include "output/cairo.h"
41 #include "output/chart-item-provider.h"
42 #include "output/driver-provider.h"
43 #include "output/message-item.h"
44 #include "output/options.h"
45 #include "output/render.h"
46 #include "output/tab.h"
47 #include "output/table-item.h"
48 #include "output/text-item.h"
51 #include "gl/minmax.h"
52 #include "gl/xalloc.h"
55 #define _(msgid) gettext (msgid)
57 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
61 #define N_BOX (RENDER_N_LINES * RENDER_N_LINES \
62 * RENDER_N_LINES * RENDER_N_LINES)
64 static const ucs4_t ascii_box_chars[N_BOX] =
95 static const ucs4_t unicode_box_chars[N_BOX] =
97 0x0020, 0x2575, 0x2551,
98 0x2574, 0x256f, 0x255c,
99 0x2550, 0x255b, 0x255d,
100 0x2577, 0x2502, 0x2551,
101 0x256e, 0x2524, 0x2562,
102 0x2555, 0x2561, 0x2563,
103 0x2551, 0x2551, 0x2551,
104 0x2556, 0x2562, 0x2562,
105 0x2557, 0x2563, 0x2563,
106 0x2576, 0x2570, 0x2559,
107 0x2500, 0x2534, 0x2568,
108 0x2550, 0x2567, 0x2569,
109 0x256d, 0x251c, 0x255f,
110 0x252c, 0x253c, 0x256a,
111 0x2564, 0x256a, 0x256c,
112 0x2553, 0x255f, 0x255f,
113 0x2565, 0x256b, 0x256b,
114 0x2566, 0x256c, 0x256c,
115 0x2550, 0x2558, 0x255a,
116 0x2550, 0x2567, 0x2569,
117 0x2550, 0x2567, 0x2569,
118 0x2552, 0x255e, 0x2560,
119 0x2564, 0x256a, 0x256c,
120 0x2564, 0x256a, 0x256c,
121 0x2554, 0x2560, 0x2560,
122 0x2566, 0x256c, 0x256c,
126 make_box_index (int left, int right, int top, int bottom)
128 return ((right * 3 + bottom) * 3 + left) * 3 + top;
131 /* A line of text. */
134 struct string s; /* Content, in UTF-8. */
135 size_t width; /* Display width, in character positions. */
138 /* How to emphasize text. */
141 EMPH_BOLD, /* Overstrike for bold. */
142 EMPH_UNDERLINE, /* Overstrike for underlining. */
143 EMPH_NONE /* No emphasis. */
146 /* ASCII output driver. */
149 struct output_driver driver;
151 /* User parameters. */
152 bool append; /* Append if output file already exists? */
153 bool headers; /* Print headers at top of page? */
154 bool paginate; /* Insert formfeeds? */
155 bool squeeze_blank_lines; /* Squeeze multiple blank lines into one? */
156 enum emphasis_style emphasis; /* How to emphasize text. */
157 char *chart_file_name; /* Name of files used for charts. */
159 int width; /* Page width. */
160 int length; /* Page length minus margins and header. */
161 bool auto_width; /* Use viewwidth as page width? */
162 bool auto_length; /* Use viewlength as page width? */
164 int top_margin; /* Top margin in lines. */
165 int bottom_margin; /* Bottom margin in lines. */
167 const ucs4_t *box; /* Line & box drawing characters. */
169 /* Internal state. */
173 char *file_name; /* Output file name. */
174 FILE *file; /* Output file. */
175 bool error; /* Output error? */
176 int page_number; /* Current page number. */
177 struct ascii_line *lines; /* Page content. */
178 int allocated_lines; /* Number of lines allocated. */
179 int chart_cnt; /* Number of charts so far. */
183 static const struct output_driver_class ascii_driver_class;
185 static void ascii_submit (struct output_driver *, const struct output_item *);
187 static int vertical_margins (const struct ascii_driver *);
189 static bool update_page_size (struct ascii_driver *, bool issue_error);
190 static int parse_page_size (struct driver_option *);
192 static void ascii_close_page (struct ascii_driver *);
193 static bool ascii_open_page (struct ascii_driver *);
195 static void ascii_draw_line (void *, int bb[TABLE_N_AXES][2],
196 enum render_line_style styles[TABLE_N_AXES][2]);
197 static void ascii_measure_cell_width (void *, const struct table_cell *,
199 static int ascii_measure_cell_height (void *, const struct table_cell *,
201 static void ascii_draw_cell (void *, const struct table_cell *,
202 int bb[TABLE_N_AXES][2],
203 int clip[TABLE_N_AXES][2]);
206 reallocate_lines (struct ascii_driver *a)
208 if (a->length > a->allocated_lines)
211 a->lines = xnrealloc (a->lines, a->length, sizeof *a->lines);
212 for (i = a->allocated_lines; i < a->length; i++)
214 struct ascii_line *line = &a->lines[i];
215 ds_init_empty (&line->s);
218 a->allocated_lines = a->length;
223 static struct ascii_driver *
224 ascii_driver_cast (struct output_driver *driver)
226 assert (driver->class == &ascii_driver_class);
227 return UP_CAST (driver, struct ascii_driver, driver);
230 static struct driver_option *
231 opt (struct output_driver *d, struct string_map *options, const char *key,
232 const char *default_value)
234 return driver_option_get (d, options, key, default_value);
237 static struct output_driver *
238 ascii_create (const char *file_name, enum settings_output_devices device_type,
239 struct string_map *o)
241 enum { BOX_ASCII, BOX_UNICODE } box;
242 struct output_driver *d;
243 struct ascii_driver *a;
246 a = xzalloc (sizeof *a);
248 output_driver_init (&a->driver, &ascii_driver_class, file_name, device_type);
249 a->append = parse_boolean (opt (d, o, "append", "false"));
250 a->headers = parse_boolean (opt (d, o, "headers", "false"));
251 a->paginate = parse_boolean (opt (d, o, "paginate", "false"));
252 a->squeeze_blank_lines = parse_boolean (opt (d, o, "squeeze", "true"));
253 a->emphasis = parse_enum (opt (d, o, "emphasis", "none"),
255 "underline", EMPH_UNDERLINE,
259 a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", file_name));
261 a->top_margin = parse_int (opt (d, o, "top-margin", "0"), 0, INT_MAX);
262 a->bottom_margin = parse_int (opt (d, o, "bottom-margin", "0"), 0, INT_MAX);
264 a->width = parse_page_size (opt (d, o, "width", "79"));
265 paper_length = parse_page_size (opt (d, o, "length", "66"));
266 a->auto_width = a->width < 0;
267 a->auto_length = paper_length < 0;
268 a->length = paper_length - vertical_margins (a);
270 box = parse_enum (opt (d, o, "box", "ascii"),
272 "unicode", BOX_UNICODE,
274 a->box = box == BOX_ASCII ? ascii_box_chars : unicode_box_chars;
276 a->command_name = NULL;
277 a->title = xstrdup ("");
278 a->subtitle = xstrdup ("");
279 a->file_name = xstrdup (file_name);
284 a->allocated_lines = 0;
287 if (!update_page_size (a, true))
293 output_driver_destroy (d);
298 parse_page_size (struct driver_option *option)
300 int dim = atol (option->default_value);
302 if (option->value != NULL)
304 if (!strcmp (option->value, "auto"))
312 value = strtol (option->value, &tail, 0);
313 if (dim >= 1 && errno != ERANGE && *tail == '\0')
316 error (0, 0, _("%s: %s must be positive integer or `auto'"),
317 option->driver_name, option->name);
321 driver_option_destroy (option);
327 vertical_margins (const struct ascii_driver *a)
329 return a->top_margin + a->bottom_margin + (a->headers ? 3 : 0);
332 /* Re-calculates the page width and length based on settings,
333 margins, and, if "auto" is set, the size of the user's
334 terminal window or GUI output window. */
336 update_page_size (struct ascii_driver *a, bool issue_error)
338 enum { MIN_WIDTH = 6, MIN_LENGTH = 6 };
341 a->width = settings_get_viewwidth ();
343 a->length = settings_get_viewlength () - vertical_margins (a);
345 if (a->width < MIN_WIDTH || a->length < MIN_LENGTH)
349 _("ascii: page excluding margins and headers "
350 "must be at least %d characters wide by %d lines long, but "
351 "as configured is only %d characters by %d lines"),
352 MIN_WIDTH, MIN_LENGTH,
353 a->width, a->length);
354 if (a->width < MIN_WIDTH)
355 a->width = MIN_WIDTH;
356 if (a->length < MIN_LENGTH)
357 a->length = MIN_LENGTH;
361 reallocate_lines (a);
367 ascii_destroy (struct output_driver *driver)
369 struct ascii_driver *a = ascii_driver_cast (driver);
373 ascii_close_page (a);
376 fn_close (a->file_name, a->file);
377 free (a->command_name);
381 free (a->chart_file_name);
382 for (i = 0; i < a->allocated_lines; i++)
383 ds_destroy (&a->lines[i].s);
389 ascii_flush (struct output_driver *driver)
391 struct ascii_driver *a = ascii_driver_cast (driver);
394 ascii_close_page (a);
396 if (fn_close (a->file_name, a->file) != 0)
397 error (0, errno, _("ascii: closing output file `%s'"),
404 ascii_init_caption_cell (const char *caption, struct table_cell *cell)
406 cell->contents = caption;
407 cell->options = TAB_LEFT;
408 cell->destructor = NULL;
412 ascii_output_table_item (struct ascii_driver *a,
413 const struct table_item *table_item)
415 const char *caption = table_item_get_caption (table_item);
416 struct render_params params;
417 struct render_page *page;
418 struct render_break x_break;
422 update_page_size (a, false);
426 /* XXX doesn't do well with very large captions */
427 struct table_cell cell;
428 ascii_init_caption_cell (caption, &cell);
429 caption_height = ascii_measure_cell_height (a, &cell, a->width);
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.draw_cell = ascii_draw_cell,
439 params.size[H] = a->width;
440 params.size[V] = a->length - caption_height;
441 params.font_size[H] = 1;
442 params.font_size[V] = 1;
443 for (i = 0; i < RENDER_N_LINES; i++)
445 int width = i == RENDER_LINE_NONE ? 0 : 1;
446 params.line_widths[H][i] = width;
447 params.line_widths[V][i] = width;
450 if (a->file == NULL && !ascii_open_page (a))
453 page = render_page_create (¶ms, table_item_get_table (table_item));
454 for (render_break_init (&x_break, page, H);
455 render_break_has_next (&x_break); )
457 struct render_page *x_slice;
458 struct render_break y_break;
460 x_slice = render_break_next (&x_break, a->width);
461 for (render_break_init (&y_break, x_slice, V);
462 render_break_has_next (&y_break); )
464 struct render_page *y_slice;
470 space = a->length - a->y - caption_height;
471 if (render_break_next_size (&y_break) > space)
474 ascii_close_page (a);
475 if (!ascii_open_page (a))
480 y_slice = render_break_next (&y_break, space);
483 struct table_cell cell;
484 int bb[TABLE_N_AXES][2];
486 ascii_init_caption_cell (caption, &cell);
490 bb[V][1] = caption_height;
491 ascii_draw_cell (a, &cell, bb, bb);
492 a->y += caption_height;
495 render_page_draw (y_slice);
496 a->y += render_page_get_size (y_slice, V);
497 render_page_unref (y_slice);
499 render_break_destroy (&y_break);
501 render_break_destroy (&x_break);
505 ascii_output_text (struct ascii_driver *a, const char *text)
507 struct table_item *table_item;
509 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
510 ascii_output_table_item (a, table_item);
511 table_item_unref (table_item);
515 ascii_submit (struct output_driver *driver,
516 const struct output_item *output_item)
518 struct ascii_driver *a = ascii_driver_cast (driver);
520 output_driver_track_current_command (output_item, &a->command_name);
525 if (is_table_item (output_item))
526 ascii_output_table_item (a, to_table_item (output_item));
528 else if (is_chart_item (output_item) && a->chart_file_name != NULL)
530 struct chart_item *chart_item = to_chart_item (output_item);
533 file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
535 if (file_name != NULL)
537 struct text_item *text_item;
539 text_item = text_item_create_format (
540 TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
542 ascii_submit (driver, &text_item->output_item);
543 text_item_unref (text_item);
547 #endif /* HAVE_CAIRO */
548 else if (is_text_item (output_item))
550 const struct text_item *text_item = to_text_item (output_item);
551 enum text_item_type type = text_item_get_type (text_item);
552 const char *text = text_item_get_text (text_item);
556 case TEXT_ITEM_TITLE:
558 a->title = xstrdup (text);
561 case TEXT_ITEM_SUBTITLE:
563 a->subtitle = xstrdup (text);
566 case TEXT_ITEM_COMMAND_OPEN:
567 case TEXT_ITEM_COMMAND_CLOSE:
570 case TEXT_ITEM_BLANK_LINE:
575 case TEXT_ITEM_EJECT_PAGE:
577 ascii_close_page (a);
581 ascii_output_text (a, text);
585 else if (is_message_item (output_item))
587 const struct message_item *message_item = to_message_item (output_item);
588 const struct msg *msg = message_item_get_msg (message_item);
589 char *s = msg_to_string (msg, a->command_name);
590 ascii_output_text (a, s);
595 const struct output_driver_factory txt_driver_factory =
596 { "txt", ascii_create };
597 const struct output_driver_factory list_driver_factory =
598 { "list", ascii_create };
600 static const struct output_driver_class ascii_driver_class =
608 static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
610 static void ascii_layout_cell (struct ascii_driver *,
611 const struct table_cell *,
612 int bb[TABLE_N_AXES][2],
613 int clip[TABLE_N_AXES][2],
614 int *width, int *height);
617 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
618 enum render_line_style styles[TABLE_N_AXES][2])
620 struct ascii_driver *a = a_;
627 /* Clip to the page. */
628 if (bb[H][0] >= a->width || bb[V][0] + a->y >= a->length)
631 x1 = MIN (bb[H][1], a->width);
632 y1 = MIN (bb[V][1] + a->y, a->length);
635 uc = a->box[make_box_index (styles[V][0], styles[V][1],
636 styles[H][0], styles[H][1])];
637 mblen = u8_uctomb (CHAR_CAST (uint8_t *, mbchar), uc, 6);
638 for (y = bb[V][0] + a->y; y < y1; y++)
640 char *p = ascii_reserve (a, y, x0, x1, mblen * (x1 - x0));
641 for (x = x0; x < x1; x++)
643 memcpy (p, mbchar, mblen);
650 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
651 int *min_width, int *max_width)
653 struct ascii_driver *a = a_;
654 int bb[TABLE_N_AXES][2];
655 int clip[TABLE_N_AXES][2];
662 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
663 ascii_layout_cell (a, cell, bb, clip, max_width, &h);
665 if (strchr (cell->contents, ' '))
668 ascii_layout_cell (a, cell, bb, clip, min_width, &h);
671 *min_width = *max_width;
675 ascii_measure_cell_height (void *a_, const struct table_cell *cell, int width)
677 struct ascii_driver *a = a_;
678 int bb[TABLE_N_AXES][2];
679 int clip[TABLE_N_AXES][2];
686 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
687 ascii_layout_cell (a, cell, bb, clip, &w, &h);
692 ascii_draw_cell (void *a_, const struct table_cell *cell,
693 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
695 struct ascii_driver *a = a_;
698 ascii_layout_cell (a, cell, bb, clip, &w, &h);
702 u8_mb_to_display (int *wp, const uint8_t *s, size_t n)
708 ofs = u8_mbtouc (&uc, s, n);
709 if (ofs < n && s[ofs] == '\b')
712 ofs += u8_mbtouc (&uc, s + ofs, n - ofs);
715 w = uc_width (uc, "UTF-8");
724 int mblen = u8_mbtouc (&uc, s + ofs, n - ofs);
725 if (uc_width (uc, "UTF-8") > 0)
743 find_ascii_pos (struct ascii_line *line, int target_x, struct ascii_pos *c)
745 const uint8_t *s = CHAR_CAST (const uint8_t *, ds_cstr (&line->s));
746 size_t length = ds_length (&line->s);
752 for (ofs = 0; ; ofs += mblen)
756 mblen = u8_mb_to_display (&w, s + ofs, length - ofs);
757 if (x + w > target_x)
762 c->ofs1 = ofs + mblen;
770 ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
772 struct ascii_line *line;
773 assert (y < a->allocated_lines);
776 if (x0 >= line->width)
778 /* The common case: adding new characters at the end of a line. */
779 ds_put_byte_multiple (&line->s, ' ', x0 - line->width);
781 return ds_put_uninit (&line->s, n);
787 /* An unusual case: overwriting characters in the middle of a line. We
788 don't keep any kind of mapping from bytes to display positions, so we
789 have to iterate over the whole line starting from the beginning. */
790 struct ascii_pos p0, p1;
793 /* Find the positions of the first and last character. We must find the
794 both characters' positions before changing the line, because that
795 would prevent finding the other character's position. */
796 find_ascii_pos (line, x0, &p0);
797 if (x1 < line->width)
798 find_ascii_pos (line, x1, &p1);
800 /* If a double-width character occupies both x0 - 1 and x0, then replace
801 its first character width by '?'. */
802 s = ds_data (&line->s);
809 if (x1 >= line->width)
811 ds_truncate (&line->s, p0.ofs0);
813 return ds_put_uninit (&line->s, n);
816 /* If a double-width character occupies both x1 - 1 and x1, then we need
817 to replace its second character width by '?'. */
826 return ds_splice_uninit (&line->s, p0.ofs0, p1.ofs1 - p0.ofs0, n);
829 return ds_splice_uninit (&line->s, p0.ofs0, p1.ofs0 - p0.ofs0, n);
834 text_draw (struct ascii_driver *a, unsigned int options,
835 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
836 int y, const uint8_t *string, int n, size_t width)
838 int x0 = MAX (0, clip[H][0]);
839 int y0 = MAX (0, clip[V][0] + a->y);
841 int y1 = MIN (a->length, clip[V][1] + a->y);
845 if (y < y0 || y >= y1)
848 switch (options & TAB_ALIGNMENT)
854 x = (bb[H][0] + bb[H][1] - width + 1) / 2;
857 x = bb[H][1] - width;
873 mblen = u8_mbtouc (&uc, string, n);
878 w = uc_width (uc, "UTF-8");
893 for (ofs = 0; ofs < n; )
899 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
901 w = uc_width (uc, "UTF-8");
904 if (width + w > x1 - x)
915 if (!(options & TAB_EMPH) || a->emphasis == EMPH_NONE)
916 memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
924 /* First figure out how many bytes need to be inserted. */
926 for (ofs = 0; ofs < n; ofs += mblen)
931 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
932 w = uc_width (uc, "UTF-8");
935 n_out += a->emphasis == EMPH_UNDERLINE ? 2 : 1 + mblen;
938 /* Then insert them. */
939 out = ascii_reserve (a, y, x, x + width, n_out);
940 for (ofs = 0; ofs < n; ofs += mblen)
945 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
946 w = uc_width (uc, "UTF-8");
950 if (a->emphasis == EMPH_UNDERLINE)
953 out = mempcpy (out, string + ofs, mblen);
956 out = mempcpy (out, string + ofs, mblen);
962 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
963 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
964 int *widthp, int *heightp)
966 const char *text = cell->contents;
967 size_t length = strlen (text);
978 breaks = xmalloc (length + 1);
979 u8_possible_linebreaks (CHAR_CAST (const uint8_t *, text), length,
981 breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
982 ? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
985 bb_width = bb[H][1] - bb[H][0];
986 for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
988 const uint8_t *line = CHAR_CAST (const uint8_t *, text + pos);
989 const char *b = breaks + pos;
990 size_t n = length - pos;
992 size_t last_break_ofs = 0;
993 int last_break_width = 0;
997 for (ofs = 0; ofs < n; )
1003 mblen = u8_mbtouc (&uc, line + ofs, n - ofs);
1004 if (b[ofs] == UC_BREAK_MANDATORY)
1006 else if (b[ofs] == UC_BREAK_POSSIBLE)
1008 last_break_ofs = ofs;
1009 last_break_width = width;
1012 w = uc_width (uc, "UTF-8");
1015 if (width + w > bb_width)
1017 if (isspace (line[ofs]))
1019 else if (last_break_ofs != 0)
1021 ofs = last_break_ofs;
1022 width = last_break_width;
1030 if (b[ofs] != UC_BREAK_MANDATORY)
1032 while (ofs > 0 && isspace (line[ofs - 1]))
1038 if (width > *widthp)
1042 text_draw (a, cell->options, bb, clip, y, line, ofs, width);
1046 if (ofs < n && isspace (line[ofs]))
1050 *heightp = y - bb[V][0];
1056 ascii_test_write (struct output_driver *driver,
1057 const char *s, int x, int y, unsigned int options)
1059 struct ascii_driver *a = ascii_driver_cast (driver);
1060 struct table_cell cell;
1061 int bb[TABLE_N_AXES][2];
1064 if (a->file == NULL && !ascii_open_page (a))
1068 memset (&cell, 0, sizeof cell);
1070 cell.options = options | TAB_LEFT;
1072 bb[TABLE_HORZ][0] = x;
1073 bb[TABLE_HORZ][1] = a->width;
1074 bb[TABLE_VERT][0] = y;
1075 bb[TABLE_VERT][1] = a->length;
1077 ascii_layout_cell (a, &cell, bb, bb, &width, &height);
1082 /* ascii_close_page () and support routines. */
1084 #if HAVE_DECL_SIGWINCH
1085 static struct ascii_driver *the_driver;
1088 winch_handler (int signum UNUSED)
1090 update_page_size (the_driver, false);
1095 ascii_open_page (struct ascii_driver *a)
1102 if (a->file == NULL)
1104 a->file = fn_open (a->file_name, a->append ? "a" : "w");
1105 if (a->file != NULL)
1107 if ( isatty (fileno (a->file)))
1109 #if HAVE_DECL_SIGWINCH
1110 struct sigaction action;
1111 sigemptyset (&action.sa_mask);
1112 action.sa_flags = 0;
1113 action.sa_handler = winch_handler;
1115 sigaction (SIGWINCH, &action, NULL);
1117 a->auto_width = true;
1118 a->auto_length = true;
1123 error (0, errno, _("ascii: opening output file `%s'"),
1132 reallocate_lines (a);
1134 for (i = 0; i < a->length; i++)
1136 struct ascii_line *line = &a->lines[i];
1137 ds_clear (&line->s);
1145 output_title_line (FILE *out, int width, const char *left, const char *right)
1147 struct string s = DS_EMPTY_INITIALIZER;
1148 ds_put_byte_multiple (&s, ' ', width);
1151 size_t length = MIN (strlen (left), width);
1152 memcpy (ds_end (&s) - width, left, length);
1156 size_t length = MIN (strlen (right), width);
1157 memcpy (ds_end (&s) - length, right, length);
1159 ds_put_byte (&s, '\n');
1160 fputs (ds_cstr (&s), out);
1165 ascii_close_page (struct ascii_driver *a)
1171 if (a->file == NULL)
1174 if (!a->top_margin && !a->bottom_margin && a->squeeze_blank_lines
1175 && !a->paginate && a->page_number > 1)
1176 putc ('\n', a->file);
1178 for (i = 0; i < a->top_margin; i++)
1179 putc ('\n', a->file);
1184 r1 = xasprintf (_("%s - Page %d"), get_start_date (), a->page_number);
1185 r2 = xasprintf ("%s - %s" , version, host_system);
1187 output_title_line (a->file, a->width, a->title, r1);
1188 output_title_line (a->file, a->width, a->subtitle, r2);
1189 putc ('\n', a->file);
1196 for (y = 0; y < a->allocated_lines; y++)
1198 struct ascii_line *line = &a->lines[y];
1200 if (a->squeeze_blank_lines && y > 0 && line->width == 0)
1206 putc ('\n', a->file);
1210 while (ds_chomp_byte (&line->s, ' '))
1212 fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
1213 putc ('\n', a->file);
1216 if (!a->squeeze_blank_lines)
1217 for (y = a->allocated_lines; y < a->length; y++)
1218 putc ('\n', a->file);
1220 for (i = 0; i < a->bottom_margin; i++)
1221 putc ('\n', a->file);
1223 putc ('\f', a->file);