1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2007, 2009, 2010 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/>. */
27 #include "data/file-name.h"
28 #include "data/settings.h"
29 #include "libpspp/assertion.h"
30 #include "libpspp/cast.h"
31 #include "libpspp/compiler.h"
32 #include "libpspp/message.h"
33 #include "libpspp/start-date.h"
34 #include "libpspp/string-map.h"
35 #include "libpspp/version.h"
36 #include "output/cairo.h"
37 #include "output/chart-item-provider.h"
38 #include "output/driver-provider.h"
39 #include "output/message-item.h"
40 #include "output/options.h"
41 #include "output/render.h"
42 #include "output/tab.h"
43 #include "output/table-item.h"
44 #include "output/text-item.h"
47 #include "gl/minmax.h"
48 #include "gl/xalloc.h"
51 #define _(msgid) gettext (msgid)
53 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
57 /* Line styles bit shifts. */
69 make_box_index (int left, int right, int top, int bottom)
71 return ((left << LNS_LEFT) | (right << LNS_RIGHT)
72 | (top << LNS_TOP) | (bottom << LNS_BOTTOM));
75 /* Character attributes. */
76 #define ATTR_EMPHASIS 0x100 /* Bold-face. */
77 #define ATTR_BOX 0x200 /* Line drawing character. */
82 unsigned short *chars; /* Characters and attributes. */
83 int n_chars; /* Length. */
84 int allocated_chars; /* Allocated "chars" elements. */
87 /* How to emphasize text. */
90 EMPH_BOLD, /* Overstrike for bold. */
91 EMPH_UNDERLINE, /* Overstrike for underlining. */
92 EMPH_NONE /* No emphasis. */
95 /* ASCII output driver. */
98 struct output_driver driver;
100 /* User parameters. */
101 bool append; /* Append if output file already exists? */
102 bool headers; /* Print headers at top of page? */
103 bool paginate; /* Insert formfeeds? */
104 bool squeeze_blank_lines; /* Squeeze multiple blank lines into one? */
105 enum emphasis_style emphasis; /* How to emphasize text. */
106 char *chart_file_name; /* Name of files used for charts. */
108 int width; /* Page width. */
109 int length; /* Page length minus margins and header. */
110 bool auto_width; /* Use viewwidth as page width? */
111 bool auto_length; /* Use viewlength as page width? */
113 int top_margin; /* Top margin in lines. */
114 int bottom_margin; /* Bottom margin in lines. */
116 char *box[LNS_COUNT]; /* Line & box drawing characters. */
117 char *init; /* Device initialization string. */
119 /* Internal state. */
123 char *file_name; /* Output file name. */
124 FILE *file; /* Output file. */
125 bool error; /* Output error? */
126 int page_number; /* Current page number. */
127 struct ascii_line *lines; /* Page content. */
128 int allocated_lines; /* Number of lines allocated. */
129 int chart_cnt; /* Number of charts so far. */
133 static const struct output_driver_class ascii_driver_class;
135 static void ascii_submit (struct output_driver *, const struct output_item *);
137 static int vertical_margins (const struct ascii_driver *);
139 static const char *get_default_box (int right, int bottom, int left, int top);
140 static bool update_page_size (struct ascii_driver *, bool issue_error);
141 static int parse_page_size (struct driver_option *);
143 static void ascii_close_page (struct ascii_driver *);
144 static bool ascii_open_page (struct ascii_driver *);
146 static void ascii_draw_line (void *, int bb[TABLE_N_AXES][2],
147 enum render_line_style styles[TABLE_N_AXES][2]);
148 static void ascii_measure_cell_width (void *, const struct table_cell *,
150 static int ascii_measure_cell_height (void *, const struct table_cell *,
152 static void ascii_draw_cell (void *, const struct table_cell *,
153 int bb[TABLE_N_AXES][2],
154 int clip[TABLE_N_AXES][2]);
156 static struct ascii_driver *
157 ascii_driver_cast (struct output_driver *driver)
159 assert (driver->class == &ascii_driver_class);
160 return UP_CAST (driver, struct ascii_driver, driver);
163 static struct driver_option *
164 opt (struct output_driver *d, struct string_map *options, const char *key,
165 const char *default_value)
167 return driver_option_get (d, options, key, default_value);
170 static struct output_driver *
171 ascii_create (const char *file_name, enum settings_output_devices device_type,
172 struct string_map *o)
174 struct output_driver *d;
175 struct ascii_driver *a;
177 int right, bottom, left, top;
179 a = xzalloc (sizeof *a);
181 output_driver_init (&a->driver, &ascii_driver_class, file_name, device_type);
182 a->append = parse_boolean (opt (d, o, "append", "false"));
183 a->headers = parse_boolean (opt (d, o, "headers", "false"));
184 a->paginate = parse_boolean (opt (d, o, "paginate", "false"));
185 a->squeeze_blank_lines = parse_boolean (opt (d, o, "squeeze", "true"));
186 a->emphasis = parse_enum (opt (d, o, "emphasis", "none"),
188 "underline", EMPH_UNDERLINE,
192 a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", file_name));
194 a->top_margin = parse_int (opt (d, o, "top-margin", "0"), 0, INT_MAX);
195 a->bottom_margin = parse_int (opt (d, o, "bottom-margin", "0"), 0, INT_MAX);
197 a->width = parse_page_size (opt (d, o, "width", "79"));
198 paper_length = parse_page_size (opt (d, o, "length", "66"));
199 a->auto_width = a->width < 0;
200 a->auto_length = paper_length < 0;
201 a->length = paper_length - vertical_margins (a);
203 for (right = 0; right < 4; right++)
204 for (bottom = 0; bottom < 4; bottom++)
205 for (left = 0; left < 4; left++)
206 for (top = 0; top < 4; top++)
208 int indx = make_box_index (left, right, top, bottom);
209 const char *default_value;
212 sprintf (name, "box[%d%d%d%d]", right, bottom, left, top);
213 default_value = get_default_box (right, bottom, left, top);
214 a->box[indx] = parse_string (opt (d, o, name, default_value));
216 a->init = parse_string (opt (d, o, "init", ""));
218 a->command_name = NULL;
219 a->title = xstrdup ("");
220 a->subtitle = xstrdup ("");
221 a->file_name = xstrdup (file_name);
226 a->allocated_lines = 0;
229 if (!update_page_size (a, true))
235 output_driver_destroy (d);
240 get_default_box (int right, int bottom, int left, int top)
242 switch ((top << 12) | (left << 8) | (bottom << 4) | (right << 0))
247 case 0x0100: case 0x0101: case 0x0001:
250 case 0x1000: case 0x1010: case 0x0010:
253 case 0x0300: case 0x0303: case 0x0003:
254 case 0x0200: case 0x0202: case 0x0002:
258 return left > 1 || top > 1 || right > 1 || bottom > 1 ? "#" : "+";
263 parse_page_size (struct driver_option *option)
265 int dim = atol (option->default_value);
267 if (option->value != NULL)
269 if (!strcmp (option->value, "auto"))
277 value = strtol (option->value, &tail, 0);
278 if (dim >= 1 && errno != ERANGE && *tail == '\0')
281 error (0, 0, _("%s: %s must be positive integer or `auto'"),
282 option->driver_name, option->name);
286 driver_option_destroy (option);
292 vertical_margins (const struct ascii_driver *a)
294 return a->top_margin + a->bottom_margin + (a->headers ? 3 : 0);
297 /* Re-calculates the page width and length based on settings,
298 margins, and, if "auto" is set, the size of the user's
299 terminal window or GUI output window. */
301 update_page_size (struct ascii_driver *a, bool issue_error)
303 enum { MIN_WIDTH = 6, MIN_LENGTH = 6 };
306 a->width = settings_get_viewwidth ();
308 a->length = settings_get_viewlength () - vertical_margins (a);
310 if (a->width < MIN_WIDTH || a->length < MIN_LENGTH)
314 _("ascii: page excluding margins and headers "
315 "must be at least %d characters wide by %d lines long, but "
316 "as configured is only %d characters by %d lines"),
317 MIN_WIDTH, MIN_LENGTH,
318 a->width, a->length);
319 if (a->width < MIN_WIDTH)
320 a->width = MIN_WIDTH;
321 if (a->length < MIN_LENGTH)
322 a->length = MIN_LENGTH;
330 ascii_destroy (struct output_driver *driver)
332 struct ascii_driver *a = ascii_driver_cast (driver);
336 ascii_close_page (a);
339 fn_close (a->file_name, a->file);
340 free (a->command_name);
344 free (a->chart_file_name);
345 for (i = 0; i < LNS_COUNT; i++)
348 for (i = 0; i < a->allocated_lines; i++)
349 free (a->lines[i].chars);
355 ascii_flush (struct output_driver *driver)
357 struct ascii_driver *a = ascii_driver_cast (driver);
360 ascii_close_page (a);
362 if (fn_close (a->file_name, a->file) != 0)
363 error (0, errno, _("ascii: closing output file `%s'"),
370 ascii_init_caption_cell (const char *caption, struct table_cell *cell)
372 cell->contents = caption;
373 cell->options = TAB_LEFT;
374 cell->destructor = NULL;
378 ascii_output_table_item (struct ascii_driver *a,
379 const struct table_item *table_item)
381 const char *caption = table_item_get_caption (table_item);
382 struct render_params params;
383 struct render_page *page;
384 struct render_break x_break;
388 update_page_size (a, false);
392 /* XXX doesn't do well with very large captions */
393 struct table_cell cell;
394 ascii_init_caption_cell (caption, &cell);
395 caption_height = ascii_measure_cell_height (a, &cell, a->width);
400 params.draw_line = ascii_draw_line;
401 params.measure_cell_width = ascii_measure_cell_width;
402 params.measure_cell_height = ascii_measure_cell_height;
403 params.draw_cell = ascii_draw_cell,
405 params.size[H] = a->width;
406 params.size[V] = a->length - caption_height;
407 params.font_size[H] = 1;
408 params.font_size[V] = 1;
409 for (i = 0; i < RENDER_N_LINES; i++)
411 int width = i == RENDER_LINE_NONE ? 0 : 1;
412 params.line_widths[H][i] = width;
413 params.line_widths[V][i] = width;
416 if (a->file == NULL && !ascii_open_page (a))
419 page = render_page_create (¶ms, table_item_get_table (table_item));
420 for (render_break_init (&x_break, page, H);
421 render_break_has_next (&x_break); )
423 struct render_page *x_slice;
424 struct render_break y_break;
426 x_slice = render_break_next (&x_break, a->width);
427 for (render_break_init (&y_break, x_slice, V);
428 render_break_has_next (&y_break); )
430 struct render_page *y_slice;
436 space = a->length - a->y - caption_height;
437 if (render_break_next_size (&y_break) > space)
440 ascii_close_page (a);
441 if (!ascii_open_page (a))
446 y_slice = render_break_next (&y_break, space);
449 struct table_cell cell;
450 int bb[TABLE_N_AXES][2];
452 ascii_init_caption_cell (caption, &cell);
456 bb[V][1] = caption_height;
457 ascii_draw_cell (a, &cell, bb, bb);
458 a->y += caption_height;
461 render_page_draw (y_slice);
462 a->y += render_page_get_size (y_slice, V);
463 render_page_unref (y_slice);
465 render_break_destroy (&y_break);
467 render_break_destroy (&x_break);
471 ascii_output_text (struct ascii_driver *a, const char *text)
473 struct table_item *table_item;
475 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
476 ascii_output_table_item (a, table_item);
477 table_item_unref (table_item);
481 ascii_submit (struct output_driver *driver,
482 const struct output_item *output_item)
484 struct ascii_driver *a = ascii_driver_cast (driver);
486 output_driver_track_current_command (output_item, &a->command_name);
491 if (is_table_item (output_item))
492 ascii_output_table_item (a, to_table_item (output_item));
494 else if (is_chart_item (output_item) && a->chart_file_name != NULL)
496 struct chart_item *chart_item = to_chart_item (output_item);
499 file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
501 if (file_name != NULL)
503 struct text_item *text_item;
505 text_item = text_item_create_format (
506 TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
508 ascii_submit (driver, &text_item->output_item);
509 text_item_unref (text_item);
513 #endif /* HAVE_CAIRO */
514 else if (is_text_item (output_item))
516 const struct text_item *text_item = to_text_item (output_item);
517 enum text_item_type type = text_item_get_type (text_item);
518 const char *text = text_item_get_text (text_item);
522 case TEXT_ITEM_TITLE:
524 a->title = xstrdup (text);
527 case TEXT_ITEM_SUBTITLE:
529 a->subtitle = xstrdup (text);
532 case TEXT_ITEM_COMMAND_CLOSE:
535 case TEXT_ITEM_BLANK_LINE:
540 case TEXT_ITEM_EJECT_PAGE:
542 ascii_close_page (a);
546 ascii_output_text (a, text);
550 else if (is_message_item (output_item))
552 const struct message_item *message_item = to_message_item (output_item);
553 const struct msg *msg = message_item_get_msg (message_item);
554 char *s = msg_to_string (msg, a->command_name);
555 ascii_output_text (a, s);
560 const struct output_driver_factory txt_driver_factory =
561 { "txt", ascii_create };
562 const struct output_driver_factory list_driver_factory =
563 { "list", ascii_create };
565 static const struct output_driver_class ascii_driver_class =
580 static void ascii_expand_line (struct ascii_driver *, int y, int length);
581 static void ascii_layout_cell (struct ascii_driver *,
582 const struct table_cell *,
583 int bb[TABLE_N_AXES][2],
584 int clip[TABLE_N_AXES][2], enum wrap_mode wrap,
585 int *width, int *height);
588 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
589 enum render_line_style styles[TABLE_N_AXES][2])
591 struct ascii_driver *a = a_;
592 unsigned short int value;
596 /* Clip to the page. */
597 if (bb[H][0] >= a->width || bb[V][0] + a->y >= a->length)
599 x1 = MIN (bb[H][1], a->width);
600 y1 = MIN (bb[V][1] + a->y, a->length);
603 value = ATTR_BOX | make_box_index (styles[V][0], styles[V][1],
604 styles[H][0], styles[H][1]);
605 for (y = bb[V][0] + a->y; y < y1; y++)
607 ascii_expand_line (a, y, x1);
608 for (x = bb[H][0]; x < x1; x++)
609 a->lines[y].chars[x] = value;
614 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
615 int *min_width, int *max_width)
617 struct ascii_driver *a = a_;
618 int bb[TABLE_N_AXES][2];
619 int clip[TABLE_N_AXES][2];
626 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
627 ascii_layout_cell (a, cell, bb, clip, WRAP_WORD, max_width, &h);
629 if (strchr (cell->contents, ' '))
632 ascii_layout_cell (a, cell, bb, clip, WRAP_WORD, min_width, &h);
635 *min_width = *max_width;
639 ascii_measure_cell_height (void *a_, const struct table_cell *cell, int width)
641 struct ascii_driver *a = a_;
642 int bb[TABLE_N_AXES][2];
643 int clip[TABLE_N_AXES][2];
650 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
651 ascii_layout_cell (a, cell, bb, clip, WRAP_WORD, &w, &h);
656 ascii_draw_cell (void *a_, const struct table_cell *cell,
657 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
659 struct ascii_driver *a = a_;
662 ascii_layout_cell (a, cell, bb, clip, WRAP_WORD, &w, &h);
665 /* Ensures that at least the first LENGTH characters of line Y in
666 ascii driver A have been cleared out. */
668 ascii_expand_line (struct ascii_driver *a, int y, int length)
670 struct ascii_line *line = &a->lines[y];
671 if (line->n_chars < length)
674 if (line->allocated_chars < length)
676 line->allocated_chars = MAX (length, MIN (length * 2, a->width));
677 line->chars = xnrealloc (line->chars, line->allocated_chars,
678 sizeof *line->chars);
680 for (x = line->n_chars; x < length; x++)
681 line->chars[x] = ' ';
682 line->n_chars = length;
687 text_draw (struct ascii_driver *a, const struct table_cell *cell,
688 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
689 int y, const char *string, int n)
691 int x0 = MAX (0, clip[H][0]);
692 int y0 = MAX (0, clip[V][0] + a->y);
694 int y1 = MIN (a->length, clip[V][1] + a->y);
698 if (y < y0 || y >= y1)
701 switch (cell->options & TAB_ALIGNMENT)
707 x = (bb[H][0] + bb[H][1] - n + 1) / 2;
729 int attr = cell->options & TAB_EMPH ? ATTR_EMPHASIS : 0;
732 ascii_expand_line (a, y, x + n);
733 for (i = 0; i < n; i++)
734 a->lines[y].chars[x + i] = string[i] | attr;
739 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
740 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
741 enum wrap_mode wrap, int *width, int *height)
743 size_t length = strlen (cell->contents);
748 for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
750 const char *line = &cell->contents[pos];
751 const char *new_line;
754 /* Find line length without considering word wrap. */
755 line_len = MIN (bb[H][1] - bb[H][0], length - pos);
756 new_line = memchr (line, '\n', line_len);
757 if (new_line != NULL)
758 line_len = new_line - line;
761 if (pos + line_len < length && wrap != WRAP_CHAR)
763 size_t space_len = line_len;
764 while (space_len > 0 && !isspace ((unsigned char) line[space_len]))
767 line_len = space_len;
768 else if (wrap == WRAP_WORD)
770 while (pos + line_len < length
771 && !isspace ((unsigned char) line[line_len]))
775 if (line_len > *width)
779 text_draw (a, cell, bb, clip, y, line, line_len);
783 if (pos < length && isspace ((unsigned char) cell->contents[pos]))
786 *height = y - bb[V][0];
789 /* ascii_close_page () and support routines. */
792 #if HAVE_DECL_SIGWINCH
793 static struct ascii_driver *the_driver;
796 winch_handler (int signum UNUSED)
798 update_page_size (the_driver, false);
803 ascii_open_page (struct ascii_driver *a)
812 a->file = fn_open (a->file_name, a->append ? "a" : "w");
815 #if HAVE_DECL_SIGWINCH
816 if ( isatty (fileno (a->file)))
818 struct sigaction action;
819 sigemptyset (&action.sa_mask);
821 action.sa_handler = winch_handler;
823 a->auto_width = true;
824 a->auto_length = true;
825 sigaction (SIGWINCH, &action, NULL);
829 fputs (a->init, a->file);
833 error (0, errno, _("ascii: opening output file `%s'"),
842 if (a->length > a->allocated_lines)
844 a->lines = xnrealloc (a->lines, a->length, sizeof *a->lines);
845 for (i = a->allocated_lines; i < a->length; i++)
847 struct ascii_line *line = &a->lines[i];
849 line->allocated_chars = 0;
851 a->allocated_lines = a->length;
854 for (i = 0; i < a->length; i++)
855 a->lines[i].n_chars = 0;
860 /* Writes LINE to A's output file. */
862 output_line (struct ascii_driver *a, const struct ascii_line *line)
867 length = line->n_chars;
868 while (length > 0 && line->chars[length - 1] == ' ')
871 for (i = 0; i < length; i++)
873 int attribute = line->chars[i] & (ATTR_BOX | ATTR_EMPHASIS);
874 int ch = line->chars[i] & ~(ATTR_BOX | ATTR_EMPHASIS);
879 fputs (a->box[ch], a->file);
883 if (a->emphasis == EMPH_BOLD)
884 fprintf (a->file, "%c\b%c", ch, ch);
885 else if (a->emphasis == EMPH_UNDERLINE)
886 fprintf (a->file, "_\b%c", ch);
897 putc ('\n', a->file);
901 output_title_line (FILE *out, int width, const char *left, const char *right)
903 struct string s = DS_EMPTY_INITIALIZER;
904 ds_put_char_multiple (&s, ' ', width);
907 size_t length = MIN (strlen (left), width);
908 memcpy (ds_end (&s) - width, left, length);
912 size_t length = MIN (strlen (right), width);
913 memcpy (ds_end (&s) - length, right, length);
915 ds_put_char (&s, '\n');
916 fputs (ds_cstr (&s), out);
921 ascii_close_page (struct ascii_driver *a)
930 if (!a->top_margin && !a->bottom_margin && a->squeeze_blank_lines
931 && !a->paginate && a->page_number > 1)
932 putc ('\n', a->file);
934 for (i = 0; i < a->top_margin; i++)
935 putc ('\n', a->file);
940 r1 = xasprintf (_("%s - Page %d"), get_start_date (), a->page_number);
941 r2 = xasprintf ("%s - %s" , version, host_system);
943 output_title_line (a->file, a->width, a->title, r1);
944 output_title_line (a->file, a->width, a->subtitle, r2);
945 putc ('\n', a->file);
952 for (y = 0; y < a->allocated_lines; y++)
954 struct ascii_line *line = &a->lines[y];
956 if (a->squeeze_blank_lines && y > 0 && line->n_chars == 0)
962 putc ('\n', a->file);
966 output_line (a, line);
969 if (!a->squeeze_blank_lines)
970 for (y = a->allocated_lines; y < a->length; y++)
971 putc ('\n', a->file);
973 for (i = 0; i < a->bottom_margin; i++)
974 putc ('\n', a->file);
976 putc ('\f', a->file);