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]);
205 static struct ascii_driver *
206 ascii_driver_cast (struct output_driver *driver)
208 assert (driver->class == &ascii_driver_class);
209 return UP_CAST (driver, struct ascii_driver, driver);
212 static struct driver_option *
213 opt (struct output_driver *d, struct string_map *options, const char *key,
214 const char *default_value)
216 return driver_option_get (d, options, key, default_value);
219 static struct output_driver *
220 ascii_create (const char *file_name, enum settings_output_devices device_type,
221 struct string_map *o)
223 enum { BOX_ASCII, BOX_UNICODE } box;
224 struct output_driver *d;
225 struct ascii_driver *a;
228 a = xzalloc (sizeof *a);
230 output_driver_init (&a->driver, &ascii_driver_class, file_name, device_type);
231 a->append = parse_boolean (opt (d, o, "append", "false"));
232 a->headers = parse_boolean (opt (d, o, "headers", "false"));
233 a->paginate = parse_boolean (opt (d, o, "paginate", "false"));
234 a->squeeze_blank_lines = parse_boolean (opt (d, o, "squeeze", "true"));
235 a->emphasis = parse_enum (opt (d, o, "emphasis", "none"),
237 "underline", EMPH_UNDERLINE,
241 a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", file_name));
243 a->top_margin = parse_int (opt (d, o, "top-margin", "0"), 0, INT_MAX);
244 a->bottom_margin = parse_int (opt (d, o, "bottom-margin", "0"), 0, INT_MAX);
246 a->width = parse_page_size (opt (d, o, "width", "79"));
247 paper_length = parse_page_size (opt (d, o, "length", "66"));
248 a->auto_width = a->width < 0;
249 a->auto_length = paper_length < 0;
250 a->length = paper_length - vertical_margins (a);
252 box = parse_enum (opt (d, o, "box", "ascii"),
254 "unicode", BOX_UNICODE,
256 a->box = box == BOX_ASCII ? ascii_box_chars : unicode_box_chars;
258 a->command_name = NULL;
259 a->title = xstrdup ("");
260 a->subtitle = xstrdup ("");
261 a->file_name = xstrdup (file_name);
266 a->allocated_lines = 0;
269 if (!update_page_size (a, true))
275 output_driver_destroy (d);
280 parse_page_size (struct driver_option *option)
282 int dim = atol (option->default_value);
284 if (option->value != NULL)
286 if (!strcmp (option->value, "auto"))
294 value = strtol (option->value, &tail, 0);
295 if (dim >= 1 && errno != ERANGE && *tail == '\0')
298 error (0, 0, _("%s: %s must be positive integer or `auto'"),
299 option->driver_name, option->name);
303 driver_option_destroy (option);
309 vertical_margins (const struct ascii_driver *a)
311 return a->top_margin + a->bottom_margin + (a->headers ? 3 : 0);
314 /* Re-calculates the page width and length based on settings,
315 margins, and, if "auto" is set, the size of the user's
316 terminal window or GUI output window. */
318 update_page_size (struct ascii_driver *a, bool issue_error)
320 enum { MIN_WIDTH = 6, MIN_LENGTH = 6 };
323 a->width = settings_get_viewwidth ();
325 a->length = settings_get_viewlength () - vertical_margins (a);
327 if (a->width < MIN_WIDTH || a->length < MIN_LENGTH)
331 _("ascii: page excluding margins and headers "
332 "must be at least %d characters wide by %d lines long, but "
333 "as configured is only %d characters by %d lines"),
334 MIN_WIDTH, MIN_LENGTH,
335 a->width, a->length);
336 if (a->width < MIN_WIDTH)
337 a->width = MIN_WIDTH;
338 if (a->length < MIN_LENGTH)
339 a->length = MIN_LENGTH;
347 ascii_destroy (struct output_driver *driver)
349 struct ascii_driver *a = ascii_driver_cast (driver);
353 ascii_close_page (a);
356 fn_close (a->file_name, a->file);
357 free (a->command_name);
361 free (a->chart_file_name);
362 for (i = 0; i < a->allocated_lines; i++)
363 ds_destroy (&a->lines[i].s);
369 ascii_flush (struct output_driver *driver)
371 struct ascii_driver *a = ascii_driver_cast (driver);
374 ascii_close_page (a);
376 if (fn_close (a->file_name, a->file) != 0)
377 error (0, errno, _("ascii: closing output file `%s'"),
384 ascii_init_caption_cell (const char *caption, struct table_cell *cell)
386 cell->contents = caption;
387 cell->options = TAB_LEFT;
388 cell->destructor = NULL;
392 ascii_output_table_item (struct ascii_driver *a,
393 const struct table_item *table_item)
395 const char *caption = table_item_get_caption (table_item);
396 struct render_params params;
397 struct render_page *page;
398 struct render_break x_break;
402 update_page_size (a, false);
406 /* XXX doesn't do well with very large captions */
407 struct table_cell cell;
408 ascii_init_caption_cell (caption, &cell);
409 caption_height = ascii_measure_cell_height (a, &cell, a->width);
414 params.draw_line = ascii_draw_line;
415 params.measure_cell_width = ascii_measure_cell_width;
416 params.measure_cell_height = ascii_measure_cell_height;
417 params.draw_cell = ascii_draw_cell,
419 params.size[H] = a->width;
420 params.size[V] = a->length - caption_height;
421 params.font_size[H] = 1;
422 params.font_size[V] = 1;
423 for (i = 0; i < RENDER_N_LINES; i++)
425 int width = i == RENDER_LINE_NONE ? 0 : 1;
426 params.line_widths[H][i] = width;
427 params.line_widths[V][i] = width;
430 if (a->file == NULL && !ascii_open_page (a))
433 page = render_page_create (¶ms, table_item_get_table (table_item));
434 for (render_break_init (&x_break, page, H);
435 render_break_has_next (&x_break); )
437 struct render_page *x_slice;
438 struct render_break y_break;
440 x_slice = render_break_next (&x_break, a->width);
441 for (render_break_init (&y_break, x_slice, V);
442 render_break_has_next (&y_break); )
444 struct render_page *y_slice;
450 space = a->length - a->y - caption_height;
451 if (render_break_next_size (&y_break) > space)
454 ascii_close_page (a);
455 if (!ascii_open_page (a))
460 y_slice = render_break_next (&y_break, space);
463 struct table_cell cell;
464 int bb[TABLE_N_AXES][2];
466 ascii_init_caption_cell (caption, &cell);
470 bb[V][1] = caption_height;
471 ascii_draw_cell (a, &cell, bb, bb);
472 a->y += caption_height;
475 render_page_draw (y_slice);
476 a->y += render_page_get_size (y_slice, V);
477 render_page_unref (y_slice);
479 render_break_destroy (&y_break);
481 render_break_destroy (&x_break);
485 ascii_output_text (struct ascii_driver *a, const char *text)
487 struct table_item *table_item;
489 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
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,
515 if (file_name != NULL)
517 struct text_item *text_item;
519 text_item = text_item_create_format (
520 TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
522 ascii_submit (driver, &text_item->output_item);
523 text_item_unref (text_item);
527 #endif /* HAVE_CAIRO */
528 else if (is_text_item (output_item))
530 const struct text_item *text_item = to_text_item (output_item);
531 enum text_item_type type = text_item_get_type (text_item);
532 const char *text = text_item_get_text (text_item);
536 case TEXT_ITEM_TITLE:
538 a->title = xstrdup (text);
541 case TEXT_ITEM_SUBTITLE:
543 a->subtitle = xstrdup (text);
546 case TEXT_ITEM_COMMAND_OPEN:
547 case TEXT_ITEM_COMMAND_CLOSE:
550 case TEXT_ITEM_BLANK_LINE:
555 case TEXT_ITEM_EJECT_PAGE:
557 ascii_close_page (a);
561 ascii_output_text (a, text);
565 else if (is_message_item (output_item))
567 const struct message_item *message_item = to_message_item (output_item);
568 const struct msg *msg = message_item_get_msg (message_item);
569 char *s = msg_to_string (msg, a->command_name);
570 ascii_output_text (a, s);
575 const struct output_driver_factory txt_driver_factory =
576 { "txt", ascii_create };
577 const struct output_driver_factory list_driver_factory =
578 { "list", ascii_create };
580 static const struct output_driver_class ascii_driver_class =
588 static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
590 static void ascii_layout_cell (struct ascii_driver *,
591 const struct table_cell *,
592 int bb[TABLE_N_AXES][2],
593 int clip[TABLE_N_AXES][2],
594 int *width, int *height);
597 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
598 enum render_line_style styles[TABLE_N_AXES][2])
600 struct ascii_driver *a = a_;
607 /* Clip to the page. */
608 if (bb[H][0] >= a->width || bb[V][0] + a->y >= a->length)
611 x1 = MIN (bb[H][1], a->width);
612 y1 = MIN (bb[V][1] + a->y, a->length);
615 uc = a->box[make_box_index (styles[V][0], styles[V][1],
616 styles[H][0], styles[H][1])];
617 mblen = u8_uctomb (CHAR_CAST (uint8_t *, mbchar), uc, 6);
618 for (y = bb[V][0] + a->y; y < y1; y++)
620 char *p = ascii_reserve (a, y, x0, x1, mblen * (x1 - x0));
621 for (x = x0; x < x1; x++)
623 memcpy (p, mbchar, mblen);
630 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
631 int *min_width, int *max_width)
633 struct ascii_driver *a = a_;
634 int bb[TABLE_N_AXES][2];
635 int clip[TABLE_N_AXES][2];
642 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
643 ascii_layout_cell (a, cell, bb, clip, max_width, &h);
645 if (strchr (cell->contents, ' '))
648 ascii_layout_cell (a, cell, bb, clip, min_width, &h);
651 *min_width = *max_width;
655 ascii_measure_cell_height (void *a_, const struct table_cell *cell, int width)
657 struct ascii_driver *a = a_;
658 int bb[TABLE_N_AXES][2];
659 int clip[TABLE_N_AXES][2];
666 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
667 ascii_layout_cell (a, cell, bb, clip, &w, &h);
672 ascii_draw_cell (void *a_, const struct table_cell *cell,
673 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
675 struct ascii_driver *a = a_;
678 ascii_layout_cell (a, cell, bb, clip, &w, &h);
682 u8_mb_to_display (int *wp, const uint8_t *s, size_t n)
688 ofs = u8_mbtouc (&uc, s, n);
689 if (ofs < n && s[ofs] == '\b')
692 ofs += u8_mbtouc (&uc, s + ofs, n - ofs);
695 w = uc_width (uc, "UTF-8");
704 int mblen = u8_mbtouc (&uc, s + ofs, n - ofs);
705 if (uc_width (uc, "UTF-8") > 0)
723 find_ascii_pos (struct ascii_line *line, int target_x, struct ascii_pos *c)
725 const uint8_t *s = CHAR_CAST (const uint8_t *, ds_cstr (&line->s));
726 size_t length = ds_length (&line->s);
732 for (ofs = 0; ; ofs += mblen)
736 mblen = u8_mb_to_display (&w, s + ofs, length - ofs);
737 if (x + w > target_x)
742 c->ofs1 = ofs + mblen;
750 ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
752 struct ascii_line *line;
753 assert (y < a->allocated_lines);
756 if (x0 >= line->width)
758 /* The common case: adding new characters at the end of a line. */
759 ds_put_byte_multiple (&line->s, ' ', x0 - line->width);
761 return ds_put_uninit (&line->s, n);
767 /* An unusual case: overwriting characters in the middle of a line. We
768 don't keep any kind of mapping from bytes to display positions, so we
769 have to iterate over the whole line starting from the beginning. */
770 struct ascii_pos p0, p1;
773 /* Find the positions of the first and last character. We must find the
774 both characters' positions before changing the line, because that
775 would prevent finding the other character's position. */
776 find_ascii_pos (line, x0, &p0);
777 if (x1 < line->width)
778 find_ascii_pos (line, x1, &p1);
780 /* If a double-width character occupies both x0 - 1 and x0, then replace
781 its first character width by '?'. */
782 s = ds_data (&line->s);
789 if (x1 >= line->width)
791 ds_truncate (&line->s, p0.ofs0);
793 return ds_put_uninit (&line->s, n);
796 /* If a double-width character occupies both x1 - 1 and x1, then we need
797 to replace its second character width by '?'. */
806 return ds_splice_uninit (&line->s, p0.ofs0, p1.ofs1 - p0.ofs0, n);
809 return ds_splice_uninit (&line->s, p0.ofs0, p1.ofs0 - p0.ofs0, n);
814 text_draw (struct ascii_driver *a, unsigned int options,
815 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
816 int y, const uint8_t *string, int n, size_t width)
818 int x0 = MAX (0, clip[H][0]);
819 int y0 = MAX (0, clip[V][0] + a->y);
821 int y1 = MIN (a->length, clip[V][1] + a->y);
825 if (y < y0 || y >= y1)
828 switch (options & TAB_ALIGNMENT)
834 x = (bb[H][0] + bb[H][1] - width + 1) / 2;
837 x = bb[H][1] - width;
853 mblen = u8_mbtouc (&uc, string, n);
858 w = uc_width (uc, "UTF-8");
873 for (ofs = 0; ofs < n; )
879 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
881 w = uc_width (uc, "UTF-8");
884 if (width + w > x1 - x)
895 if (!(options & TAB_EMPH) || a->emphasis == EMPH_NONE)
896 memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
904 /* First figure out how many bytes need to be inserted. */
906 for (ofs = 0; ofs < n; ofs += mblen)
911 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
912 w = uc_width (uc, "UTF-8");
915 n_out += a->emphasis == EMPH_UNDERLINE ? 2 : 1 + mblen;
918 /* Then insert them. */
919 out = ascii_reserve (a, y, x, x + width, n_out);
920 for (ofs = 0; ofs < n; ofs += mblen)
925 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
926 w = uc_width (uc, "UTF-8");
930 if (a->emphasis == EMPH_UNDERLINE)
933 out = mempcpy (out, string + ofs, mblen);
936 out = mempcpy (out, string + ofs, mblen);
942 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
943 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
944 int *widthp, int *heightp)
946 const char *text = cell->contents;
947 size_t length = strlen (text);
958 text = cell->contents;
959 breaks = xmalloc (length + 1);
960 u8_possible_linebreaks (CHAR_CAST (const uint8_t *, text), length,
962 breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
963 ? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
966 bb_width = bb[H][1] - bb[H][0];
967 for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
969 const uint8_t *line = CHAR_CAST (const uint8_t *, text + pos);
970 const char *b = breaks + pos;
971 size_t n = length - pos;
973 size_t last_break_ofs = 0;
974 int last_break_width = 0;
978 for (ofs = 0; ofs < n; )
984 mblen = u8_mbtouc (&uc, line + ofs, n - ofs);
985 if (b[ofs] == UC_BREAK_MANDATORY)
987 else if (b[ofs] == UC_BREAK_POSSIBLE)
989 last_break_ofs = ofs;
990 last_break_width = width;
993 w = uc_width (uc, "UTF-8");
996 if (width + w > bb_width)
998 if (isspace (line[ofs]))
1000 else if (last_break_ofs != 0)
1002 ofs = last_break_ofs;
1003 width = last_break_width;
1011 if (b[ofs] != UC_BREAK_MANDATORY)
1013 while (ofs > 0 && isspace (line[ofs - 1]))
1019 if (width > *widthp)
1023 text_draw (a, cell->options, bb, clip, y, line, ofs, width);
1027 if (ofs < n && isspace (line[ofs]))
1031 *heightp = y - bb[V][0];
1037 ascii_test_write (struct output_driver *driver,
1038 const char *s, int x, int y, unsigned int options)
1040 struct ascii_driver *a = ascii_driver_cast (driver);
1041 struct table_cell cell;
1042 int bb[TABLE_N_AXES][2];
1045 if (a->file == NULL && !ascii_open_page (a))
1049 memset (&cell, 0, sizeof cell);
1051 cell.options = options | TAB_LEFT;
1053 bb[TABLE_HORZ][0] = x;
1054 bb[TABLE_HORZ][1] = a->width;
1055 bb[TABLE_VERT][0] = y;
1056 bb[TABLE_VERT][1] = a->length;
1058 ascii_layout_cell (a, &cell, bb, bb, &width, &height);
1063 /* ascii_close_page () and support routines. */
1065 #if HAVE_DECL_SIGWINCH
1066 static struct ascii_driver *the_driver;
1069 winch_handler (int signum UNUSED)
1071 update_page_size (the_driver, false);
1076 ascii_open_page (struct ascii_driver *a)
1083 if (a->file == NULL)
1085 a->file = fn_open (a->file_name, a->append ? "a" : "w");
1086 if (a->file != NULL)
1088 if ( isatty (fileno (a->file)))
1090 #if HAVE_DECL_SIGWINCH
1091 struct sigaction action;
1092 sigemptyset (&action.sa_mask);
1093 action.sa_flags = 0;
1094 action.sa_handler = winch_handler;
1096 sigaction (SIGWINCH, &action, NULL);
1098 a->auto_width = true;
1099 a->auto_length = true;
1104 error (0, errno, _("ascii: opening output file `%s'"),
1113 if (a->length > a->allocated_lines)
1115 a->lines = xnrealloc (a->lines, a->length, sizeof *a->lines);
1116 for (i = a->allocated_lines; i < a->length; i++)
1118 struct ascii_line *line = &a->lines[i];
1119 ds_init_empty (&line->s);
1122 a->allocated_lines = a->length;
1125 for (i = 0; i < a->length; i++)
1127 struct ascii_line *line = &a->lines[i];
1128 ds_clear (&line->s);
1136 output_title_line (FILE *out, int width, const char *left, const char *right)
1138 struct string s = DS_EMPTY_INITIALIZER;
1139 ds_put_byte_multiple (&s, ' ', width);
1142 size_t length = MIN (strlen (left), width);
1143 memcpy (ds_end (&s) - width, left, length);
1147 size_t length = MIN (strlen (right), width);
1148 memcpy (ds_end (&s) - length, right, length);
1150 ds_put_byte (&s, '\n');
1151 fputs (ds_cstr (&s), out);
1156 ascii_close_page (struct ascii_driver *a)
1162 if (a->file == NULL)
1165 if (!a->top_margin && !a->bottom_margin && a->squeeze_blank_lines
1166 && !a->paginate && a->page_number > 1)
1167 putc ('\n', a->file);
1169 for (i = 0; i < a->top_margin; i++)
1170 putc ('\n', a->file);
1175 r1 = xasprintf (_("%s - Page %d"), get_start_date (), a->page_number);
1176 r2 = xasprintf ("%s - %s" , version, host_system);
1178 output_title_line (a->file, a->width, a->title, r1);
1179 output_title_line (a->file, a->width, a->subtitle, r2);
1180 putc ('\n', a->file);
1187 for (y = 0; y < a->allocated_lines; y++)
1189 struct ascii_line *line = &a->lines[y];
1191 if (a->squeeze_blank_lines && y > 0 && line->width == 0)
1197 putc ('\n', a->file);
1201 while (ds_chomp_byte (&line->s, ' '))
1203 fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
1204 putc ('\n', a->file);
1207 if (!a->squeeze_blank_lines)
1208 for (y = a->allocated_lines; y < a->length; y++)
1209 putc ('\n', a->file);
1211 for (i = 0; i < a->bottom_margin; i++)
1212 putc ('\n', a->file);
1214 putc ('\f', a->file);