ncurses 6.0 - patch 20160806
[ncurses.git] / progs / reset_cmd.c
1 /****************************************************************************
2  * Copyright (c) 2016 Free Software Foundation, Inc.                        *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28
29 /****************************************************************************
30  *  Author: Thomas E. Dickey                                                *
31  ****************************************************************************/
32
33 #include <reset_cmd.h>
34
35 #include <errno.h>
36 #include <stdio.h>
37 #include <fcntl.h>
38
39 #if HAVE_SIZECHANGE
40 # if !defined(sun) || !TERMIOS
41 #  if HAVE_SYS_IOCTL_H
42 #   include <sys/ioctl.h>
43 #  endif
44 # endif
45 #endif
46
47 #if NEED_PTEM_H
48 /* they neglected to define struct winsize in termios.h -- it's only
49    in termio.h  */
50 #include <sys/stream.h>
51 #include <sys/ptem.h>
52 #endif
53
54 MODULE_ID("$Id: reset_cmd.c,v 1.5 2016/08/06 21:04:54 tom Exp $")
55
56 /*
57  * SCO defines TIOCGSIZE and the corresponding struct.  Other systems (SunOS,
58  * Solaris, IRIX) define TIOCGWINSZ and struct winsize.
59  */
60 #ifdef TIOCGSIZE
61 # define IOCTL_GET_WINSIZE TIOCGSIZE
62 # define IOCTL_SET_WINSIZE TIOCSSIZE
63 # define STRUCT_WINSIZE struct ttysize
64 # define WINSIZE_ROWS(n) n.ts_lines
65 # define WINSIZE_COLS(n) n.ts_cols
66 #else
67 # ifdef TIOCGWINSZ
68 #  define IOCTL_GET_WINSIZE TIOCGWINSZ
69 #  define IOCTL_SET_WINSIZE TIOCSWINSZ
70 #  define STRUCT_WINSIZE struct winsize
71 #  define WINSIZE_ROWS(n) n.ws_row
72 #  define WINSIZE_COLS(n) n.ws_col
73 # endif
74 #endif
75
76 static int my_fd;
77 static FILE *my_file;
78 static TTY original_settings;
79
80 static bool can_restore = FALSE;
81 static bool use_reset = FALSE;  /* invoked as reset */
82 static bool use_init = FALSE;   /* invoked as init */
83
84 static void
85 exit_error(void)
86 {
87     restore_tty_settings();
88     (void) fprintf(my_file, "\n");
89     fflush(my_file);
90     ExitProgram(EXIT_FAILURE);
91     /* NOTREACHED */
92 }
93
94 static void
95 failed(const char *msg)
96 {
97     char temp[BUFSIZ];
98
99     _nc_STRCPY(temp, _nc_progname, sizeof(temp));
100     _nc_STRCAT(temp, ": ", sizeof(temp));
101     perror(strncat(temp, msg, sizeof(temp) - strlen(temp) - 2));
102     exit_error();
103     /* NOTREACHED */
104 }
105
106 static bool
107 get_tty_settings(int fd, TTY * tty_settings)
108 {
109     bool success = TRUE;
110     my_fd = fd;
111     if (fd < 0 || GET_TTY(my_fd, tty_settings) < 0) {
112         success = FALSE;
113     }
114     return success;
115 }
116
117 static bool
118 cat_file(char *file)
119 {
120     FILE *fp;
121     size_t nr;
122     char buf[BUFSIZ];
123     bool sent = FALSE;
124
125     if (file != 0) {
126         if ((fp = fopen(file, "r")) == 0)
127             failed(file);
128
129         while ((nr = fread(buf, sizeof(char), sizeof(buf), fp)) != 0) {
130             if (fwrite(buf, sizeof(char), nr, my_file) != nr) {
131                 failed(file);
132             }
133             sent = TRUE;
134         }
135         fclose(fp);
136     }
137     return sent;
138 }
139
140 static int
141 out_char(int c)
142 {
143     return putc(c, my_file);
144 }
145
146 /**************************************************************************
147  * Mode-setting logic
148  **************************************************************************/
149
150 /* some BSD systems have these built in, some systems are missing
151  * one or more definitions. The safest solution is to override unless the
152  * commonly-altered ones are defined.
153  */
154 #if !(defined(CERASE) && defined(CINTR) && defined(CKILL) && defined(CQUIT))
155 #undef CEOF
156 #undef CERASE
157 #undef CINTR
158 #undef CKILL
159 #undef CLNEXT
160 #undef CRPRNT
161 #undef CQUIT
162 #undef CSTART
163 #undef CSTOP
164 #undef CSUSP
165 #endif
166
167 /* control-character defaults */
168 #ifndef CEOF
169 #define CEOF    CTRL('D')
170 #endif
171 #ifndef CERASE
172 #define CERASE  CTRL('H')
173 #endif
174 #ifndef CINTR
175 #define CINTR   127             /* ^? */
176 #endif
177 #ifndef CKILL
178 #define CKILL   CTRL('U')
179 #endif
180 #ifndef CLNEXT
181 #define CLNEXT  CTRL('v')
182 #endif
183 #ifndef CRPRNT
184 #define CRPRNT  CTRL('r')
185 #endif
186 #ifndef CQUIT
187 #define CQUIT   CTRL('\\')
188 #endif
189 #ifndef CSTART
190 #define CSTART  CTRL('Q')
191 #endif
192 #ifndef CSTOP
193 #define CSTOP   CTRL('S')
194 #endif
195 #ifndef CSUSP
196 #define CSUSP   CTRL('Z')
197 #endif
198
199 #if defined(_POSIX_VDISABLE)
200 #define DISABLED(val)   (((_POSIX_VDISABLE != -1) \
201                        && ((val) == _POSIX_VDISABLE)) \
202                       || ((val) <= 0))
203 #else
204 #define DISABLED(val)   ((int)(val) <= 0)
205 #endif
206
207 #define CHK(val, dft)   (unsigned char) (DISABLED(val) ? dft : val)
208
209 #define reset_char(item, value) \
210     tty_settings->c_cc[item] = CHK(tty_settings->c_cc[item], value)
211
212 /*
213  * Reset the terminal mode bits to a sensible state.  Very useful after
214  * a child program dies in raw mode.
215  */
216 void
217 reset_tty_settings(TTY * tty_settings)
218 {
219 #ifdef TERMIOS
220     tcgetattr(my_fd, tty_settings);
221 #else
222     stty(my_fd, tty_settings);
223 #endif
224
225 #ifdef TERMIOS
226 #if defined(VDISCARD) && defined(CDISCARD)
227     reset_char(VDISCARD, CDISCARD);
228 #endif
229     reset_char(VEOF, CEOF);
230     reset_char(VERASE, CERASE);
231 #if defined(VFLUSH) && defined(CFLUSH)
232     reset_char(VFLUSH, CFLUSH);
233 #endif
234     reset_char(VINTR, CINTR);
235     reset_char(VKILL, CKILL);
236 #if defined(VLNEXT) && defined(CLNEXT)
237     reset_char(VLNEXT, CLNEXT);
238 #endif
239     reset_char(VQUIT, CQUIT);
240 #if defined(VREPRINT) && defined(CRPRNT)
241     reset_char(VREPRINT, CRPRNT);
242 #endif
243 #if defined(VSTART) && defined(CSTART)
244     reset_char(VSTART, CSTART);
245 #endif
246 #if defined(VSTOP) && defined(CSTOP)
247     reset_char(VSTOP, CSTOP);
248 #endif
249 #if defined(VSUSP) && defined(CSUSP)
250     reset_char(VSUSP, CSUSP);
251 #endif
252 #if defined(VWERASE) && defined(CWERASE)
253     reset_char(VWERASE, CWERASE);
254 #endif
255
256     tty_settings->c_iflag &= ~((unsigned) (IGNBRK
257                                            | PARMRK
258                                            | INPCK
259                                            | ISTRIP
260                                            | INLCR
261                                            | IGNCR
262 #ifdef IUCLC
263                                            | IUCLC
264 #endif
265 #ifdef IXANY
266                                            | IXANY
267 #endif
268                                            | IXOFF));
269
270     tty_settings->c_iflag |= (BRKINT
271                               | IGNPAR
272                               | ICRNL
273                               | IXON
274 #ifdef IMAXBEL
275                               | IMAXBEL
276 #endif
277         );
278
279     tty_settings->c_oflag &= ~((unsigned) (0
280 #ifdef OLCUC
281                                            | OLCUC
282 #endif
283 #ifdef OCRNL
284                                            | OCRNL
285 #endif
286 #ifdef ONOCR
287                                            | ONOCR
288 #endif
289 #ifdef ONLRET
290                                            | ONLRET
291 #endif
292 #ifdef OFILL
293                                            | OFILL
294 #endif
295 #ifdef OFDEL
296                                            | OFDEL
297 #endif
298 #ifdef NLDLY
299                                            | NLDLY
300 #endif
301 #ifdef CRDLY
302                                            | CRDLY
303 #endif
304 #ifdef TABDLY
305                                            | TABDLY
306 #endif
307 #ifdef BSDLY
308                                            | BSDLY
309 #endif
310 #ifdef VTDLY
311                                            | VTDLY
312 #endif
313 #ifdef FFDLY
314                                            | FFDLY
315 #endif
316                                ));
317
318     tty_settings->c_oflag |= (OPOST
319 #ifdef ONLCR
320                               | ONLCR
321 #endif
322         );
323
324     tty_settings->c_cflag &= ~((unsigned) (CSIZE
325                                            | CSTOPB
326                                            | PARENB
327                                            | PARODD
328                                            | CLOCAL));
329     tty_settings->c_cflag |= (CS8 | CREAD);
330     tty_settings->c_lflag &= ~((unsigned) (ECHONL
331                                            | NOFLSH
332 #ifdef TOSTOP
333                                            | TOSTOP
334 #endif
335 #ifdef ECHOPTR
336                                            | ECHOPRT
337 #endif
338 #ifdef XCASE
339                                            | XCASE
340 #endif
341                                ));
342
343     tty_settings->c_lflag |= (ISIG
344                               | ICANON
345                               | ECHO
346                               | ECHOE
347                               | ECHOK
348 #ifdef ECHOCTL
349                               | ECHOCTL
350 #endif
351 #ifdef ECHOKE
352                               | ECHOKE
353 #endif
354         );
355 #endif
356
357     SET_TTY(my_fd, tty_settings);
358 }
359
360 /*
361  * Returns a "good" value for the erase character.  This is loosely based on
362  * the BSD4.4 logic.
363  */
364 static int
365 default_erase(void)
366 {
367     int result;
368
369     if (over_strike
370         && key_backspace != 0
371         && strlen(key_backspace) == 1) {
372         result = key_backspace[0];
373     } else {
374         result = CERASE;
375     }
376
377     return result;
378 }
379
380 /*
381  * Update the values of the erase, interrupt, and kill characters in the TTY
382  * parameter.
383  *
384  * SVr4 tset (e.g., Solaris 2.5) only modifies the intr, quit or erase
385  * characters if they're unset, or if we specify them as options.  This differs
386  * from BSD 4.4 tset, which always sets erase.
387  */
388 void
389 set_control_chars(TTY * tty_settings, int my_erase, int my_intr, int my_kill)
390 {
391     if (DISABLED(tty_settings->c_cc[VERASE]) || my_erase >= 0) {
392         tty_settings->c_cc[VERASE] = UChar((my_erase >= 0)
393                                            ? my_erase
394                                            : default_erase());
395     }
396
397     if (DISABLED(tty_settings->c_cc[VINTR]) || my_intr >= 0) {
398         tty_settings->c_cc[VINTR] = UChar((my_intr >= 0)
399                                           ? my_intr
400                                           : CINTR);
401     }
402
403     if (DISABLED(tty_settings->c_cc[VKILL]) || my_kill >= 0) {
404         tty_settings->c_cc[VKILL] = UChar((my_kill >= 0)
405                                           ? my_kill
406                                           : CKILL);
407     }
408 }
409
410 /*
411  * Set up various conversions in the TTY parameter, including parity, tabs,
412  * returns, echo, and case, according to the termcap entry.
413  */
414 void
415 set_conversions(TTY * tty_settings)
416 {
417 #ifdef ONLCR
418     tty_settings->c_oflag |= ONLCR;
419 #endif
420     tty_settings->c_iflag |= ICRNL;
421     tty_settings->c_lflag |= ECHO;
422 #ifdef OXTABS
423     tty_settings->c_oflag |= OXTABS;
424 #endif /* OXTABS */
425
426     /* test used to be tgetflag("NL") */
427     if (newline != (char *) 0 && newline[0] == '\n' && !newline[1]) {
428         /* Newline, not linefeed. */
429 #ifdef ONLCR
430         tty_settings->c_oflag &= ~((unsigned) ONLCR);
431 #endif
432         tty_settings->c_iflag &= ~((unsigned) ICRNL);
433     }
434 #ifdef OXTABS
435     /* test used to be tgetflag("pt") */
436     if (has_hardware_tabs)      /* Print tabs. */
437         tty_settings->c_oflag &= ~OXTABS;
438 #endif /* OXTABS */
439     tty_settings->c_lflag |= (ECHOE | ECHOK);
440 }
441
442 /*
443  * Set the hardware tabs on the terminal, using the 'ct' (clear all tabs),
444  * 'st' (set one tab) and 'ch' (horizontal cursor addressing) capabilities.
445  * This is done before 'if' and 'is', so they can recover in case of error.
446  *
447  * Return TRUE if we set any tab stops, FALSE if not.
448  */
449 static bool
450 reset_tabstops(int wide)
451 {
452     if ((init_tabs != 8) && (set_tab && clear_all_tabs)) {
453         int c;
454
455         (void) putc('\r', my_file);     /* Force to left margin. */
456         tputs(clear_all_tabs, 0, out_char);
457
458         for (c = 8; c < wide; c += 8) {
459             /* Get to the right column.  In BSD tset, this used to try a bunch
460              * of half-clever things with cup and hpa, for an average saving of
461              * somewhat less than two character times per tab stop, less than
462              * .01 sec at 2400cps.  We lost all this cruft because it seemed to
463              * be introducing some odd bugs.
464              * -----------12345678----------- */
465             (void) fputs("        ", my_file);
466             tputs(set_tab, 0, out_char);
467         }
468         putc('\r', my_file);
469         return (TRUE);
470     }
471     return (FALSE);
472 }
473
474 static bool
475 sent_string(const char *s)
476 {
477     bool sent = FALSE;
478     if (s != 0) {
479         tputs(s, 0, out_char);
480         sent = TRUE;
481     }
482     return sent;
483 }
484
485 #define PUTCHAR(c) fputc(c, my_file)
486
487 /* Output startup string. */
488 bool
489 send_init_strings(TTY * old_settings)
490 {
491     int i;
492     bool need_flush = FALSE;
493
494 #ifdef TAB3
495     if (old_settings != 0 &&
496         old_settings->c_oflag & (TAB3 | ONLCR | OCRNL | ONLRET)) {
497         old_settings->c_oflag &= (TAB3 | ONLCR | OCRNL | ONLRET);
498         SET_TTY(my_fd, old_settings);
499     }
500 #endif
501     if (use_reset || use_init) {
502         if (init_prog != 0) {
503             IGNORE_RC(system(init_prog));
504         }
505
506         need_flush |= sent_string((use_reset && (reset_1string != 0))
507                                   ? reset_1string
508                                   : init_1string);
509
510         need_flush |= sent_string((use_reset && (reset_2string != 0))
511                                   ? reset_2string
512                                   : init_2string);
513
514         if (set_lr_margin != 0) {
515             need_flush |= sent_string(TPARM_2(set_lr_margin, 0,
516                                               columns - 1));
517         } else if (set_left_margin_parm != 0
518                    && set_right_margin_parm != 0) {
519             need_flush |= sent_string(TPARM_1(set_left_margin_parm, 0));
520             need_flush |= sent_string(TPARM_1(set_right_margin_parm,
521                                               columns - 1));
522         } else if (clear_margins != 0
523                    && set_left_margin != 0
524                    && set_right_margin != 0) {
525             need_flush |= sent_string(clear_margins);
526             if (carriage_return != 0) {
527                 need_flush |= sent_string(carriage_return);
528             } else {
529                 PUTCHAR('\r');
530             }
531             need_flush |= sent_string(set_left_margin);
532             if (parm_right_cursor) {
533                 need_flush |= sent_string(TPARM_1(parm_right_cursor,
534                                                   columns - 1));
535             } else {
536                 for (i = 0; i < columns - 1; i++) {
537                     PUTCHAR(' ');
538                 }
539             }
540             need_flush |= sent_string(set_right_margin);
541             if (carriage_return != 0) {
542                 need_flush |= sent_string(carriage_return);
543             } else {
544                 PUTCHAR('\r');
545             }
546         }
547
548         need_flush |= reset_tabstops(columns);
549
550         need_flush |= cat_file((use_reset && reset_file) ? reset_file : init_file);
551
552         need_flush |= sent_string((use_reset && (reset_3string != 0))
553                                   ? reset_3string
554                                   : init_3string);
555     }
556
557     return need_flush;
558 }
559
560 /*
561  * Tell the user if a control key has been changed from the default value.
562  */
563 static void
564 show_tty_change(TTY * old_settings,
565                 TTY * new_settings,
566                 const char *name,
567                 int which,
568                 unsigned def)
569 {
570     unsigned older, newer;
571     char *p;
572
573     newer = new_settings->c_cc[which];
574     older = old_settings->c_cc[which];
575
576     if (older == newer && older == def)
577         return;
578
579     (void) fprintf(stderr, "%s %s ", name, older == newer ? "is" : "set to");
580
581     if (DISABLED(newer)) {
582         (void) fprintf(stderr, "undef.\n");
583         /*
584          * Check 'delete' before 'backspace', since the key_backspace value
585          * is ambiguous.
586          */
587     } else if (newer == 0177) {
588         (void) fprintf(stderr, "delete.\n");
589     } else if ((p = key_backspace) != 0
590                && newer == (unsigned char) p[0]
591                && p[1] == '\0') {
592         (void) fprintf(stderr, "backspace.\n");
593     } else if (newer < 040) {
594         newer ^= 0100;
595         (void) fprintf(stderr, "control-%c (^%c).\n", UChar(newer), UChar(newer));
596     } else
597         (void) fprintf(stderr, "%c.\n", UChar(newer));
598 }
599
600 /**************************************************************************
601  * Miscellaneous.
602  **************************************************************************/
603
604 void
605 reset_start(FILE *fp, bool is_reset, bool is_init)
606 {
607     my_file = fp;
608     use_reset = is_reset;
609     use_init = is_init;
610 }
611
612 void
613 reset_flush(void)
614 {
615     if (my_file != 0)
616         fflush(my_file);
617 }
618
619 void
620 print_tty_chars(TTY * old_settings, TTY * new_settings)
621 {
622     show_tty_change(old_settings, new_settings, "Erase", VERASE, CERASE);
623     show_tty_change(old_settings, new_settings, "Kill", VKILL, CKILL);
624     show_tty_change(old_settings, new_settings, "Interrupt", VINTR, CINTR);
625 }
626
627 /*
628  * Open a file descriptor on the current terminal, to obtain its settings.
629  * stderr is less likely to be redirected than stdout; try that first.
630  */
631 int
632 save_tty_settings(TTY * tty_settings)
633 {
634     if (!get_tty_settings(STDERR_FILENO, tty_settings) &&
635         !get_tty_settings(STDOUT_FILENO, tty_settings) &&
636         !get_tty_settings(STDIN_FILENO, tty_settings) &&
637         !get_tty_settings(open("/dev/tty", O_RDWR), tty_settings)) {
638         failed("terminal attributes");
639     }
640     can_restore = TRUE;
641     original_settings = *tty_settings;
642     return my_fd;
643 }
644
645 void
646 restore_tty_settings(void)
647 {
648     if (can_restore)
649         SET_TTY(my_fd, &original_settings);
650 }
651
652 /* Set the modes if they've changed. */
653 void
654 update_tty_settings(TTY * old_settings, TTY * new_settings)
655 {
656     if (memcmp(new_settings, old_settings, sizeof(TTY))) {
657         SET_TTY(my_fd, new_settings);
658     }
659 }
660
661 #if HAVE_SIZECHANGE
662 /* Set window size if not set already */
663 void
664 set_window_size(int fd, int high, int wide)
665 {
666     STRUCT_WINSIZE win;
667     (void) ioctl(fd, IOCTL_GET_WINSIZE, &win);
668     if (WINSIZE_ROWS(win) == 0 &&
669         WINSIZE_COLS(win) == 0 &&
670         high > 0 && wide > 0) {
671         WINSIZE_ROWS(win) = (unsigned short) high;
672         WINSIZE_COLS(win) = (unsigned short) wide;
673         (void) ioctl(fd, IOCTL_SET_WINSIZE, &win);
674     }
675 }
676 #endif