Merge remote branch 'origin/master' into import-gui
[pspp] / src / output / ascii.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include <ctype.h>
20 #include <errno.h>
21 #include <limits.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <signal.h>
25 #include <unilbrk.h>
26 #include <unistd.h>
27 #include <unistr.h>
28 #include <uniwidth.h>
29
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"
50
51 #include "gl/error.h"
52 #include "gl/minmax.h"
53 #include "gl/xalloc.h"
54
55 #include "gettext.h"
56 #define _(msgid) gettext (msgid)
57
58 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
59 #define H TABLE_HORZ
60 #define V TABLE_VERT
61
62 #define N_BOX (RENDER_N_LINES * RENDER_N_LINES \
63                * RENDER_N_LINES * RENDER_N_LINES)
64
65 static const ucs4_t ascii_box_chars[N_BOX] =
66   {
67     ' ', '|', '#',
68     '-', '+', '#',
69     '=', '#', '#',
70     '|', '|', '#',
71     '+', '+', '#',
72     '#', '#', '#',
73     '#', '#', '#',
74     '#', '#', '#',
75     '#', '#', '#',
76     '-', '+', '#',
77     '-', '+', '#',
78     '#', '#', '#',
79     '+', '+', '#',
80     '+', '+', '#',
81     '#', '#', '#',
82     '#', '#', '#',
83     '#', '#', '#',
84     '#', '#', '#',
85     '=', '#', '#',
86     '#', '#', '#',
87     '=', '#', '#',
88     '#', '#', '#',
89     '#', '#', '#',
90     '#', '#', '#',
91     '#', '#', '#',
92     '#', '#', '#',
93     '#', '#', '#',
94   };
95
96 static const ucs4_t unicode_box_chars[N_BOX] =
97   {
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,
125   };
126
127 static inline int
128 make_box_index (int left, int right, int top, int bottom)
129 {
130   return ((right * RENDER_N_LINES + bottom) * RENDER_N_LINES + left) * RENDER_N_LINES + top;
131 }
132
133 /* How to emphasize text. */
134 enum emphasis_style
135   {
136     EMPH_BOLD,                  /* Overstrike for bold. */
137     EMPH_UNDERLINE,             /* Overstrike for underlining. */
138     EMPH_NONE                   /* No emphasis. */
139   };
140
141 /* ASCII output driver. */
142 struct ascii_driver
143   {
144     struct output_driver driver;
145
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. */
153
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? */
158
159     int top_margin;             /* Top margin in lines. */
160     int bottom_margin;          /* Bottom margin in lines. */
161
162     const ucs4_t *box;          /* Line & box drawing characters. */
163
164     /* Internal state. */
165     char *command_name;
166     char *title;
167     char *subtitle;
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. */
175     int y;
176   };
177
178 static const struct output_driver_class ascii_driver_class;
179
180 static void ascii_submit (struct output_driver *, const struct output_item *);
181
182 static int vertical_margins (const struct ascii_driver *);
183
184 static bool update_page_size (struct ascii_driver *, bool issue_error);
185 static int parse_page_size (struct driver_option *);
186
187 static void ascii_close_page (struct ascii_driver *);
188 static bool ascii_open_page (struct ascii_driver *);
189
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 *,
193                                       int *min, int *max);
194 static int ascii_measure_cell_height (void *, const struct table_cell *,
195                                       int width);
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]);
199
200 static void
201 reallocate_lines (struct ascii_driver *a)
202 {
203   if (a->length > a->allocated_lines)
204     {
205       int i;
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;
210     }
211 }
212
213
214 static struct ascii_driver *
215 ascii_driver_cast (struct output_driver *driver)
216 {
217   assert (driver->class == &ascii_driver_class);
218   return UP_CAST (driver, struct ascii_driver, driver);
219 }
220
221 static struct driver_option *
222 opt (struct output_driver *d, struct string_map *options, const char *key,
223      const char *default_value)
224 {
225   return driver_option_get (d, options, key, default_value);
226 }
227
228 static struct output_driver *
229 ascii_create (const char *file_name, enum settings_output_devices device_type,
230               struct string_map *o)
231 {
232   enum { BOX_ASCII, BOX_UNICODE } box;
233   struct output_driver *d;
234   struct ascii_driver *a;
235   int paper_length;
236
237   a = xzalloc (sizeof *a);
238   d = &a->driver;
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"),
245                             "bold", EMPH_BOLD,
246                             "underline", EMPH_UNDERLINE,
247                             "none", EMPH_NONE,
248                             NULL_SENTINEL);
249
250   a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", file_name));
251
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);
254
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);
260
261   box = parse_enum (opt (d, o, "box", "ascii"),
262                     "ascii", BOX_ASCII,
263                     "unicode", BOX_UNICODE,
264                     NULL_SENTINEL);
265   a->box = box == BOX_ASCII ? ascii_box_chars : unicode_box_chars;
266
267   a->command_name = NULL;
268   a->title = xstrdup ("");
269   a->subtitle = xstrdup ("");
270   a->file_name = xstrdup (file_name);
271   a->file = NULL;
272   a->error = false;
273   a->page_number = 0;
274   a->lines = NULL;
275   a->allocated_lines = 0;
276   a->chart_cnt = 1;
277
278   if (!update_page_size (a, true))
279     goto error;
280
281   return d;
282
283 error:
284   output_driver_destroy (d);
285   return NULL;
286 }
287
288 static int
289 parse_page_size (struct driver_option *option)
290 {
291   int dim = atol (option->default_value);
292
293   if (option->value != NULL)
294     {
295       if (!strcmp (option->value, "auto"))
296         dim = -1;
297       else
298         {
299           int value;
300           char *tail;
301
302           errno = 0;
303           value = strtol (option->value, &tail, 0);
304           if (dim >= 1 && errno != ERANGE && *tail == '\0')
305             dim = value;
306           else
307             error (0, 0, _("%s: %s must be positive integer or `auto'"),
308                    option->driver_name, option->name);
309         }
310     }
311
312   driver_option_destroy (option);
313
314   return dim;
315 }
316
317 static int
318 vertical_margins (const struct ascii_driver *a)
319 {
320   return a->top_margin + a->bottom_margin + (a->headers ? 3 : 0);
321 }
322
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. */
326 static bool
327 update_page_size (struct ascii_driver *a, bool issue_error)
328 {
329   enum { MIN_WIDTH = 6, MIN_LENGTH = 6 };
330
331   if (a->auto_width)
332     a->width = settings_get_viewwidth ();
333   if (a->auto_length)
334     a->length = settings_get_viewlength () - vertical_margins (a);
335
336   if (a->width < MIN_WIDTH || a->length < MIN_LENGTH)
337     {
338       if (issue_error)
339         error (0, 0,
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;
349       return false;
350     }
351
352   reallocate_lines (a);
353
354   return true;
355 }
356
357 static void
358 ascii_destroy (struct output_driver *driver)
359 {
360   struct ascii_driver *a = ascii_driver_cast (driver);
361   int i;
362
363   if (a->y > 0)
364     ascii_close_page (a);
365
366   if (a->file != NULL)
367     fn_close (a->file_name, a->file);
368   free (a->command_name);
369   free (a->title);
370   free (a->subtitle);
371   free (a->file_name);
372   free (a->chart_file_name);
373   for (i = 0; i < a->allocated_lines; i++)
374     u8_line_destroy (&a->lines[i]);
375   free (a->lines);
376   free (a);
377 }
378
379 static void
380 ascii_flush (struct output_driver *driver)
381 {
382   struct ascii_driver *a = ascii_driver_cast (driver);
383   if (a->y > 0)
384     {
385       ascii_close_page (a);
386
387       if (fn_close (a->file_name, a->file) != 0)
388         error (0, errno, _("ascii: closing output file `%s'"),
389                a->file_name);
390       a->file = NULL;
391     }
392 }
393
394 static void
395 ascii_init_caption_cell (const char *caption, struct table_cell *cell)
396 {
397   cell->contents = caption;
398   cell->options = TAB_LEFT;
399   cell->destructor = NULL;
400 }
401
402 static void
403 ascii_output_table_item (struct ascii_driver *a,
404                          const struct table_item *table_item)
405 {
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;
410   int caption_height;
411   int i;
412
413   update_page_size (a, false);
414
415   if (caption != NULL)
416     {
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);
421     }
422   else
423     caption_height = 0;
424
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,
429     params.aux = a;
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++)
435     {
436       int width = i == RENDER_LINE_NONE ? 0 : 1;
437       params.line_widths[H][i] = width;
438       params.line_widths[V][i] = width;
439     }
440
441   if (a->file == NULL && !ascii_open_page (a))
442     return;
443
444   page = render_page_create (&params, table_item_get_table (table_item));
445   for (render_break_init (&x_break, page, H);
446        render_break_has_next (&x_break); )
447     {
448       struct render_page *x_slice;
449       struct render_break y_break;
450
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); )
454         {
455           struct render_page *y_slice;
456           int space;
457
458           if (a->y > 0)
459             a->y++;
460
461           space = a->length - a->y - caption_height;
462           if (render_break_next_size (&y_break) > space)
463             {
464               assert (a->y > 0);
465               ascii_close_page (a);
466               if (!ascii_open_page (a))
467                 return;
468               continue;
469             }
470
471           y_slice = render_break_next (&y_break, space);
472           if (caption_height)
473             {
474               struct table_cell cell;
475               int bb[TABLE_N_AXES][2];
476
477               ascii_init_caption_cell (caption, &cell);
478               bb[H][0] = 0;
479               bb[H][1] = a->width;
480               bb[V][0] = 0;
481               bb[V][1] = caption_height;
482               ascii_draw_cell (a, &cell, bb, bb);
483               a->y += caption_height;
484               caption_height = 0;
485             }
486           render_page_draw (y_slice);
487           a->y += render_page_get_size (y_slice, V);
488           render_page_unref (y_slice);
489         }
490       render_break_destroy (&y_break);
491     }
492   render_break_destroy (&x_break);
493 }
494
495 static void
496 ascii_output_text (struct ascii_driver *a, const char *text)
497 {
498   struct table_item *table_item;
499
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);
503 }
504
505 static void
506 ascii_submit (struct output_driver *driver,
507               const struct output_item *output_item)
508 {
509   struct ascii_driver *a = ascii_driver_cast (driver);
510
511   output_driver_track_current_command (output_item, &a->command_name);
512
513   if (a->error)
514     return;
515
516   if (is_table_item (output_item))
517     ascii_output_table_item (a, to_table_item (output_item));
518 #ifdef HAVE_CAIRO
519   else if (is_chart_item (output_item) && a->chart_file_name != NULL)
520     {
521       struct chart_item *chart_item = to_chart_item (output_item);
522       char *file_name;
523
524       file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
525                                      a->chart_cnt++);
526       if (file_name != NULL)
527         {
528           struct text_item *text_item;
529
530           text_item = text_item_create_format (
531             TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
532
533           ascii_submit (driver, &text_item->output_item);
534           text_item_unref (text_item);
535           free (file_name);
536         }
537     }
538 #endif  /* HAVE_CAIRO */
539   else if (is_text_item (output_item))
540     {
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);
544
545       switch (type)
546         {
547         case TEXT_ITEM_TITLE:
548           free (a->title);
549           a->title = xstrdup (text);
550           break;
551
552         case TEXT_ITEM_SUBTITLE:
553           free (a->subtitle);
554           a->subtitle = xstrdup (text);
555           break;
556
557         case TEXT_ITEM_COMMAND_OPEN:
558         case TEXT_ITEM_COMMAND_CLOSE:
559           break;
560
561         case TEXT_ITEM_BLANK_LINE:
562           if (a->y > 0)
563             a->y++;
564           break;
565
566         case TEXT_ITEM_EJECT_PAGE:
567           if (a->y > 0)
568             ascii_close_page (a);
569           break;
570
571         default:
572           ascii_output_text (a, text);
573           break;
574         }
575     }
576   else if (is_message_item (output_item))
577     {
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);
582       free (s);
583     }
584 }
585
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 };
590
591 static const struct output_driver_class ascii_driver_class =
592   {
593     "text",
594     ascii_destroy,
595     ascii_submit,
596     ascii_flush,
597   };
598 \f
599 static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
600                             int n);
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);
606
607 static void
608 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
609                  enum render_line_style styles[TABLE_N_AXES][2])
610 {
611   struct ascii_driver *a = a_;
612   char mbchar[6];
613   int x0, x1, y1;
614   ucs4_t uc;
615   int mblen;
616   int x, y;
617
618   /* Clip to the page. */
619   if (bb[H][0] >= a->width || bb[V][0] + a->y >= a->length)
620     return;
621   x0 = bb[H][0];
622   x1 = MIN (bb[H][1], a->width);
623   y1 = MIN (bb[V][1] + a->y, a->length);
624
625   /* Draw. */
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++)
630     {
631       char *p = ascii_reserve (a, y, x0, x1, mblen * (x1 - x0));
632       for (x = x0; x < x1; x++)
633         {
634           memcpy (p, mbchar, mblen);
635           p += mblen;
636         }
637     }
638 }
639
640 static void
641 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
642                           int *min_width, int *max_width)
643 {
644   struct ascii_driver *a = a_;
645   int bb[TABLE_N_AXES][2];
646   int clip[TABLE_N_AXES][2];
647   int h;
648
649   bb[H][0] = 0;
650   bb[H][1] = INT_MAX;
651   bb[V][0] = 0;
652   bb[V][1] = INT_MAX;
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);
655
656   if (strchr (cell->contents, ' '))
657     {
658       bb[H][1] = 1;
659       ascii_layout_cell (a, cell, bb, clip, min_width, &h);
660     }
661   else
662     *min_width = *max_width;
663 }
664
665 static int
666 ascii_measure_cell_height (void *a_, const struct table_cell *cell, int width)
667 {
668   struct ascii_driver *a = a_;
669   int bb[TABLE_N_AXES][2];
670   int clip[TABLE_N_AXES][2];
671   int w, h;
672
673   bb[H][0] = 0;
674   bb[H][1] = width;
675   bb[V][0] = 0;
676   bb[V][1] = INT_MAX;
677   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
678   ascii_layout_cell (a, cell, bb, clip, &w, &h);
679   return h;
680 }
681
682 static void
683 ascii_draw_cell (void *a_, const struct table_cell *cell,
684                  int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
685 {
686   struct ascii_driver *a = a_;
687   int w, h;
688
689   ascii_layout_cell (a, cell, bb, clip, &w, &h);
690 }
691
692 static char *
693 ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
694 {
695   assert (y < a->allocated_lines);
696   return u8_line_reserve (&a->lines[y], x0, x1, n);
697 }
698
699 static void
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)
703 {
704   int x0 = MAX (0, clip[H][0]);
705   int y0 = MAX (0, clip[V][0] + a->y);
706   int x1 = clip[H][1];
707   int y1 = MIN (a->length, clip[V][1] + a->y);
708   int x;
709
710   y += a->y;
711   if (y < y0 || y >= y1)
712     return;
713
714   switch (options & TAB_ALIGNMENT)
715     {
716     case TAB_LEFT:
717       x = bb[H][0];
718       break;
719     case TAB_CENTER:
720       x = (bb[H][0] + bb[H][1] - width + 1) / 2;
721       break;
722     case TAB_RIGHT:
723       x = bb[H][1] - width;
724       break;
725     default:
726       NOT_REACHED ();
727     }
728   if (x >= x1)
729     return;
730
731   while (x < x0)
732     {
733       ucs4_t uc;
734       int mblen;
735       int w;
736
737       if (n == 0)
738         return;
739       mblen = u8_mbtouc (&uc, string, n);
740
741       string += mblen;
742       n -= mblen;
743
744       w = uc_width (uc, "UTF-8");
745       if (w > 0)
746         {
747           x += w;
748           width -= w;
749         }
750     }
751   if (n == 0)
752     return;
753
754   if (x + width > x1)
755     {
756       int ofs;
757
758       ofs = width = 0;
759       for (ofs = 0; ofs < n; )
760         {
761           ucs4_t uc;
762           int mblen;
763           int w;
764
765           mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
766
767           w = uc_width (uc, "UTF-8");
768           if (w > 0)
769             {
770               if (width + w > x1 - x)
771                 break;
772               width += w;
773             }
774           ofs += mblen;
775         }
776       n = ofs;
777       if (n == 0)
778         return;
779     }
780
781   if (!(options & TAB_EMPH) || a->emphasis == EMPH_NONE)
782     memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
783   else
784     {
785       size_t n_out;
786       size_t ofs;
787       char *out;
788       int mblen;
789
790       /* First figure out how many bytes need to be inserted. */
791       n_out = n;
792       for (ofs = 0; ofs < n; ofs += mblen)
793         {
794           ucs4_t uc;
795           int w;
796
797           mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
798           w = uc_width (uc, "UTF-8");
799
800           if (w > 0)
801             n_out += a->emphasis == EMPH_UNDERLINE ? 2 : 1 + mblen;
802         }
803
804       /* Then insert them. */
805       out = ascii_reserve (a, y, x, x + width, n_out);
806       for (ofs = 0; ofs < n; ofs += mblen)
807         {
808           ucs4_t uc;
809           int w;
810
811           mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
812           w = uc_width (uc, "UTF-8");
813
814           if (w > 0)
815             {
816               if (a->emphasis == EMPH_UNDERLINE)
817                 *out++ = '_';
818               else
819                 out = mempcpy (out, string + ofs, mblen);
820               *out++ = '\b';
821             }
822           out = mempcpy (out, string + ofs, mblen);
823         }
824     }
825 }
826
827 static void
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)
831 {
832   const char *text = cell->contents;
833   size_t length = strlen (text);
834   char *breaks;
835   int bb_width;
836   size_t pos;
837   int y;
838
839   *widthp = 0;
840   *heightp = 0;
841   if (length == 0)
842     return;
843
844   breaks = xmalloc (length + 1);
845   u8_possible_linebreaks (CHAR_CAST (const uint8_t *, text), length,
846                           "UTF-8", breaks);
847   breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
848                     ? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
849
850   pos = 0;
851   bb_width = bb[H][1] - bb[H][0];
852   for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
853     {
854       const uint8_t *line = CHAR_CAST (const uint8_t *, text + pos);
855       const char *b = breaks + pos;
856       size_t n = length - pos;
857
858       size_t last_break_ofs = 0;
859       int last_break_width = 0;
860       int width = 0;
861       size_t ofs;
862
863       for (ofs = 0; ofs < n; )
864         {
865           ucs4_t uc;
866           int mblen;
867           int w;
868
869           mblen = u8_mbtouc (&uc, line + ofs, n - ofs);
870           if (b[ofs] == UC_BREAK_MANDATORY)
871             break;
872           else if (b[ofs] == UC_BREAK_POSSIBLE)
873             {
874               last_break_ofs = ofs;
875               last_break_width = width;
876             }
877
878           w = uc_width (uc, "UTF-8");
879           if (w > 0)
880             {
881               if (width + w > bb_width)
882                 {
883                   if (isspace (line[ofs]))
884                     break;
885                   else if (last_break_ofs != 0)
886                     {
887                       ofs = last_break_ofs;
888                       width = last_break_width;
889                       break;
890                     }
891                 }
892               width += w;
893             }
894           ofs += mblen;
895         }
896       if (b[ofs] != UC_BREAK_MANDATORY)
897         {
898           while (ofs > 0 && isspace (line[ofs - 1]))
899             {
900               ofs--;
901               width--;
902             }
903         }
904       if (width > *widthp)
905         *widthp = width;
906
907       /* Draw text. */
908       text_draw (a, cell->options, bb, clip, y, line, ofs, width);
909
910       /* Next line. */
911       pos += ofs;
912       if (ofs < n && isspace (line[ofs]))
913         pos++;
914
915     }
916   *heightp = y - bb[V][0];
917
918   free (breaks);
919 }
920
921 void
922 ascii_test_write (struct output_driver *driver,
923                   const char *s, int x, int y, unsigned int options)
924 {
925   struct ascii_driver *a = ascii_driver_cast (driver);
926   struct table_cell cell;
927   int bb[TABLE_N_AXES][2];
928   int width, height;
929
930   if (a->file == NULL && !ascii_open_page (a))
931     return;
932   a->y = 0;
933
934   memset (&cell, 0, sizeof cell);
935   cell.contents = s;
936   cell.options = options | TAB_LEFT;
937
938   bb[TABLE_HORZ][0] = x;
939   bb[TABLE_HORZ][1] = a->width;
940   bb[TABLE_VERT][0] = y;
941   bb[TABLE_VERT][1] = a->length;
942
943   ascii_layout_cell (a, &cell, bb, bb, &width, &height);
944
945   a->y = 1;
946 }
947
948 void
949 ascii_test_set_length (struct output_driver *driver, int y, int length)
950 {
951   struct ascii_driver *a = ascii_driver_cast (driver);
952
953   if (a->file == NULL && !ascii_open_page (a))
954     return;
955
956   if (y < 0 || y >= a->length)
957     return;
958   u8_line_set_length (&a->lines[y], length);
959 }
960 \f
961 /* ascii_close_page () and support routines. */
962
963 #if HAVE_DECL_SIGWINCH
964 static struct ascii_driver *the_driver;
965
966 static void
967 winch_handler (int signum UNUSED)
968 {
969   update_page_size (the_driver, false);
970 }
971 #endif
972
973 static bool
974 ascii_open_page (struct ascii_driver *a)
975 {
976   int i;
977
978   if (a->error)
979     return false;
980
981   if (a->file == NULL)
982     {
983       a->file = fn_open (a->file_name, a->append ? "a" : "w");
984       if (a->file != NULL)
985         {
986           if ( isatty (fileno (a->file)))
987             {
988 #if HAVE_DECL_SIGWINCH
989               struct sigaction action;
990               sigemptyset (&action.sa_mask);
991               action.sa_flags = 0;
992               action.sa_handler = winch_handler;
993               the_driver = a;
994               sigaction (SIGWINCH, &action, NULL);
995 #endif
996               a->auto_width = true;
997               a->auto_length = true;
998             }
999         }
1000       else
1001         {
1002           error (0, errno, _("ascii: opening output file `%s'"),
1003                  a->file_name);
1004           a->error = true;
1005           return false;
1006         }
1007     }
1008
1009   a->page_number++;
1010
1011   reallocate_lines (a);
1012
1013   for (i = 0; i < a->length; i++)
1014     u8_line_clear (&a->lines[i]);
1015
1016   return true;
1017 }
1018
1019 static void
1020 output_title_line (FILE *out, int width, const char *left, const char *right)
1021 {
1022   struct string s = DS_EMPTY_INITIALIZER;
1023   ds_put_byte_multiple (&s, ' ', width);
1024   if (left != NULL)
1025     {
1026       size_t length = MIN (strlen (left), width);
1027       memcpy (ds_end (&s) - width, left, length);
1028     }
1029   if (right != NULL)
1030     {
1031       size_t length = MIN (strlen (right), width);
1032       memcpy (ds_end (&s) - length, right, length);
1033     }
1034   ds_put_byte (&s, '\n');
1035   fputs (ds_cstr (&s), out);
1036   ds_destroy (&s);
1037 }
1038
1039 static void
1040 ascii_close_page (struct ascii_driver *a)
1041 {
1042   bool any_blank;
1043   int i, y;
1044
1045   a->y = 0;
1046   if (a->file == NULL)
1047     return;
1048
1049   if (!a->top_margin && !a->bottom_margin && a->squeeze_blank_lines
1050       && !a->paginate && a->page_number > 1)
1051     putc ('\n', a->file);
1052
1053   for (i = 0; i < a->top_margin; i++)
1054     putc ('\n', a->file);
1055   if (a->headers)
1056     {
1057       char *r1, *r2;
1058
1059       r1 = xasprintf (_("%s - Page %d"), get_start_date (), a->page_number);
1060       r2 = xasprintf ("%s - %s" , version, host_system);
1061
1062       output_title_line (a->file, a->width, a->title, r1);
1063       output_title_line (a->file, a->width, a->subtitle, r2);
1064       putc ('\n', a->file);
1065
1066       free (r1);
1067       free (r2);
1068     }
1069
1070   any_blank = false;
1071   for (y = 0; y < a->allocated_lines; y++)
1072     {
1073       struct u8_line *line = &a->lines[y];
1074
1075       if (a->squeeze_blank_lines && y > 0 && line->width == 0)
1076         any_blank = true;
1077       else
1078         {
1079           if (any_blank)
1080             {
1081               putc ('\n', a->file);
1082               any_blank = false;
1083             }
1084
1085           while (ds_chomp_byte (&line->s, ' '))
1086             continue;
1087           fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
1088           putc ('\n', a->file);
1089         }
1090     }
1091   if (!a->squeeze_blank_lines)
1092     for (y = a->allocated_lines; y < a->length; y++)
1093       putc ('\n', a->file);
1094
1095   for (i = 0; i < a->bottom_margin; i++)
1096     putc ('\n', a->file);
1097   if (a->paginate)
1098     putc ('\f', a->file);
1099 }