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"
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 return ((right * RENDER_N_LINES + bottom) * RENDER_N_LINES + left) * RENDER_N_LINES + top;
133 /* How to emphasize text. */
136 EMPH_BOLD, /* Overstrike for bold. */
137 EMPH_UNDERLINE, /* Overstrike for underlining. */
138 EMPH_NONE /* No emphasis. */
141 /* ASCII output driver. */
144 struct output_driver driver;
146 /* User parameters. */
147 bool append; /* Append if output file already exists? */
148 bool headers; /* Print headers at top of page? */
149 bool paginate; /* Insert formfeeds? */
150 bool squeeze_blank_lines; /* Squeeze multiple blank lines into one? */
151 enum emphasis_style emphasis; /* How to emphasize text. */
152 char *chart_file_name; /* Name of files used for charts. */
154 int width; /* Page width. */
155 int length; /* Page length minus margins and header. */
156 bool auto_width; /* Use viewwidth as page width? */
157 bool auto_length; /* Use viewlength as page width? */
159 int top_margin; /* Top margin in lines. */
160 int bottom_margin; /* Bottom margin in lines. */
162 const ucs4_t *box; /* Line & box drawing characters. */
164 /* Internal state. */
168 char *file_name; /* Output file name. */
169 FILE *file; /* Output file. */
170 bool error; /* Output error? */
171 int page_number; /* Current page number. */
172 struct u8_line *lines; /* Page content. */
173 int allocated_lines; /* Number of lines allocated. */
174 int chart_cnt; /* Number of charts so far. */
178 static const struct output_driver_class ascii_driver_class;
180 static void ascii_submit (struct output_driver *, const struct output_item *);
182 static int vertical_margins (const struct ascii_driver *);
184 static bool update_page_size (struct ascii_driver *, bool issue_error);
185 static int parse_page_size (struct driver_option *);
187 static void ascii_close_page (struct ascii_driver *);
188 static bool ascii_open_page (struct ascii_driver *);
190 static void ascii_draw_line (void *, int bb[TABLE_N_AXES][2],
191 enum render_line_style styles[TABLE_N_AXES][2]);
192 static void ascii_measure_cell_width (void *, const struct table_cell *,
194 static int ascii_measure_cell_height (void *, const struct table_cell *,
196 static void ascii_draw_cell (void *, const struct table_cell *,
197 int bb[TABLE_N_AXES][2],
198 int clip[TABLE_N_AXES][2]);
201 reallocate_lines (struct ascii_driver *a)
203 if (a->length > a->allocated_lines)
206 a->lines = xnrealloc (a->lines, a->length, sizeof *a->lines);
207 for (i = a->allocated_lines; i < a->length; i++)
208 u8_line_init (&a->lines[i]);
209 a->allocated_lines = a->length;
214 static struct ascii_driver *
215 ascii_driver_cast (struct output_driver *driver)
217 assert (driver->class == &ascii_driver_class);
218 return UP_CAST (driver, struct ascii_driver, driver);
221 static struct driver_option *
222 opt (struct output_driver *d, struct string_map *options, const char *key,
223 const char *default_value)
225 return driver_option_get (d, options, key, default_value);
228 static struct output_driver *
229 ascii_create (const char *file_name, enum settings_output_devices device_type,
230 struct string_map *o)
232 enum { BOX_ASCII, BOX_UNICODE } box;
233 struct output_driver *d;
234 struct ascii_driver *a;
237 a = xzalloc (sizeof *a);
239 output_driver_init (&a->driver, &ascii_driver_class, file_name, device_type);
240 a->append = parse_boolean (opt (d, o, "append", "false"));
241 a->headers = parse_boolean (opt (d, o, "headers", "false"));
242 a->paginate = parse_boolean (opt (d, o, "paginate", "false"));
243 a->squeeze_blank_lines = parse_boolean (opt (d, o, "squeeze", "true"));
244 a->emphasis = parse_enum (opt (d, o, "emphasis", "none"),
246 "underline", EMPH_UNDERLINE,
250 a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", file_name));
252 a->top_margin = parse_int (opt (d, o, "top-margin", "0"), 0, INT_MAX);
253 a->bottom_margin = parse_int (opt (d, o, "bottom-margin", "0"), 0, INT_MAX);
255 a->width = parse_page_size (opt (d, o, "width", "79"));
256 paper_length = parse_page_size (opt (d, o, "length", "66"));
257 a->auto_width = a->width < 0;
258 a->auto_length = paper_length < 0;
259 a->length = paper_length - vertical_margins (a);
261 box = parse_enum (opt (d, o, "box", "ascii"),
263 "unicode", BOX_UNICODE,
265 a->box = box == BOX_ASCII ? ascii_box_chars : unicode_box_chars;
267 a->command_name = NULL;
268 a->title = xstrdup ("");
269 a->subtitle = xstrdup ("");
270 a->file_name = xstrdup (file_name);
275 a->allocated_lines = 0;
278 if (!update_page_size (a, true))
284 output_driver_destroy (d);
289 parse_page_size (struct driver_option *option)
291 int dim = atol (option->default_value);
293 if (option->value != NULL)
295 if (!strcmp (option->value, "auto"))
303 value = strtol (option->value, &tail, 0);
304 if (dim >= 1 && errno != ERANGE && *tail == '\0')
307 error (0, 0, _("%s: %s must be positive integer or `auto'"),
308 option->driver_name, option->name);
312 driver_option_destroy (option);
318 vertical_margins (const struct ascii_driver *a)
320 return a->top_margin + a->bottom_margin + (a->headers ? 3 : 0);
323 /* Re-calculates the page width and length based on settings,
324 margins, and, if "auto" is set, the size of the user's
325 terminal window or GUI output window. */
327 update_page_size (struct ascii_driver *a, bool issue_error)
329 enum { MIN_WIDTH = 6, MIN_LENGTH = 6 };
332 a->width = settings_get_viewwidth ();
334 a->length = settings_get_viewlength () - vertical_margins (a);
336 if (a->width < MIN_WIDTH || a->length < MIN_LENGTH)
340 _("ascii: page excluding margins and headers "
341 "must be at least %d characters wide by %d lines long, but "
342 "as configured is only %d characters by %d lines"),
343 MIN_WIDTH, MIN_LENGTH,
344 a->width, a->length);
345 if (a->width < MIN_WIDTH)
346 a->width = MIN_WIDTH;
347 if (a->length < MIN_LENGTH)
348 a->length = MIN_LENGTH;
352 reallocate_lines (a);
358 ascii_destroy (struct output_driver *driver)
360 struct ascii_driver *a = ascii_driver_cast (driver);
364 ascii_close_page (a);
367 fn_close (a->file_name, a->file);
368 free (a->command_name);
372 free (a->chart_file_name);
373 for (i = 0; i < a->allocated_lines; i++)
374 u8_line_destroy (&a->lines[i]);
380 ascii_flush (struct output_driver *driver)
382 struct ascii_driver *a = ascii_driver_cast (driver);
385 ascii_close_page (a);
387 if (fn_close (a->file_name, a->file) != 0)
388 error (0, errno, _("ascii: closing output file `%s'"),
395 ascii_init_caption_cell (const char *caption, struct table_cell *cell)
397 cell->contents = caption;
398 cell->options = TAB_LEFT;
399 cell->destructor = NULL;
403 ascii_output_table_item (struct ascii_driver *a,
404 const struct table_item *table_item)
406 const char *caption = table_item_get_caption (table_item);
407 struct render_params params;
408 struct render_page *page;
409 struct render_break x_break;
413 update_page_size (a, false);
417 /* XXX doesn't do well with very large captions */
418 struct table_cell cell;
419 ascii_init_caption_cell (caption, &cell);
420 caption_height = ascii_measure_cell_height (a, &cell, a->width);
425 params.draw_line = ascii_draw_line;
426 params.measure_cell_width = ascii_measure_cell_width;
427 params.measure_cell_height = ascii_measure_cell_height;
428 params.draw_cell = ascii_draw_cell,
430 params.size[H] = a->width;
431 params.size[V] = a->length - caption_height;
432 params.font_size[H] = 1;
433 params.font_size[V] = 1;
434 for (i = 0; i < RENDER_N_LINES; i++)
436 int width = i == RENDER_LINE_NONE ? 0 : 1;
437 params.line_widths[H][i] = width;
438 params.line_widths[V][i] = width;
441 if (a->file == NULL && !ascii_open_page (a))
444 page = render_page_create (¶ms, table_item_get_table (table_item));
445 for (render_break_init (&x_break, page, H);
446 render_break_has_next (&x_break); )
448 struct render_page *x_slice;
449 struct render_break y_break;
451 x_slice = render_break_next (&x_break, a->width);
452 for (render_break_init (&y_break, x_slice, V);
453 render_break_has_next (&y_break); )
455 struct render_page *y_slice;
461 space = a->length - a->y - caption_height;
462 if (render_break_next_size (&y_break) > space)
465 ascii_close_page (a);
466 if (!ascii_open_page (a))
471 y_slice = render_break_next (&y_break, space);
474 struct table_cell cell;
475 int bb[TABLE_N_AXES][2];
477 ascii_init_caption_cell (caption, &cell);
481 bb[V][1] = caption_height;
482 ascii_draw_cell (a, &cell, bb, bb);
483 a->y += caption_height;
486 render_page_draw (y_slice);
487 a->y += render_page_get_size (y_slice, V);
488 render_page_unref (y_slice);
490 render_break_destroy (&y_break);
492 render_break_destroy (&x_break);
496 ascii_output_text (struct ascii_driver *a, const char *text)
498 struct table_item *table_item;
500 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
501 ascii_output_table_item (a, table_item);
502 table_item_unref (table_item);
506 ascii_submit (struct output_driver *driver,
507 const struct output_item *output_item)
509 struct ascii_driver *a = ascii_driver_cast (driver);
511 output_driver_track_current_command (output_item, &a->command_name);
516 if (is_table_item (output_item))
517 ascii_output_table_item (a, to_table_item (output_item));
519 else if (is_chart_item (output_item) && a->chart_file_name != NULL)
521 struct chart_item *chart_item = to_chart_item (output_item);
524 file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
526 if (file_name != NULL)
528 struct text_item *text_item;
530 text_item = text_item_create_format (
531 TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
533 ascii_submit (driver, &text_item->output_item);
534 text_item_unref (text_item);
538 #endif /* HAVE_CAIRO */
539 else if (is_text_item (output_item))
541 const struct text_item *text_item = to_text_item (output_item);
542 enum text_item_type type = text_item_get_type (text_item);
543 const char *text = text_item_get_text (text_item);
547 case TEXT_ITEM_TITLE:
549 a->title = xstrdup (text);
552 case TEXT_ITEM_SUBTITLE:
554 a->subtitle = xstrdup (text);
557 case TEXT_ITEM_COMMAND_OPEN:
558 case TEXT_ITEM_COMMAND_CLOSE:
561 case TEXT_ITEM_BLANK_LINE:
566 case TEXT_ITEM_EJECT_PAGE:
568 ascii_close_page (a);
572 ascii_output_text (a, text);
576 else if (is_message_item (output_item))
578 const struct message_item *message_item = to_message_item (output_item);
579 const struct msg *msg = message_item_get_msg (message_item);
580 char *s = msg_to_string (msg, a->command_name);
581 ascii_output_text (a, s);
586 const struct output_driver_factory txt_driver_factory =
587 { "txt", "-", ascii_create };
588 const struct output_driver_factory list_driver_factory =
589 { "list", "-", ascii_create };
591 static const struct output_driver_class ascii_driver_class =
599 static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
601 static void ascii_layout_cell (struct ascii_driver *,
602 const struct table_cell *,
603 int bb[TABLE_N_AXES][2],
604 int clip[TABLE_N_AXES][2],
605 int *width, int *height);
608 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
609 enum render_line_style styles[TABLE_N_AXES][2])
611 struct ascii_driver *a = a_;
618 /* Clip to the page. */
619 if (bb[H][0] >= a->width || bb[V][0] + a->y >= a->length)
622 x1 = MIN (bb[H][1], a->width);
623 y1 = MIN (bb[V][1] + a->y, a->length);
626 uc = a->box[make_box_index (styles[V][0], styles[V][1],
627 styles[H][0], styles[H][1])];
628 mblen = u8_uctomb (CHAR_CAST (uint8_t *, mbchar), uc, 6);
629 for (y = bb[V][0] + a->y; y < y1; y++)
631 char *p = ascii_reserve (a, y, x0, x1, mblen * (x1 - x0));
632 for (x = x0; x < x1; x++)
634 memcpy (p, mbchar, mblen);
641 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
642 int *min_width, int *max_width)
644 struct ascii_driver *a = a_;
645 int bb[TABLE_N_AXES][2];
646 int clip[TABLE_N_AXES][2];
653 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
654 ascii_layout_cell (a, cell, bb, clip, max_width, &h);
656 if (strchr (cell->contents, ' '))
659 ascii_layout_cell (a, cell, bb, clip, min_width, &h);
662 *min_width = *max_width;
666 ascii_measure_cell_height (void *a_, const struct table_cell *cell, int width)
668 struct ascii_driver *a = a_;
669 int bb[TABLE_N_AXES][2];
670 int clip[TABLE_N_AXES][2];
677 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
678 ascii_layout_cell (a, cell, bb, clip, &w, &h);
683 ascii_draw_cell (void *a_, const struct table_cell *cell,
684 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
686 struct ascii_driver *a = a_;
689 ascii_layout_cell (a, cell, bb, clip, &w, &h);
693 ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
695 assert (y < a->allocated_lines);
696 return u8_line_reserve (&a->lines[y], x0, x1, n);
700 text_draw (struct ascii_driver *a, unsigned int options,
701 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
702 int y, const uint8_t *string, int n, size_t width)
704 int x0 = MAX (0, clip[H][0]);
705 int y0 = MAX (0, clip[V][0] + a->y);
707 int y1 = MIN (a->length, clip[V][1] + a->y);
711 if (y < y0 || y >= y1)
714 switch (options & TAB_ALIGNMENT)
720 x = (bb[H][0] + bb[H][1] - width + 1) / 2;
723 x = bb[H][1] - width;
739 mblen = u8_mbtouc (&uc, string, n);
744 w = uc_width (uc, "UTF-8");
759 for (ofs = 0; ofs < n; )
765 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
767 w = uc_width (uc, "UTF-8");
770 if (width + w > x1 - x)
781 if (!(options & TAB_EMPH) || a->emphasis == EMPH_NONE)
782 memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
790 /* First figure out how many bytes need to be inserted. */
792 for (ofs = 0; ofs < n; ofs += mblen)
797 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
798 w = uc_width (uc, "UTF-8");
801 n_out += a->emphasis == EMPH_UNDERLINE ? 2 : 1 + mblen;
804 /* Then insert them. */
805 out = ascii_reserve (a, y, x, x + width, n_out);
806 for (ofs = 0; ofs < n; ofs += mblen)
811 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
812 w = uc_width (uc, "UTF-8");
816 if (a->emphasis == EMPH_UNDERLINE)
819 out = mempcpy (out, string + ofs, mblen);
822 out = mempcpy (out, string + ofs, mblen);
828 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
829 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
830 int *widthp, int *heightp)
832 const char *text = cell->contents;
833 size_t length = strlen (text);
844 breaks = xmalloc (length + 1);
845 u8_possible_linebreaks (CHAR_CAST (const uint8_t *, text), length,
847 breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
848 ? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
851 bb_width = bb[H][1] - bb[H][0];
852 for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
854 const uint8_t *line = CHAR_CAST (const uint8_t *, text + pos);
855 const char *b = breaks + pos;
856 size_t n = length - pos;
858 size_t last_break_ofs = 0;
859 int last_break_width = 0;
864 for (ofs = 0; ofs < n; )
870 mblen = u8_mbtouc (&uc, line + ofs, n - ofs);
871 if (b[ofs] == UC_BREAK_MANDATORY)
873 else if (b[ofs] == UC_BREAK_POSSIBLE)
875 last_break_ofs = ofs;
876 last_break_width = width;
879 w = uc_width (uc, "UTF-8");
882 if (width + w > bb_width)
884 if (isspace (line[ofs]))
886 else if (last_break_ofs != 0)
888 ofs = last_break_ofs;
889 width = last_break_width;
898 /* Trim any trailing spaces off the end of the text to be drawn. */
899 for (graph_ofs = ofs; graph_ofs > 0; graph_ofs--)
900 if (!isspace (line[graph_ofs - 1]))
902 width -= ofs - graph_ofs;
905 text_draw (a, cell->options, bb, clip, y, line, graph_ofs, width);
907 /* If a new-line ended the line, just skip the new-line. Otherwise, skip
908 past any spaces past the end of the line (but not past a new-line). */
909 if (b[ofs] == UC_BREAK_MANDATORY)
912 while (ofs < n && isspace (line[ofs]) && b[ofs] != UC_BREAK_MANDATORY)
919 *heightp = y - bb[V][0];
925 ascii_test_write (struct output_driver *driver,
926 const char *s, int x, int y, unsigned int options)
928 struct ascii_driver *a = ascii_driver_cast (driver);
929 struct table_cell cell;
930 int bb[TABLE_N_AXES][2];
933 if (a->file == NULL && !ascii_open_page (a))
937 memset (&cell, 0, sizeof cell);
939 cell.options = options | TAB_LEFT;
941 bb[TABLE_HORZ][0] = x;
942 bb[TABLE_HORZ][1] = a->width;
943 bb[TABLE_VERT][0] = y;
944 bb[TABLE_VERT][1] = a->length;
946 ascii_layout_cell (a, &cell, bb, bb, &width, &height);
952 ascii_test_set_length (struct output_driver *driver, int y, int length)
954 struct ascii_driver *a = ascii_driver_cast (driver);
956 if (a->file == NULL && !ascii_open_page (a))
959 if (y < 0 || y >= a->length)
961 u8_line_set_length (&a->lines[y], length);
964 /* ascii_close_page () and support routines. */
966 #if HAVE_DECL_SIGWINCH
967 static struct ascii_driver *the_driver;
970 winch_handler (int signum UNUSED)
972 update_page_size (the_driver, false);
977 ascii_open_page (struct ascii_driver *a)
986 a->file = fn_open (a->file_name, a->append ? "a" : "w");
989 if ( isatty (fileno (a->file)))
991 #if HAVE_DECL_SIGWINCH
992 struct sigaction action;
993 sigemptyset (&action.sa_mask);
995 action.sa_handler = winch_handler;
997 sigaction (SIGWINCH, &action, NULL);
999 a->auto_width = true;
1000 a->auto_length = true;
1005 error (0, errno, _("ascii: opening output file `%s'"),
1014 reallocate_lines (a);
1016 for (i = 0; i < a->length; i++)
1017 u8_line_clear (&a->lines[i]);
1023 output_title_line (FILE *out, int width, const char *left, const char *right)
1025 struct string s = DS_EMPTY_INITIALIZER;
1026 ds_put_byte_multiple (&s, ' ', width);
1029 size_t length = MIN (strlen (left), width);
1030 memcpy (ds_end (&s) - width, left, length);
1034 size_t length = MIN (strlen (right), width);
1035 memcpy (ds_end (&s) - length, right, length);
1037 ds_put_byte (&s, '\n');
1038 fputs (ds_cstr (&s), out);
1043 ascii_close_page (struct ascii_driver *a)
1049 if (a->file == NULL)
1052 if (!a->top_margin && !a->bottom_margin && a->squeeze_blank_lines
1053 && !a->paginate && a->page_number > 1)
1054 putc ('\n', a->file);
1056 for (i = 0; i < a->top_margin; i++)
1057 putc ('\n', a->file);
1062 r1 = xasprintf (_("%s - Page %d"), get_start_date (), a->page_number);
1063 r2 = xasprintf ("%s - %s" , version, host_system);
1065 output_title_line (a->file, a->width, a->title, r1);
1066 output_title_line (a->file, a->width, a->subtitle, r2);
1067 putc ('\n', a->file);
1074 for (y = 0; y < a->allocated_lines; y++)
1076 struct u8_line *line = &a->lines[y];
1078 if (a->squeeze_blank_lines && y > 0 && line->width == 0)
1084 putc ('\n', a->file);
1088 while (ds_chomp_byte (&line->s, ' '))
1090 fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
1091 putc ('\n', a->file);
1094 if (!a->squeeze_blank_lines)
1095 for (y = a->allocated_lines; y < a->length; y++)
1096 putc ('\n', a->file);
1098 for (i = 0; i < a->bottom_margin; i++)
1099 putc ('\n', a->file);
1101 putc ('\f', a->file);