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"
50 #include "output/cairo.h"
51 #include "output/chart-item-provider.h"
52 #include "output/driver-provider.h"
53 #include "output/message-item.h"
54 #include "output/options.h"
55 #include "output/render.h"
56 #include "output/table-item.h"
57 #include "output/text-item.h"
59 #include "gl/minmax.h"
60 #include "gl/xalloc.h"
64 #define _(msgid) gettext (msgid)
66 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
78 #define N_BOX (ASCII_N_LINES * ASCII_N_LINES \
79 * ASCII_N_LINES * ASCII_N_LINES)
81 static const ucs4_t ascii_box_chars[N_BOX] =
112 static const ucs4_t unicode_box_chars[N_BOX] =
114 0x0020, 0x2575, 0x2551,
115 0x2574, 0x256f, 0x255c,
116 0x2550, 0x255b, 0x255d,
117 0x2577, 0x2502, 0x2551,
118 0x256e, 0x2524, 0x2562,
119 0x2555, 0x2561, 0x2563,
120 0x2551, 0x2551, 0x2551,
121 0x2556, 0x2562, 0x2562,
122 0x2557, 0x2563, 0x2563,
123 0x2576, 0x2570, 0x2559,
124 0x2500, 0x2534, 0x2568,
125 0x2550, 0x2567, 0x2569,
126 0x256d, 0x251c, 0x255f,
127 0x252c, 0x253c, 0x256a,
128 0x2564, 0x256a, 0x256c,
129 0x2553, 0x255f, 0x255f,
130 0x2565, 0x256b, 0x256b,
131 0x2566, 0x256c, 0x256c,
132 0x2550, 0x2558, 0x255a,
133 0x2550, 0x2567, 0x2569,
134 0x2550, 0x2567, 0x2569,
135 0x2552, 0x255e, 0x2560,
136 0x2564, 0x256a, 0x256c,
137 0x2564, 0x256a, 0x256c,
138 0x2554, 0x2560, 0x2560,
139 0x2560, 0x256c, 0x256c,
140 0x2566, 0x256c, 0x256c,
144 ascii_line_from_render_line (int render_line)
148 case RENDER_LINE_NONE:
149 return ASCII_LINE_NONE;
151 case RENDER_LINE_SINGLE:
152 case RENDER_LINE_DASHED:
153 case RENDER_LINE_THICK:
154 case RENDER_LINE_THIN:
155 return ASCII_LINE_SINGLE;
157 case RENDER_LINE_DOUBLE:
158 return ASCII_LINE_DOUBLE;
161 return ASCII_LINE_NONE;
167 make_box_index (int left_, int right_, int top_, int bottom_)
169 bool rtl = render_direction_rtl ();
170 int left = ascii_line_from_render_line (rtl ? right_ : left_);
171 int right = ascii_line_from_render_line (rtl ? left_ : right_);
172 int top = ascii_line_from_render_line (top_);
173 int bottom = ascii_line_from_render_line (bottom_);
176 idx = idx * ASCII_N_LINES + bottom;
177 idx = idx * ASCII_N_LINES + left;
178 idx = idx * ASCII_N_LINES + top;
182 /* ASCII output driver. */
185 struct output_driver driver;
187 /* User parameters. */
188 bool append; /* Append if output file already exists? */
189 bool emphasis; /* Enable bold and underline in output? */
190 char *chart_file_name; /* Name of files used for charts. */
193 /* Colours for charts */
194 struct cell_color fg;
195 struct cell_color bg;
198 /* How the page width is determined: */
200 FIXED_WIDTH, /* Specified by configuration. */
201 VIEW_WIDTH, /* From SET WIDTH. */
202 TERMINAL_WIDTH /* From the terminal's width. */
204 int width; /* Page width. */
206 int min_hbreak; /* Min cell size to break across pages. */
208 const ucs4_t *box; /* Line & box drawing characters. */
210 /* Internal state. */
211 struct file_handle *handle;
212 FILE *file; /* Output file. */
213 bool error; /* Output error? */
214 struct u8_line *lines; /* Page content. */
215 int allocated_lines; /* Number of lines allocated. */
216 int chart_cnt; /* Number of charts so far. */
217 int object_cnt; /* Number of objects so far. */
218 struct render_params params;
221 static const struct output_driver_class ascii_driver_class;
223 static void ascii_submit (struct output_driver *, const struct output_item *);
225 static int get_terminal_width (void);
227 static bool update_page_size (struct ascii_driver *, bool issue_error);
228 static int parse_page_size (struct driver_option *);
230 static void ascii_draw_line (void *, int bb[TABLE_N_AXES][2],
231 enum render_line_style styles[TABLE_N_AXES][2],
232 struct cell_color colors[TABLE_N_AXES][2]);
233 static void ascii_measure_cell_width (void *, const struct table_cell *,
235 static int ascii_measure_cell_height (void *, const struct table_cell *,
237 static void ascii_draw_cell (void *, const struct table_cell *, int color_idx,
238 int bb[TABLE_N_AXES][2], int valign_offset,
239 int spill[TABLE_N_AXES][2],
240 int clip[TABLE_N_AXES][2]);
242 static struct ascii_driver *
243 ascii_driver_cast (struct output_driver *driver)
245 assert (driver->class == &ascii_driver_class);
246 return UP_CAST (driver, struct ascii_driver, driver);
249 static struct driver_option *
250 opt (struct output_driver *d, struct string_map *options, const char *key,
251 const char *default_value)
253 return driver_option_get (d, options, key, default_value);
256 /* Return true iff the terminal appears to be an xterm with
257 UTF-8 capabilities */
259 term_is_utf8_xterm (void)
261 const char *term = getenv ("TERM");
262 const char *xterm_locale = getenv ("XTERM_LOCAL");
263 return (term && xterm_locale
264 && !strcmp (term, "xterm")
265 && (strcasestr (xterm_locale, "utf8")
266 || strcasestr (xterm_locale, "utf-8")));
269 static struct output_driver *
270 ascii_create (struct file_handle *fh, enum settings_output_devices device_type,
271 struct string_map *o)
273 enum { BOX_ASCII, BOX_UNICODE } box;
274 struct output_driver *d;
275 struct ascii_driver *a;
277 a = xzalloc (sizeof *a);
279 output_driver_init (&a->driver, &ascii_driver_class, fh_get_file_name (fh), device_type);
280 a->append = parse_boolean (opt (d, o, "append", "false"));
281 a->emphasis = parse_boolean (opt (d, o, "emphasis", "false"));
283 a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", fh_get_file_name (fh)));
287 bool terminal = !strcmp (fh_get_file_name (fh), "-") && isatty (1);
288 a->width = parse_page_size (opt (d, o, "width", "-1"));
289 a->width_mode = (a->width > 0 ? FIXED_WIDTH
290 : terminal ? TERMINAL_WIDTH
292 a->min_hbreak = parse_int (opt (d, o, "min-hbreak", "-1"), -1, INT_MAX);
295 a->bg = parse_color (opt (d, o, "background-color", "#FFFFFFFFFFFF"));
296 a->fg = parse_color (opt (d, o, "foreground-color", "#000000000000"));
299 const char *default_box = (terminal && (!strcmp (locale_charset (), "UTF-8")
300 || term_is_utf8_xterm ())
301 ? "unicode" : "ascii");
302 box = parse_enum (opt (d, o, "box", default_box),
304 "unicode", BOX_UNICODE,
306 a->box = box == BOX_ASCII ? ascii_box_chars : unicode_box_chars;
311 a->allocated_lines = 0;
315 static const struct render_ops ascii_render_ops = {
316 .draw_line = ascii_draw_line,
317 .measure_cell_width = ascii_measure_cell_width,
318 .measure_cell_height = ascii_measure_cell_height,
319 .adjust_break = NULL,
320 .draw_cell = ascii_draw_cell,
322 a->params.ops = &ascii_render_ops;
324 a->params.size[H] = a->width;
325 a->params.size[V] = INT_MAX;
326 a->params.font_size[H] = 1;
327 a->params.font_size[V] = 1;
329 static const int ascii_line_widths[RENDER_N_LINES] = {
330 [RENDER_LINE_NONE] = 0,
331 [RENDER_LINE_SINGLE] = 1,
332 [RENDER_LINE_DASHED] = 1,
333 [RENDER_LINE_THICK] = 1,
334 [RENDER_LINE_THIN] = 1,
335 [RENDER_LINE_DOUBLE] = 1,
337 a->params.line_widths = ascii_line_widths;
338 a->params.supports_margins = false;
339 a->params.rtl = render_direction_rtl ();
341 if (!update_page_size (a, true))
344 a->file = fn_open (a->handle, a->append ? "a" : "w");
347 msg_error (errno, _("ascii: opening output file `%s'"),
348 fh_get_file_name (a->handle));
355 output_driver_destroy (d);
360 parse_page_size (struct driver_option *option)
362 int dim = atol (option->default_value);
364 if (option->value != NULL)
366 if (!strcmp (option->value, "auto"))
374 value = strtol (option->value, &tail, 0);
375 if (value >= 1 && errno != ERANGE && *tail == '\0')
378 msg (MW, _("%s: %s must be positive integer or `auto'"),
379 option->driver_name, option->name);
383 driver_option_destroy (option);
388 /* Re-calculates the page width based on settings, margins, and, if "auto" is
389 set, the size of the user's terminal window or GUI output window. */
391 update_page_size (struct ascii_driver *a, bool issue_error)
393 enum { MIN_WIDTH = 6 };
395 int want_width = (a->width_mode == VIEW_WIDTH ? settings_get_viewwidth ()
396 : a->width_mode == TERMINAL_WIDTH ? get_terminal_width ()
398 bool ok = want_width >= MIN_WIDTH;
399 if (!ok && issue_error)
400 msg (ME, _("ascii: page must be at least %d characters wide, but "
401 "as configured is only %d characters"),
402 MIN_WIDTH, want_width);
404 a->width = ok ? want_width : MIN_WIDTH;
405 a->params.size[H] = a->width;
406 a->params.min_break[H] = a->min_hbreak >= 0 ? a->min_hbreak : a->width / 2;
412 ascii_destroy (struct output_driver *driver)
414 struct ascii_driver *a = ascii_driver_cast (driver);
418 fn_close (a->handle, a->file);
419 fh_unref (a->handle);
420 free (a->chart_file_name);
421 for (i = 0; i < a->allocated_lines; i++)
422 u8_line_destroy (&a->lines[i]);
428 ascii_flush (struct output_driver *driver)
430 struct ascii_driver *a = ascii_driver_cast (driver);
436 ascii_output_lines (struct ascii_driver *a, size_t n_lines)
438 for (size_t y = 0; y < n_lines; y++)
440 if (y < a->allocated_lines)
442 struct u8_line *line = &a->lines[y];
444 while (ds_chomp_byte (&line->s, ' '))
446 fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
447 u8_line_clear (&a->lines[y]);
449 putc ('\n', a->file);
454 ascii_output_table_item (struct ascii_driver *a,
455 const struct table_item *table_item)
457 struct render_pager *p;
459 update_page_size (a, false);
462 putc ('\n', a->file);
464 p = render_pager_create (&a->params, table_item);
465 for (int i = 0; render_pager_has_next (p); i++)
468 putc ('\n', a->file);
469 ascii_output_lines (a, render_pager_draw_next (p, INT_MAX));
471 render_pager_destroy (p);
475 ascii_output_table_item_unref (struct ascii_driver *a,
476 struct table_item *table_item)
478 ascii_output_table_item (a, table_item);
479 table_item_unref (table_item);
483 ascii_output_text (struct ascii_driver *a, const char *text)
485 ascii_output_table_item_unref (
486 a, table_item_create (table_from_string (text), NULL, NULL));
490 ascii_submit (struct output_driver *driver,
491 const struct output_item *output_item)
493 struct ascii_driver *a = ascii_driver_cast (driver);
498 if (is_table_item (output_item))
499 ascii_output_table_item (a, to_table_item (output_item));
501 else if (is_chart_item (output_item) && a->chart_file_name != NULL)
503 struct chart_item *chart_item = to_chart_item (output_item);
506 file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
510 if (file_name != NULL)
512 struct text_item *text_item;
514 text_item = text_item_create_format (
515 TEXT_ITEM_LOG, _("See %s for a chart."), file_name);
517 ascii_submit (driver, &text_item->output_item);
518 text_item_unref (text_item);
522 #endif /* HAVE_CAIRO */
523 else if (is_text_item (output_item))
525 const struct text_item *text_item = to_text_item (output_item);
526 enum text_item_type type = text_item_get_type (text_item);
528 if (type != TEXT_ITEM_PAGE_TITLE)
529 ascii_output_table_item_unref (
530 a, text_item_to_table_item (text_item_ref (text_item)));
532 else if (is_message_item (output_item))
534 const struct message_item *message_item = to_message_item (output_item);
535 char *s = msg_to_string (message_item_get_msg (message_item));
536 ascii_output_text (a, s);
541 const struct output_driver_factory txt_driver_factory =
542 { "txt", "-", ascii_create };
543 const struct output_driver_factory list_driver_factory =
544 { "list", "-", ascii_create };
546 static const struct output_driver_class ascii_driver_class =
554 static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
556 static void ascii_layout_cell (struct ascii_driver *,
557 const struct table_cell *,
558 int bb[TABLE_N_AXES][2],
559 int clip[TABLE_N_AXES][2],
560 int *width, int *height);
563 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
564 enum render_line_style styles[TABLE_N_AXES][2],
565 struct cell_color colors[TABLE_N_AXES][2] UNUSED)
567 struct ascii_driver *a = a_;
574 /* Clip to the page. */
575 x0 = MAX (bb[H][0], 0);
576 y0 = MAX (bb[V][0], 0);
577 x1 = MIN (bb[H][1], a->width);
579 if (x1 <= 0 || y1 <= 0 || x0 >= a->width)
583 uc = a->box[make_box_index (styles[V][0], styles[V][1],
584 styles[H][0], styles[H][1])];
585 mblen = u8_uctomb (CHAR_CAST (uint8_t *, mbchar), uc, 6);
586 for (y = y0; y < y1; y++)
588 char *p = ascii_reserve (a, y, x0, x1, mblen * (x1 - x0));
589 for (x = x0; x < x1; x++)
591 memcpy (p, mbchar, mblen);
598 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
599 int *min_width, int *max_width)
601 struct ascii_driver *a = a_;
602 int bb[TABLE_N_AXES][2];
603 int clip[TABLE_N_AXES][2];
610 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
611 ascii_layout_cell (a, cell, bb, clip, max_width, &h);
613 if (cell->n_footnotes || strchr (cell->text, ' ')
614 || cell->n_subscripts || cell->superscript)
617 ascii_layout_cell (a, cell, bb, clip, min_width, &h);
620 *min_width = *max_width;
624 ascii_measure_cell_height (void *a_, const struct table_cell *cell, int width)
626 struct ascii_driver *a = a_;
627 int bb[TABLE_N_AXES][2];
628 int clip[TABLE_N_AXES][2];
635 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
636 ascii_layout_cell (a, cell, bb, clip, &w, &h);
641 ascii_draw_cell (void *a_, const struct table_cell *cell, int color_idx UNUSED,
642 int bb[TABLE_N_AXES][2], int valign_offset,
643 int spill[TABLE_N_AXES][2] UNUSED,
644 int clip[TABLE_N_AXES][2])
646 struct ascii_driver *a = a_;
649 bb[V][0] += valign_offset;
650 ascii_layout_cell (a, cell, bb, clip, &w, &h);
654 ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
656 if (y >= a->allocated_lines)
658 size_t new_alloc = MAX (25, a->allocated_lines);
659 while (new_alloc <= y)
660 new_alloc = xtimes (new_alloc, 2);
661 a->lines = xnrealloc (a->lines, new_alloc, sizeof *a->lines);
662 for (size_t i = a->allocated_lines; i < new_alloc; i++)
663 u8_line_init (&a->lines[i]);
664 a->allocated_lines = new_alloc;
666 return u8_line_reserve (&a->lines[y], x0, x1, n);
670 text_draw (struct ascii_driver *a, enum table_halign halign, int options,
671 bool bold, bool underline,
672 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
673 int y, const uint8_t *string, int n, size_t width)
675 int x0 = MAX (0, clip[H][0]);
676 int y0 = MAX (0, clip[V][0]);
677 int x1 = MIN (a->width, clip[H][1]);
681 if (y < y0 || y >= y1)
684 switch (table_halign_interpret (halign, options & TAB_NUMERIC))
686 case TABLE_HALIGN_LEFT:
689 case TABLE_HALIGN_CENTER:
690 x = (bb[H][0] + bb[H][1] - width + 1) / 2;
692 case TABLE_HALIGN_RIGHT:
693 case TABLE_HALIGN_DECIMAL:
694 x = bb[H][1] - width;
710 mblen = u8_mbtouc (&uc, string, n);
715 w = uc_width (uc, "UTF-8");
730 for (ofs = 0; ofs < n;)
736 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
738 w = uc_width (uc, "UTF-8");
741 if (width + w > x1 - x)
752 if (!a->emphasis || (!bold && !underline))
753 memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
761 /* First figure out how many bytes need to be inserted. */
763 for (ofs = 0; ofs < n; ofs += mblen)
768 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
769 w = uc_width (uc, "UTF-8");
780 /* Then insert them. */
781 out = ascii_reserve (a, y, x, x + width, n_out);
782 for (ofs = 0; ofs < n; ofs += mblen)
787 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
788 w = uc_width (uc, "UTF-8");
794 out = mempcpy (out, string + ofs, mblen);
803 out = mempcpy (out, string + ofs, mblen);
809 add_markers (const char *text, const struct table_cell *cell)
811 struct string s = DS_EMPTY_INITIALIZER;
812 ds_put_cstr (&s, text);
813 for (size_t i = 0; i < cell->n_subscripts; i++)
814 ds_put_format (&s, "%c%s", i ? ',' : '_', cell->subscripts[i]);
815 if (cell->superscript)
816 ds_put_format (&s, "^%s", cell->superscript);
817 for (size_t i = 0; i < cell->n_footnotes; i++)
818 ds_put_format (&s, "[%s]", cell->footnotes[i]->marker);
819 return ds_steal_cstr (&s);
823 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
824 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
825 int *widthp, int *heightp)
830 /* Get the basic textual contents. */
831 const char *plain_text = (cell->options & TAB_MARKUP
832 ? output_get_text_from_markup (cell->text)
835 /* Append footnotes, subscripts, superscript if any. */
837 if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
839 text = add_markers (plain_text, cell);
840 if (plain_text != cell->text)
841 free (CONST_CAST (char *, plain_text));
846 /* Calculate length; if it's zero, then there's nothing to do. */
847 size_t length = strlen (text);
850 if (text != cell->text)
851 free (CONST_CAST (char *, text));
855 char *breaks = xmalloc (length + 1);
856 u8_possible_linebreaks (CHAR_CAST (const uint8_t *, text), length,
858 breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
859 ? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
862 int bb_width = bb[H][1] - bb[H][0];
863 for (int y = bb[V][0]; y < bb[V][1] && pos < length; y++)
865 const uint8_t *line = CHAR_CAST (const uint8_t *, text + pos);
866 const char *b = breaks + pos;
867 size_t n = length - pos;
869 size_t last_break_ofs = 0;
870 int last_break_width = 0;
875 for (ofs = 0; ofs < n;)
881 mblen = u8_mbtouc (&uc, line + ofs, n - ofs);
882 if (b[ofs] == UC_BREAK_MANDATORY)
884 else if (b[ofs] == UC_BREAK_POSSIBLE)
886 last_break_ofs = ofs;
887 last_break_width = width;
890 w = uc_width (uc, "UTF-8");
893 if (width + w > bb_width)
895 if (isspace (line[ofs]))
897 else if (last_break_ofs != 0)
899 ofs = last_break_ofs;
900 width = last_break_width;
909 /* Trim any trailing spaces off the end of the text to be drawn. */
910 for (graph_ofs = ofs; graph_ofs > 0; graph_ofs--)
911 if (!isspace (line[graph_ofs - 1]))
913 width -= ofs - graph_ofs;
916 text_draw (a, cell->style->cell_style.halign, cell->options,
917 cell->style->font_style.bold,
918 cell->style->font_style.underline,
919 bb, clip, y, line, graph_ofs, width);
921 /* If a new-line ended the line, just skip the new-line. Otherwise, skip
922 past any spaces past the end of the line (but not past a new-line). */
923 if (b[ofs] == UC_BREAK_MANDATORY)
926 while (ofs < n && isspace (line[ofs]) && b[ofs] != UC_BREAK_MANDATORY)
936 if (text != cell->text)
937 free (CONST_CAST (char *, text));
941 ascii_test_write (struct output_driver *driver,
942 const char *s, int x, int y, bool bold, bool underline)
944 struct ascii_driver *a = ascii_driver_cast (driver);
945 int bb[TABLE_N_AXES][2];
951 struct table_area_style style = {
952 .cell_style.halign = TABLE_HALIGN_LEFT,
953 .font_style.bold = bold,
954 .font_style.underline = underline,
956 struct table_cell cell = {
957 .text = CONST_CAST (char *, s),
961 bb[TABLE_HORZ][0] = x;
962 bb[TABLE_HORZ][1] = a->width;
963 bb[TABLE_VERT][0] = y;
964 bb[TABLE_VERT][1] = INT_MAX;
966 ascii_layout_cell (a, &cell, bb, bb, &width, &height);
970 ascii_test_set_length (struct output_driver *driver, int y, int length)
972 struct ascii_driver *a = ascii_driver_cast (driver);
979 u8_line_set_length (&a->lines[y], length);
983 ascii_test_flush (struct output_driver *driver)
985 struct ascii_driver *a = ascii_driver_cast (driver);
987 for (size_t i = a->allocated_lines; i-- > 0;)
988 if (a->lines[i].width)
990 ascii_output_lines (a, i + 1);
995 static sig_atomic_t terminal_changed = true;
996 static int terminal_width;
998 #if HAVE_DECL_SIGWINCH
1000 winch_handler (int signum UNUSED)
1002 terminal_changed = true;
1007 get_terminal_width (void)
1009 #if HAVE_DECL_SIGWINCH
1010 static bool setup_signal;
1013 setup_signal = true;
1015 struct sigaction action = { .sa_handler = winch_handler };
1016 sigemptyset (&action.sa_mask);
1017 sigaction (SIGWINCH, &action, NULL);
1021 if (terminal_changed)
1023 terminal_changed = false;
1025 #ifdef HAVE_TERMIOS_H
1027 if (!ioctl (0, TIOCGWINSZ, &ws))
1028 terminal_width = ws.ws_col;
1032 if (getenv ("COLUMNS"))
1033 terminal_width = atoi (getenv ("COLUMNS"));
1036 if (terminal_width <= 0 || terminal_width > 1024)
1037 terminal_width = 79;
1040 return terminal_width;