1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011, 2012, 2013 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/u8-line.h"
39 #include "libpspp/version.h"
40 #include "output/ascii.h"
41 #include "output/cairo.h"
42 #include "output/chart-item-provider.h"
43 #include "output/driver-provider.h"
44 #include "output/message-item.h"
45 #include "output/options.h"
46 #include "output/render.h"
47 #include "output/tab.h"
48 #include "output/table-item.h"
49 #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 0x2560, 0x256c, 0x256c,
123 0x2566, 0x256c, 0x256c,
127 make_box_index (int left, int right, int top, int bottom)
129 return ((right * RENDER_N_LINES + bottom) * RENDER_N_LINES + left) * RENDER_N_LINES + top;
132 /* How to emphasize text. */
135 EMPH_BOLD, /* Overstrike for bold. */
136 EMPH_UNDERLINE, /* Overstrike for underlining. */
137 EMPH_NONE /* No emphasis. */
140 /* ASCII output driver. */
143 struct output_driver driver;
145 /* User parameters. */
146 bool append; /* Append if output file already exists? */
147 bool headers; /* Print headers at top of page? */
148 bool paginate; /* Insert formfeeds? */
149 bool squeeze_blank_lines; /* Squeeze multiple blank lines into one? */
150 enum emphasis_style emphasis; /* How to emphasize text. */
151 char *chart_file_name; /* Name of files used for charts. */
153 int width; /* Page width. */
154 int length; /* Page length minus margins and header. */
155 bool auto_width; /* Use viewwidth as page width? */
156 bool auto_length; /* Use viewlength as page width? */
158 int top_margin; /* Top margin in lines. */
159 int bottom_margin; /* Bottom margin in lines. */
161 const ucs4_t *box; /* Line & box drawing characters. */
163 /* Internal state. */
167 char *file_name; /* Output file name. */
168 FILE *file; /* Output file. */
169 bool error; /* Output error? */
170 int page_number; /* Current page number. */
171 struct u8_line *lines; /* Page content. */
172 int allocated_lines; /* Number of lines allocated. */
173 int chart_cnt; /* Number of charts so far. */
177 static const struct output_driver_class ascii_driver_class;
179 static void ascii_submit (struct output_driver *, const struct output_item *);
181 static int vertical_margins (const struct ascii_driver *);
183 static bool update_page_size (struct ascii_driver *, bool issue_error);
184 static int parse_page_size (struct driver_option *);
186 static void ascii_close_page (struct ascii_driver *);
187 static bool ascii_open_page (struct ascii_driver *);
189 static void ascii_draw_line (void *, int bb[TABLE_N_AXES][2],
190 enum render_line_style styles[TABLE_N_AXES][2]);
191 static void ascii_measure_cell_width (void *, const struct table_cell *,
193 static int ascii_measure_cell_height (void *, const struct table_cell *,
195 static void ascii_draw_cell (void *, const struct table_cell *,
196 int bb[TABLE_N_AXES][2],
197 int clip[TABLE_N_AXES][2]);
200 reallocate_lines (struct ascii_driver *a)
202 if (a->length > a->allocated_lines)
205 a->lines = xnrealloc (a->lines, a->length, sizeof *a->lines);
206 for (i = a->allocated_lines; i < a->length; i++)
207 u8_line_init (&a->lines[i]);
208 a->allocated_lines = a->length;
213 static struct ascii_driver *
214 ascii_driver_cast (struct output_driver *driver)
216 assert (driver->class == &ascii_driver_class);
217 return UP_CAST (driver, struct ascii_driver, driver);
220 static struct driver_option *
221 opt (struct output_driver *d, struct string_map *options, const char *key,
222 const char *default_value)
224 return driver_option_get (d, options, key, default_value);
227 static struct output_driver *
228 ascii_create (const char *file_name, enum settings_output_devices device_type,
229 struct string_map *o)
231 enum { BOX_ASCII, BOX_UNICODE } box;
232 struct output_driver *d;
233 struct ascii_driver *a;
236 a = xzalloc (sizeof *a);
238 output_driver_init (&a->driver, &ascii_driver_class, file_name, device_type);
239 a->append = parse_boolean (opt (d, o, "append", "false"));
240 a->headers = parse_boolean (opt (d, o, "headers", "false"));
241 a->paginate = parse_boolean (opt (d, o, "paginate", "false"));
242 a->squeeze_blank_lines = parse_boolean (opt (d, o, "squeeze", "true"));
243 a->emphasis = parse_enum (opt (d, o, "emphasis", "none"),
245 "underline", EMPH_UNDERLINE,
249 a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", file_name));
251 a->top_margin = parse_int (opt (d, o, "top-margin", "0"), 0, INT_MAX);
252 a->bottom_margin = parse_int (opt (d, o, "bottom-margin", "0"), 0, INT_MAX);
254 a->width = parse_page_size (opt (d, o, "width", "79"));
255 paper_length = parse_page_size (opt (d, o, "length", "66"));
256 a->auto_width = a->width < 0;
257 a->auto_length = paper_length < 0;
258 a->length = paper_length - vertical_margins (a);
260 box = parse_enum (opt (d, o, "box", "ascii"),
262 "unicode", BOX_UNICODE,
264 a->box = box == BOX_ASCII ? ascii_box_chars : unicode_box_chars;
266 a->command_name = NULL;
267 a->title = xstrdup ("");
268 a->subtitle = xstrdup ("");
269 a->file_name = xstrdup (file_name);
274 a->allocated_lines = 0;
277 if (!update_page_size (a, true))
283 output_driver_destroy (d);
288 parse_page_size (struct driver_option *option)
290 int dim = atol (option->default_value);
292 if (option->value != NULL)
294 if (!strcmp (option->value, "auto"))
302 value = strtol (option->value, &tail, 0);
303 if (dim >= 1 && errno != ERANGE && *tail == '\0')
306 msg (MW, _("%s: %s must be positive integer or `auto'"),
307 option->driver_name, option->name);
311 driver_option_destroy (option);
317 vertical_margins (const struct ascii_driver *a)
319 return a->top_margin + a->bottom_margin + (a->headers ? 3 : 0);
322 /* Re-calculates the page width and length based on settings,
323 margins, and, if "auto" is set, the size of the user's
324 terminal window or GUI output window. */
326 update_page_size (struct ascii_driver *a, bool issue_error)
328 enum { MIN_WIDTH = 6, MIN_LENGTH = 6 };
331 a->width = settings_get_viewwidth ();
333 a->length = settings_get_viewlength () - vertical_margins (a);
335 if (a->width < MIN_WIDTH || a->length < MIN_LENGTH)
339 _("ascii: page excluding margins and headers "
340 "must be at least %d characters wide by %d lines long, but "
341 "as configured is only %d characters by %d lines"),
342 MIN_WIDTH, MIN_LENGTH,
343 a->width, a->length);
344 if (a->width < MIN_WIDTH)
345 a->width = MIN_WIDTH;
346 if (a->length < MIN_LENGTH)
347 a->length = MIN_LENGTH;
351 reallocate_lines (a);
357 ascii_destroy (struct output_driver *driver)
359 struct ascii_driver *a = ascii_driver_cast (driver);
363 ascii_close_page (a);
366 fn_close (a->file_name, a->file);
367 free (a->command_name);
371 free (a->chart_file_name);
372 for (i = 0; i < a->allocated_lines; i++)
373 u8_line_destroy (&a->lines[i]);
379 ascii_flush (struct output_driver *driver)
381 struct ascii_driver *a = ascii_driver_cast (driver);
384 ascii_close_page (a);
386 if (fn_close (a->file_name, a->file) != 0)
387 msg_error (errno, _("ascii: closing output file `%s'"), a->file_name);
393 ascii_init_caption_cell (const char *caption, struct table_cell *cell)
395 cell->contents = caption;
396 cell->options = TAB_LEFT;
397 cell->destructor = NULL;
401 ascii_output_table_item (struct ascii_driver *a,
402 const struct table_item *table_item)
404 const char *caption = table_item_get_caption (table_item);
405 struct render_params params;
406 struct render_page *page;
407 struct render_break x_break;
411 update_page_size (a, false);
415 /* XXX doesn't do well with very large captions */
416 struct table_cell cell;
417 ascii_init_caption_cell (caption, &cell);
418 caption_height = ascii_measure_cell_height (a, &cell, a->width);
423 params.draw_line = ascii_draw_line;
424 params.measure_cell_width = ascii_measure_cell_width;
425 params.measure_cell_height = ascii_measure_cell_height;
426 params.draw_cell = ascii_draw_cell,
428 params.size[H] = a->width;
429 params.size[V] = a->length - caption_height;
430 params.font_size[H] = 1;
431 params.font_size[V] = 1;
432 for (i = 0; i < RENDER_N_LINES; i++)
434 int width = i == RENDER_LINE_NONE ? 0 : 1;
435 params.line_widths[H][i] = width;
436 params.line_widths[V][i] = width;
439 if (a->file == NULL && !ascii_open_page (a))
442 page = render_page_create (¶ms, table_item_get_table (table_item));
443 for (render_break_init (&x_break, page, H);
444 render_break_has_next (&x_break); )
446 struct render_page *x_slice;
447 struct render_break y_break;
449 x_slice = render_break_next (&x_break, a->width);
450 for (render_break_init (&y_break, x_slice, V);
451 render_break_has_next (&y_break); )
453 struct render_page *y_slice;
459 space = a->length - a->y - caption_height;
460 if (render_break_next_size (&y_break) > space)
463 ascii_close_page (a);
464 if (!ascii_open_page (a))
469 y_slice = render_break_next (&y_break, space);
472 struct table_cell cell;
473 int bb[TABLE_N_AXES][2];
475 ascii_init_caption_cell (caption, &cell);
479 bb[V][1] = caption_height;
480 ascii_draw_cell (a, &cell, bb, bb);
481 a->y += caption_height;
484 render_page_draw (y_slice);
485 a->y += render_page_get_size (y_slice, V);
486 render_page_unref (y_slice);
488 render_break_destroy (&y_break);
490 render_break_destroy (&x_break);
494 ascii_output_text (struct ascii_driver *a, const char *text)
496 struct table_item *table_item;
498 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
499 ascii_output_table_item (a, table_item);
500 table_item_unref (table_item);
504 ascii_submit (struct output_driver *driver,
505 const struct output_item *output_item)
507 struct ascii_driver *a = ascii_driver_cast (driver);
509 output_driver_track_current_command (output_item, &a->command_name);
514 if (is_table_item (output_item))
515 ascii_output_table_item (a, to_table_item (output_item));
517 else if (is_chart_item (output_item) && a->chart_file_name != NULL)
519 struct chart_item *chart_item = to_chart_item (output_item);
522 file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
524 if (file_name != NULL)
526 struct text_item *text_item;
528 text_item = text_item_create_format (
529 TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
531 ascii_submit (driver, &text_item->output_item);
532 text_item_unref (text_item);
536 #endif /* HAVE_CAIRO */
537 else if (is_text_item (output_item))
539 const struct text_item *text_item = to_text_item (output_item);
540 enum text_item_type type = text_item_get_type (text_item);
541 const char *text = text_item_get_text (text_item);
545 case TEXT_ITEM_TITLE:
547 a->title = xstrdup (text);
550 case TEXT_ITEM_SUBTITLE:
552 a->subtitle = xstrdup (text);
555 case TEXT_ITEM_COMMAND_OPEN:
556 case TEXT_ITEM_COMMAND_CLOSE:
559 case TEXT_ITEM_BLANK_LINE:
564 case TEXT_ITEM_EJECT_PAGE:
566 ascii_close_page (a);
570 ascii_output_text (a, text);
574 else if (is_message_item (output_item))
576 const struct message_item *message_item = to_message_item (output_item);
577 const struct msg *msg = message_item_get_msg (message_item);
578 char *s = msg_to_string (msg, a->command_name);
579 ascii_output_text (a, s);
584 const struct output_driver_factory txt_driver_factory =
585 { "txt", "-", ascii_create };
586 const struct output_driver_factory list_driver_factory =
587 { "list", "-", ascii_create };
589 static const struct output_driver_class ascii_driver_class =
597 static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
599 static void ascii_layout_cell (struct ascii_driver *,
600 const struct table_cell *,
601 int bb[TABLE_N_AXES][2],
602 int clip[TABLE_N_AXES][2],
603 int *width, int *height);
606 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
607 enum render_line_style styles[TABLE_N_AXES][2])
609 struct ascii_driver *a = a_;
616 /* Clip to the page. */
617 if (bb[H][0] >= a->width || bb[V][0] + a->y >= a->length)
620 x1 = MIN (bb[H][1], a->width);
621 y1 = MIN (bb[V][1] + a->y, a->length);
624 uc = a->box[make_box_index (styles[V][0], styles[V][1],
625 styles[H][0], styles[H][1])];
626 mblen = u8_uctomb (CHAR_CAST (uint8_t *, mbchar), uc, 6);
627 for (y = bb[V][0] + a->y; y < y1; y++)
629 char *p = ascii_reserve (a, y, x0, x1, mblen * (x1 - x0));
630 for (x = x0; x < x1; x++)
632 memcpy (p, mbchar, mblen);
639 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
640 int *min_width, int *max_width)
642 struct ascii_driver *a = a_;
643 int bb[TABLE_N_AXES][2];
644 int clip[TABLE_N_AXES][2];
651 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
652 ascii_layout_cell (a, cell, bb, clip, max_width, &h);
654 if (strchr (cell->contents, ' '))
657 ascii_layout_cell (a, cell, bb, clip, min_width, &h);
660 *min_width = *max_width;
664 ascii_measure_cell_height (void *a_, const struct table_cell *cell, int width)
666 struct ascii_driver *a = a_;
667 int bb[TABLE_N_AXES][2];
668 int clip[TABLE_N_AXES][2];
675 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
676 ascii_layout_cell (a, cell, bb, clip, &w, &h);
681 ascii_draw_cell (void *a_, const struct table_cell *cell,
682 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
684 struct ascii_driver *a = a_;
687 ascii_layout_cell (a, cell, bb, clip, &w, &h);
691 ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
693 assert (y < a->allocated_lines);
694 return u8_line_reserve (&a->lines[y], x0, x1, n);
698 text_draw (struct ascii_driver *a, unsigned int options,
699 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
700 int y, const uint8_t *string, int n, size_t width)
702 int x0 = MAX (0, clip[H][0]);
703 int y0 = MAX (0, clip[V][0] + a->y);
705 int y1 = MIN (a->length, clip[V][1] + a->y);
709 if (y < y0 || y >= y1)
712 switch (options & TAB_ALIGNMENT)
718 x = (bb[H][0] + bb[H][1] - width + 1) / 2;
721 x = bb[H][1] - width;
737 mblen = u8_mbtouc (&uc, string, n);
742 w = uc_width (uc, "UTF-8");
757 for (ofs = 0; ofs < n; )
763 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
765 w = uc_width (uc, "UTF-8");
768 if (width + w > x1 - x)
779 if (!(options & TAB_EMPH) || a->emphasis == EMPH_NONE)
780 memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
788 /* First figure out how many bytes need to be inserted. */
790 for (ofs = 0; ofs < n; ofs += mblen)
795 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
796 w = uc_width (uc, "UTF-8");
799 n_out += a->emphasis == EMPH_UNDERLINE ? 2 : 1 + mblen;
802 /* Then insert them. */
803 out = ascii_reserve (a, y, x, x + width, n_out);
804 for (ofs = 0; ofs < n; ofs += mblen)
809 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
810 w = uc_width (uc, "UTF-8");
814 if (a->emphasis == EMPH_UNDERLINE)
817 out = mempcpy (out, string + ofs, mblen);
820 out = mempcpy (out, string + ofs, mblen);
826 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
827 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
828 int *widthp, int *heightp)
830 const char *text = cell->contents;
831 size_t length = strlen (text);
842 breaks = xmalloc (length + 1);
843 u8_possible_linebreaks (CHAR_CAST (const uint8_t *, text), length,
845 breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
846 ? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
849 bb_width = bb[H][1] - bb[H][0];
850 for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
852 const uint8_t *line = CHAR_CAST (const uint8_t *, text + pos);
853 const char *b = breaks + pos;
854 size_t n = length - pos;
856 size_t last_break_ofs = 0;
857 int last_break_width = 0;
862 for (ofs = 0; ofs < n; )
868 mblen = u8_mbtouc (&uc, line + ofs, n - ofs);
869 if (b[ofs] == UC_BREAK_MANDATORY)
871 else if (b[ofs] == UC_BREAK_POSSIBLE)
873 last_break_ofs = ofs;
874 last_break_width = width;
877 w = uc_width (uc, "UTF-8");
880 if (width + w > bb_width)
882 if (isspace (line[ofs]))
884 else if (last_break_ofs != 0)
886 ofs = last_break_ofs;
887 width = last_break_width;
896 /* Trim any trailing spaces off the end of the text to be drawn. */
897 for (graph_ofs = ofs; graph_ofs > 0; graph_ofs--)
898 if (!isspace (line[graph_ofs - 1]))
900 width -= ofs - graph_ofs;
903 text_draw (a, cell->options, bb, clip, y, line, graph_ofs, width);
905 /* If a new-line ended the line, just skip the new-line. Otherwise, skip
906 past any spaces past the end of the line (but not past a new-line). */
907 if (b[ofs] == UC_BREAK_MANDATORY)
910 while (ofs < n && isspace (line[ofs]) && b[ofs] != UC_BREAK_MANDATORY)
917 *heightp = y - bb[V][0];
923 ascii_test_write (struct output_driver *driver,
924 const char *s, int x, int y, unsigned int options)
926 struct ascii_driver *a = ascii_driver_cast (driver);
927 struct table_cell cell;
928 int bb[TABLE_N_AXES][2];
931 if (a->file == NULL && !ascii_open_page (a))
935 memset (&cell, 0, sizeof cell);
937 cell.options = options | TAB_LEFT;
939 bb[TABLE_HORZ][0] = x;
940 bb[TABLE_HORZ][1] = a->width;
941 bb[TABLE_VERT][0] = y;
942 bb[TABLE_VERT][1] = a->length;
944 ascii_layout_cell (a, &cell, bb, bb, &width, &height);
950 ascii_test_set_length (struct output_driver *driver, int y, int length)
952 struct ascii_driver *a = ascii_driver_cast (driver);
954 if (a->file == NULL && !ascii_open_page (a))
957 if (y < 0 || y >= a->length)
959 u8_line_set_length (&a->lines[y], length);
962 /* ascii_close_page () and support routines. */
964 #if HAVE_DECL_SIGWINCH
965 static struct ascii_driver *the_driver;
968 winch_handler (int signum UNUSED)
970 update_page_size (the_driver, false);
975 ascii_open_page (struct ascii_driver *a)
984 a->file = fn_open (a->file_name, a->append ? "a" : "w");
987 if ( isatty (fileno (a->file)))
989 #if HAVE_DECL_SIGWINCH
990 struct sigaction action;
991 sigemptyset (&action.sa_mask);
993 action.sa_handler = winch_handler;
995 sigaction (SIGWINCH, &action, NULL);
997 a->auto_width = true;
998 a->auto_length = true;
1003 msg_error (errno, _("ascii: opening output file `%s'"),
1012 reallocate_lines (a);
1014 for (i = 0; i < a->length; i++)
1015 u8_line_clear (&a->lines[i]);
1021 output_title_line (FILE *out, int width, const char *left, const char *right)
1023 struct string s = DS_EMPTY_INITIALIZER;
1024 ds_put_byte_multiple (&s, ' ', width);
1027 size_t length = MIN (strlen (left), width);
1028 memcpy (ds_end (&s) - width, left, length);
1032 size_t length = MIN (strlen (right), width);
1033 memcpy (ds_end (&s) - length, right, length);
1035 ds_put_byte (&s, '\n');
1036 fputs (ds_cstr (&s), out);
1041 ascii_close_page (struct ascii_driver *a)
1047 if (a->file == NULL)
1050 if (!a->top_margin && !a->bottom_margin && a->squeeze_blank_lines
1051 && !a->paginate && a->page_number > 1)
1052 putc ('\n', a->file);
1054 for (i = 0; i < a->top_margin; i++)
1055 putc ('\n', a->file);
1060 r1 = xasprintf (_("%s - Page %d"), get_start_date (), a->page_number);
1061 r2 = xasprintf ("%s - %s" , version, host_system);
1063 output_title_line (a->file, a->width, a->title, r1);
1064 output_title_line (a->file, a->width, a->subtitle, r2);
1065 putc ('\n', a->file);
1072 for (y = 0; y < a->allocated_lines; y++)
1074 struct u8_line *line = &a->lines[y];
1076 if (a->squeeze_blank_lines && y > 0 && line->width == 0)
1082 putc ('\n', a->file);
1086 while (ds_chomp_byte (&line->s, ' '))
1088 fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
1089 putc ('\n', a->file);
1092 if (!a->squeeze_blank_lines)
1093 for (y = a->allocated_lines; y < a->length; y++)
1094 putc ('\n', a->file);
1096 for (i = 0; i < a->bottom_margin; i++)
1097 putc ('\n', a->file);
1099 putc ('\f', a->file);