1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011, 2012, 2013, 2014 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/>. */
34 #ifdef GWINSZ_IN_SYS_IOCTL
35 # include <sys/ioctl.h>
38 #include "data/file-name.h"
39 #include "data/file-handle-def.h"
40 #include "data/settings.h"
41 #include "libpspp/assertion.h"
42 #include "libpspp/cast.h"
43 #include "libpspp/compiler.h"
44 #include "libpspp/message.h"
45 #include "libpspp/start-date.h"
46 #include "libpspp/string-map.h"
47 #include "libpspp/u8-line.h"
48 #include "libpspp/version.h"
49 #include "output/ascii.h"
51 #include "output/cairo-chart.h"
53 #include "output/chart-item-provider.h"
54 #include "output/driver-provider.h"
55 #include "output/message-item.h"
56 #include "output/options.h"
57 #include "output/render.h"
58 #include "output/table-item.h"
59 #include "output/text-item.h"
61 #include "gl/minmax.h"
62 #include "gl/xalloc.h"
66 #define _(msgid) gettext (msgid)
68 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
80 #define N_BOX (ASCII_N_LINES * ASCII_N_LINES \
81 * ASCII_N_LINES * ASCII_N_LINES)
83 static const ucs4_t ascii_box_chars[N_BOX] =
114 static const ucs4_t unicode_box_chars[N_BOX] =
116 0x0020, 0x2575, 0x2551,
117 0x2574, 0x256f, 0x255c,
118 0x2550, 0x255b, 0x255d,
119 0x2577, 0x2502, 0x2551,
120 0x256e, 0x2524, 0x2562,
121 0x2555, 0x2561, 0x2563,
122 0x2551, 0x2551, 0x2551,
123 0x2556, 0x2562, 0x2562,
124 0x2557, 0x2563, 0x2563,
125 0x2576, 0x2570, 0x2559,
126 0x2500, 0x2534, 0x2568,
127 0x2550, 0x2567, 0x2569,
128 0x256d, 0x251c, 0x255f,
129 0x252c, 0x253c, 0x256a,
130 0x2564, 0x256a, 0x256c,
131 0x2553, 0x255f, 0x255f,
132 0x2565, 0x256b, 0x256b,
133 0x2566, 0x256c, 0x256c,
134 0x2550, 0x2558, 0x255a,
135 0x2550, 0x2567, 0x2569,
136 0x2550, 0x2567, 0x2569,
137 0x2552, 0x255e, 0x2560,
138 0x2564, 0x256a, 0x256c,
139 0x2564, 0x256a, 0x256c,
140 0x2554, 0x2560, 0x2560,
141 0x2560, 0x256c, 0x256c,
142 0x2566, 0x256c, 0x256c,
146 ascii_line_from_render_line (int render_line)
150 case RENDER_LINE_NONE:
151 return ASCII_LINE_NONE;
153 case RENDER_LINE_SINGLE:
154 case RENDER_LINE_DASHED:
155 case RENDER_LINE_THICK:
156 case RENDER_LINE_THIN:
157 return ASCII_LINE_SINGLE;
159 case RENDER_LINE_DOUBLE:
160 return ASCII_LINE_DOUBLE;
163 return ASCII_LINE_NONE;
169 make_box_index (int left_, int right_, int top_, int bottom_)
171 bool rtl = render_direction_rtl ();
172 int left = ascii_line_from_render_line (rtl ? right_ : left_);
173 int right = ascii_line_from_render_line (rtl ? left_ : right_);
174 int top = ascii_line_from_render_line (top_);
175 int bottom = ascii_line_from_render_line (bottom_);
178 idx = idx * ASCII_N_LINES + bottom;
179 idx = idx * ASCII_N_LINES + left;
180 idx = idx * ASCII_N_LINES + top;
184 /* ASCII output driver. */
187 struct output_driver driver;
189 /* User parameters. */
190 bool append; /* Append if output file already exists? */
191 bool emphasis; /* Enable bold and underline in output? */
192 char *chart_file_name; /* Name of files used for charts. */
195 /* Colours for charts */
196 struct cell_color fg;
197 struct cell_color bg;
200 /* How the page width is determined: */
202 FIXED_WIDTH, /* Specified by configuration. */
203 VIEW_WIDTH, /* From SET WIDTH. */
204 TERMINAL_WIDTH /* From the terminal's width. */
206 int width; /* Page width. */
208 int min_hbreak; /* Min cell size to break across pages. */
210 const ucs4_t *box; /* Line & box drawing characters. */
212 /* Internal state. */
213 struct file_handle *handle;
214 FILE *file; /* Output file. */
215 bool error; /* Output error? */
216 struct u8_line *lines; /* Page content. */
217 int allocated_lines; /* Number of lines allocated. */
218 int chart_cnt; /* Number of charts so far. */
219 int object_cnt; /* Number of objects so far. */
220 struct render_params params;
223 static const struct output_driver_class ascii_driver_class;
225 static void ascii_submit (struct output_driver *, const struct output_item *);
227 static int get_terminal_width (void);
229 static bool update_page_size (struct ascii_driver *, bool issue_error);
230 static int parse_page_size (struct driver_option *);
232 static void ascii_draw_line (void *, int bb[TABLE_N_AXES][2],
233 enum render_line_style styles[TABLE_N_AXES][2],
234 struct cell_color colors[TABLE_N_AXES][2]);
235 static void ascii_measure_cell_width (void *, const struct table_cell *,
237 static int ascii_measure_cell_height (void *, const struct table_cell *,
239 static void ascii_draw_cell (void *, const struct table_cell *, int color_idx,
240 int bb[TABLE_N_AXES][2], int valign_offset,
241 int spill[TABLE_N_AXES][2],
242 int clip[TABLE_N_AXES][2]);
244 static struct ascii_driver *
245 ascii_driver_cast (struct output_driver *driver)
247 assert (driver->class == &ascii_driver_class);
248 return UP_CAST (driver, struct ascii_driver, driver);
251 static struct driver_option *
252 opt (struct output_driver *d, struct string_map *options, const char *key,
253 const char *default_value)
255 return driver_option_get (d, options, key, default_value);
258 /* Return true iff the terminal appears to be an xterm with
259 UTF-8 capabilities */
261 term_is_utf8_xterm (void)
263 const char *term = getenv ("TERM");
264 const char *xterm_locale = getenv ("XTERM_LOCAL");
265 return (term && xterm_locale
266 && !strcmp (term, "xterm")
267 && (strcasestr (xterm_locale, "utf8")
268 || strcasestr (xterm_locale, "utf-8")));
271 static struct output_driver *
272 ascii_create (struct file_handle *fh, enum settings_output_devices device_type,
273 struct string_map *o)
275 enum { BOX_ASCII, BOX_UNICODE } box;
276 struct output_driver *d;
277 struct ascii_driver *a;
279 a = xzalloc (sizeof *a);
281 output_driver_init (&a->driver, &ascii_driver_class, fh_get_file_name (fh), device_type);
282 a->append = parse_boolean (opt (d, o, "append", "false"));
283 a->emphasis = parse_boolean (opt (d, o, "emphasis", "false"));
285 a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", fh_get_file_name (fh)));
289 bool terminal = !strcmp (fh_get_file_name (fh), "-") && isatty (1);
290 a->width = parse_page_size (opt (d, o, "width", "-1"));
291 a->width_mode = (a->width > 0 ? FIXED_WIDTH
292 : terminal ? TERMINAL_WIDTH
294 a->min_hbreak = parse_int (opt (d, o, "min-hbreak", "-1"), -1, INT_MAX);
297 a->bg = parse_color (opt (d, o, "background-color", "#FFFFFFFFFFFF"));
298 a->fg = parse_color (opt (d, o, "foreground-color", "#000000000000"));
301 const char *default_box = (terminal && (!strcmp (locale_charset (), "UTF-8")
302 || term_is_utf8_xterm ())
303 ? "unicode" : "ascii");
304 box = parse_enum (opt (d, o, "box", default_box),
306 "unicode", BOX_UNICODE,
308 a->box = box == BOX_ASCII ? ascii_box_chars : unicode_box_chars;
313 a->allocated_lines = 0;
317 static const struct render_ops ascii_render_ops = {
318 .draw_line = ascii_draw_line,
319 .measure_cell_width = ascii_measure_cell_width,
320 .measure_cell_height = ascii_measure_cell_height,
321 .adjust_break = NULL,
322 .draw_cell = ascii_draw_cell,
324 a->params.ops = &ascii_render_ops;
326 a->params.size[H] = a->width;
327 a->params.size[V] = INT_MAX;
328 a->params.font_size[H] = 1;
329 a->params.font_size[V] = 1;
331 static const int ascii_line_widths[RENDER_N_LINES] = {
332 [RENDER_LINE_NONE] = 0,
333 [RENDER_LINE_SINGLE] = 1,
334 [RENDER_LINE_DASHED] = 1,
335 [RENDER_LINE_THICK] = 1,
336 [RENDER_LINE_THIN] = 1,
337 [RENDER_LINE_DOUBLE] = 1,
339 a->params.line_widths = ascii_line_widths;
340 a->params.supports_margins = false;
341 a->params.rtl = render_direction_rtl ();
343 if (!update_page_size (a, true))
346 a->file = fn_open (a->handle, a->append ? "a" : "w");
349 msg_error (errno, _("ascii: opening output file `%s'"),
350 fh_get_file_name (a->handle));
357 output_driver_destroy (d);
362 parse_page_size (struct driver_option *option)
364 int dim = atol (option->default_value);
366 if (option->value != NULL)
368 if (!strcmp (option->value, "auto"))
376 value = strtol (option->value, &tail, 0);
377 if (value >= 1 && errno != ERANGE && *tail == '\0')
380 msg (MW, _("%s: %s must be positive integer or `auto'"),
381 option->driver_name, option->name);
385 driver_option_destroy (option);
390 /* Re-calculates the page width based on settings, margins, and, if "auto" is
391 set, the size of the user's terminal window or GUI output window. */
393 update_page_size (struct ascii_driver *a, bool issue_error)
395 enum { MIN_WIDTH = 6 };
397 int want_width = (a->width_mode == VIEW_WIDTH ? settings_get_viewwidth ()
398 : a->width_mode == TERMINAL_WIDTH ? get_terminal_width ()
400 bool ok = want_width >= MIN_WIDTH;
401 if (!ok && issue_error)
402 msg (ME, _("ascii: page must be at least %d characters wide, but "
403 "as configured is only %d characters"),
404 MIN_WIDTH, want_width);
406 a->width = ok ? want_width : MIN_WIDTH;
407 a->params.size[H] = a->width;
408 a->params.min_break[H] = a->min_hbreak >= 0 ? a->min_hbreak : a->width / 2;
414 ascii_destroy (struct output_driver *driver)
416 struct ascii_driver *a = ascii_driver_cast (driver);
420 fn_close (a->handle, a->file);
421 fh_unref (a->handle);
422 free (a->chart_file_name);
423 for (i = 0; i < a->allocated_lines; i++)
424 u8_line_destroy (&a->lines[i]);
430 ascii_flush (struct output_driver *driver)
432 struct ascii_driver *a = ascii_driver_cast (driver);
438 ascii_output_lines (struct ascii_driver *a, size_t n_lines)
440 for (size_t y = 0; y < n_lines; y++)
442 if (y < a->allocated_lines)
444 struct u8_line *line = &a->lines[y];
446 while (ds_chomp_byte (&line->s, ' '))
448 fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
449 u8_line_clear (&a->lines[y]);
451 putc ('\n', a->file);
456 ascii_output_table_item (struct ascii_driver *a,
457 const struct table_item *table_item)
459 struct render_pager *p;
461 update_page_size (a, false);
464 putc ('\n', a->file);
466 p = render_pager_create (&a->params, table_item);
467 for (int i = 0; render_pager_has_next (p); i++)
470 putc ('\n', a->file);
471 ascii_output_lines (a, render_pager_draw_next (p, INT_MAX));
473 render_pager_destroy (p);
477 ascii_output_table_item_unref (struct ascii_driver *a,
478 struct table_item *table_item)
480 ascii_output_table_item (a, table_item);
481 table_item_unref (table_item);
485 ascii_output_text (struct ascii_driver *a, const char *text)
487 ascii_output_table_item_unref (
488 a, table_item_create (table_from_string (text), NULL, NULL, NULL));
492 ascii_submit (struct output_driver *driver,
493 const struct output_item *output_item)
495 struct ascii_driver *a = ascii_driver_cast (driver);
500 if (is_table_item (output_item))
501 ascii_output_table_item (a, to_table_item (output_item));
503 else if (is_chart_item (output_item) && a->chart_file_name != NULL)
505 struct chart_item *chart_item = to_chart_item (output_item);
508 file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
512 if (file_name != NULL)
514 struct text_item *text_item;
516 text_item = text_item_create_format (
517 TEXT_ITEM_LOG, _("See %s for a chart."), file_name);
519 ascii_submit (driver, &text_item->output_item);
520 text_item_unref (text_item);
524 #endif /* HAVE_CAIRO */
525 else if (is_text_item (output_item))
527 const struct text_item *text_item = to_text_item (output_item);
528 enum text_item_type type = text_item_get_type (text_item);
530 if (type != TEXT_ITEM_PAGE_TITLE)
531 ascii_output_table_item_unref (
532 a, text_item_to_table_item (text_item_ref (text_item)));
534 else if (is_message_item (output_item))
536 const struct message_item *message_item = to_message_item (output_item);
537 char *s = msg_to_string (message_item_get_msg (message_item));
538 ascii_output_text (a, s);
543 const struct output_driver_factory txt_driver_factory =
544 { "txt", "-", ascii_create };
545 const struct output_driver_factory list_driver_factory =
546 { "list", "-", ascii_create };
548 static const struct output_driver_class ascii_driver_class =
556 static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
558 static void ascii_layout_cell (struct ascii_driver *,
559 const struct table_cell *,
560 int bb[TABLE_N_AXES][2],
561 int clip[TABLE_N_AXES][2],
562 int *width, int *height);
565 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
566 enum render_line_style styles[TABLE_N_AXES][2],
567 struct cell_color colors[TABLE_N_AXES][2] UNUSED)
569 struct ascii_driver *a = a_;
576 /* Clip to the page. */
577 x0 = MAX (bb[H][0], 0);
578 y0 = MAX (bb[V][0], 0);
579 x1 = MIN (bb[H][1], a->width);
581 if (x1 <= 0 || y1 <= 0 || x0 >= a->width)
585 uc = a->box[make_box_index (styles[V][0], styles[V][1],
586 styles[H][0], styles[H][1])];
587 mblen = u8_uctomb (CHAR_CAST (uint8_t *, mbchar), uc, 6);
588 for (y = y0; y < y1; y++)
590 char *p = ascii_reserve (a, y, x0, x1, mblen * (x1 - x0));
591 for (x = x0; x < x1; x++)
593 memcpy (p, mbchar, mblen);
600 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
601 int *min_width, int *max_width)
603 struct ascii_driver *a = a_;
604 int bb[TABLE_N_AXES][2];
605 int clip[TABLE_N_AXES][2];
612 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
613 ascii_layout_cell (a, cell, bb, clip, max_width, &h);
615 if (cell->n_footnotes || strchr (cell->text, ' ')
616 || cell->n_subscripts || cell->superscript)
619 ascii_layout_cell (a, cell, bb, clip, min_width, &h);
622 *min_width = *max_width;
626 ascii_measure_cell_height (void *a_, const struct table_cell *cell, int width)
628 struct ascii_driver *a = a_;
629 int bb[TABLE_N_AXES][2];
630 int clip[TABLE_N_AXES][2];
637 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
638 ascii_layout_cell (a, cell, bb, clip, &w, &h);
643 ascii_draw_cell (void *a_, const struct table_cell *cell, int color_idx UNUSED,
644 int bb[TABLE_N_AXES][2], int valign_offset,
645 int spill[TABLE_N_AXES][2] UNUSED,
646 int clip[TABLE_N_AXES][2])
648 struct ascii_driver *a = a_;
651 bb[V][0] += valign_offset;
652 ascii_layout_cell (a, cell, bb, clip, &w, &h);
656 ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
658 if (y >= a->allocated_lines)
660 size_t new_alloc = MAX (25, a->allocated_lines);
661 while (new_alloc <= y)
662 new_alloc = xtimes (new_alloc, 2);
663 a->lines = xnrealloc (a->lines, new_alloc, sizeof *a->lines);
664 for (size_t i = a->allocated_lines; i < new_alloc; i++)
665 u8_line_init (&a->lines[i]);
666 a->allocated_lines = new_alloc;
668 return u8_line_reserve (&a->lines[y], x0, x1, n);
672 text_draw (struct ascii_driver *a, enum table_halign halign, int options,
673 bool bold, bool underline,
674 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
675 int y, const uint8_t *string, int n, size_t width)
677 int x0 = MAX (0, clip[H][0]);
678 int y0 = MAX (0, clip[V][0]);
679 int x1 = MIN (a->width, clip[H][1]);
683 if (y < y0 || y >= y1)
686 switch (table_halign_interpret (halign, options & TAB_NUMERIC))
688 case TABLE_HALIGN_LEFT:
691 case TABLE_HALIGN_CENTER:
692 x = (bb[H][0] + bb[H][1] - width + 1) / 2;
694 case TABLE_HALIGN_RIGHT:
695 case TABLE_HALIGN_DECIMAL:
696 x = bb[H][1] - width;
712 mblen = u8_mbtouc (&uc, string, n);
717 w = uc_width (uc, "UTF-8");
732 for (ofs = 0; ofs < n;)
738 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
740 w = uc_width (uc, "UTF-8");
743 if (width + w > x1 - x)
754 if (!a->emphasis || (!bold && !underline))
755 memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
763 /* First figure out how many bytes need to be inserted. */
765 for (ofs = 0; ofs < n; ofs += mblen)
770 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
771 w = uc_width (uc, "UTF-8");
782 /* Then insert them. */
783 out = ascii_reserve (a, y, x, x + width, n_out);
784 for (ofs = 0; ofs < n; ofs += mblen)
789 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
790 w = uc_width (uc, "UTF-8");
796 out = mempcpy (out, string + ofs, mblen);
805 out = mempcpy (out, string + ofs, mblen);
811 add_markers (const char *text, const struct table_cell *cell)
813 struct string s = DS_EMPTY_INITIALIZER;
814 ds_put_cstr (&s, text);
815 for (size_t i = 0; i < cell->n_subscripts; i++)
816 ds_put_format (&s, "%c%s", i ? ',' : '_', cell->subscripts[i]);
817 if (cell->superscript)
818 ds_put_format (&s, "^%s", cell->superscript);
819 for (size_t i = 0; i < cell->n_footnotes; i++)
820 ds_put_format (&s, "[%s]", cell->footnotes[i]->marker);
821 return ds_steal_cstr (&s);
825 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
826 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
827 int *widthp, int *heightp)
832 /* Get the basic textual contents. */
833 const char *plain_text = (cell->options & TAB_MARKUP
834 ? output_get_text_from_markup (cell->text)
837 /* Append footnotes, subscripts, superscript if any. */
839 if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
841 text = add_markers (plain_text, cell);
842 if (plain_text != cell->text)
843 free (CONST_CAST (char *, plain_text));
848 /* Calculate length; if it's zero, then there's nothing to do. */
849 size_t length = strlen (text);
852 if (text != cell->text)
853 free (CONST_CAST (char *, text));
857 char *breaks = xmalloc (length + 1);
858 u8_possible_linebreaks (CHAR_CAST (const uint8_t *, text), length,
860 breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
861 ? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
864 int bb_width = bb[H][1] - bb[H][0];
865 for (int y = bb[V][0]; y < bb[V][1] && pos < length; y++)
867 const uint8_t *line = CHAR_CAST (const uint8_t *, text + pos);
868 const char *b = breaks + pos;
869 size_t n = length - pos;
871 size_t last_break_ofs = 0;
872 int last_break_width = 0;
877 for (ofs = 0; ofs < n;)
883 mblen = u8_mbtouc (&uc, line + ofs, n - ofs);
884 if (b[ofs] == UC_BREAK_MANDATORY)
886 else if (b[ofs] == UC_BREAK_POSSIBLE)
888 last_break_ofs = ofs;
889 last_break_width = width;
892 w = uc_width (uc, "UTF-8");
895 if (width + w > bb_width)
897 if (isspace (line[ofs]))
899 else if (last_break_ofs != 0)
901 ofs = last_break_ofs;
902 width = last_break_width;
911 /* Trim any trailing spaces off the end of the text to be drawn. */
912 for (graph_ofs = ofs; graph_ofs > 0; graph_ofs--)
913 if (!isspace (line[graph_ofs - 1]))
915 width -= ofs - graph_ofs;
918 text_draw (a, cell->style->cell_style.halign, cell->options,
919 cell->style->font_style.bold,
920 cell->style->font_style.underline,
921 bb, clip, y, line, graph_ofs, width);
923 /* If a new-line ended the line, just skip the new-line. Otherwise, skip
924 past any spaces past the end of the line (but not past a new-line). */
925 if (b[ofs] == UC_BREAK_MANDATORY)
928 while (ofs < n && isspace (line[ofs]) && b[ofs] != UC_BREAK_MANDATORY)
938 if (text != cell->text)
939 free (CONST_CAST (char *, text));
943 ascii_test_write (struct output_driver *driver,
944 const char *s, int x, int y, bool bold, bool underline)
946 struct ascii_driver *a = ascii_driver_cast (driver);
947 int bb[TABLE_N_AXES][2];
953 struct table_area_style style = {
954 .cell_style.halign = TABLE_HALIGN_LEFT,
955 .font_style.bold = bold,
956 .font_style.underline = underline,
958 struct table_cell cell = {
959 .text = CONST_CAST (char *, s),
963 bb[TABLE_HORZ][0] = x;
964 bb[TABLE_HORZ][1] = a->width;
965 bb[TABLE_VERT][0] = y;
966 bb[TABLE_VERT][1] = INT_MAX;
968 ascii_layout_cell (a, &cell, bb, bb, &width, &height);
972 ascii_test_set_length (struct output_driver *driver, int y, int length)
974 struct ascii_driver *a = ascii_driver_cast (driver);
981 u8_line_set_length (&a->lines[y], length);
985 ascii_test_flush (struct output_driver *driver)
987 struct ascii_driver *a = ascii_driver_cast (driver);
989 for (size_t i = a->allocated_lines; i-- > 0;)
990 if (a->lines[i].width)
992 ascii_output_lines (a, i + 1);
997 static sig_atomic_t terminal_changed = true;
998 static int terminal_width;
1000 #if HAVE_DECL_SIGWINCH
1002 winch_handler (int signum UNUSED)
1004 terminal_changed = true;
1009 get_terminal_width (void)
1011 #if HAVE_DECL_SIGWINCH
1012 static bool setup_signal;
1015 setup_signal = true;
1017 struct sigaction action = { .sa_handler = winch_handler };
1018 sigemptyset (&action.sa_mask);
1019 sigaction (SIGWINCH, &action, NULL);
1023 if (terminal_changed)
1025 terminal_changed = false;
1027 #ifdef HAVE_TERMIOS_H
1029 if (!ioctl (0, TIOCGWINSZ, &ws))
1030 terminal_width = ws.ws_col;
1034 if (getenv ("COLUMNS"))
1035 terminal_width = atoi (getenv ("COLUMNS"));
1038 if (terminal_width <= 0 || terminal_width > 1024)
1039 terminal_width = 79;
1042 return terminal_width;