ncurses 6.0 - patch 20160820
[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.6 2016/08/20 23:53:44 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     (void) old_settings;
495 #ifdef TAB3
496     if (old_settings != 0 &&
497         old_settings->c_oflag & (TAB3 | ONLCR | OCRNL | ONLRET)) {
498         old_settings->c_oflag &= (TAB3 | ONLCR | OCRNL | ONLRET);
499         SET_TTY(my_fd, old_settings);
500     }
501 #endif
502     if (use_reset || use_init) {
503         if (init_prog != 0) {
504             IGNORE_RC(system(init_prog));
505         }
506
507         need_flush |= sent_string((use_reset && (reset_1string != 0))
508                                   ? reset_1string
509                                   : init_1string);
510
511         need_flush |= sent_string((use_reset && (reset_2string != 0))
512                                   ? reset_2string
513                                   : init_2string);
514
515         if (set_lr_margin != 0) {
516             need_flush |= sent_string(TPARM_2(set_lr_margin, 0,
517                                               columns - 1));
518         } else if (set_left_margin_parm != 0
519                    && set_right_margin_parm != 0) {
520             need_flush |= sent_string(TPARM_1(set_left_margin_parm, 0));
521             need_flush |= sent_string(TPARM_1(set_right_margin_parm,
522                                               columns - 1));
523         } else if (clear_margins != 0
524                    && set_left_margin != 0
525                    && set_right_margin != 0) {
526             need_flush |= sent_string(clear_margins);
527             if (carriage_return != 0) {
528                 need_flush |= sent_string(carriage_return);
529             } else {
530                 PUTCHAR('\r');
531             }
532             need_flush |= sent_string(set_left_margin);
533             if (parm_right_cursor) {
534                 need_flush |= sent_string(TPARM_1(parm_right_cursor,
535                                                   columns - 1));
536             } else {
537                 for (i = 0; i < columns - 1; i++) {
538                     PUTCHAR(' ');
539                 }
540             }
541             need_flush |= sent_string(set_right_margin);
542             if (carriage_return != 0) {
543                 need_flush |= sent_string(carriage_return);
544             } else {
545                 PUTCHAR('\r');
546             }
547         }
548
549         need_flush |= reset_tabstops(columns);
550
551         need_flush |= cat_file((use_reset && reset_file) ? reset_file : init_file);
552
553         need_flush |= sent_string((use_reset && (reset_3string != 0))
554                                   ? reset_3string
555                                   : init_3string);
556     }
557
558     return need_flush;
559 }
560
561 /*
562  * Tell the user if a control key has been changed from the default value.
563  */
564 static void
565 show_tty_change(TTY * old_settings,
566                 TTY * new_settings,
567                 const char *name,
568                 int which,
569                 unsigned def)
570 {
571     unsigned older, newer;
572     char *p;
573
574     newer = new_settings->c_cc[which];
575     older = old_settings->c_cc[which];
576
577     if (older == newer && older == def)
578         return;
579
580     (void) fprintf(stderr, "%s %s ", name, older == newer ? "is" : "set to");
581
582     if (DISABLED(newer)) {
583         (void) fprintf(stderr, "undef.\n");
584         /*
585          * Check 'delete' before 'backspace', since the key_backspace value
586          * is ambiguous.
587          */
588     } else if (newer == 0177) {
589         (void) fprintf(stderr, "delete.\n");
590     } else if ((p = key_backspace) != 0
591                && newer == (unsigned char) p[0]
592                && p[1] == '\0') {
593         (void) fprintf(stderr, "backspace.\n");
594     } else if (newer < 040) {
595         newer ^= 0100;
596         (void) fprintf(stderr, "control-%c (^%c).\n", UChar(newer), UChar(newer));
597     } else
598         (void) fprintf(stderr, "%c.\n", UChar(newer));
599 }
600
601 /**************************************************************************
602  * Miscellaneous.
603  **************************************************************************/
604
605 void
606 reset_start(FILE *fp, bool is_reset, bool is_init)
607 {
608     my_file = fp;
609     use_reset = is_reset;
610     use_init = is_init;
611 }
612
613 void
614 reset_flush(void)
615 {
616     if (my_file != 0)
617         fflush(my_file);
618 }
619
620 void
621 print_tty_chars(TTY * old_settings, TTY * new_settings)
622 {
623     show_tty_change(old_settings, new_settings, "Erase", VERASE, CERASE);
624     show_tty_change(old_settings, new_settings, "Kill", VKILL, CKILL);
625     show_tty_change(old_settings, new_settings, "Interrupt", VINTR, CINTR);
626 }
627
628 /*
629  * Open a file descriptor on the current terminal, to obtain its settings.
630  * stderr is less likely to be redirected than stdout; try that first.
631  */
632 int
633 save_tty_settings(TTY * tty_settings)
634 {
635     if (!get_tty_settings(STDERR_FILENO, tty_settings) &&
636         !get_tty_settings(STDOUT_FILENO, tty_settings) &&
637         !get_tty_settings(STDIN_FILENO, tty_settings) &&
638         !get_tty_settings(open("/dev/tty", O_RDWR), tty_settings)) {
639         failed("terminal attributes");
640     }
641     can_restore = TRUE;
642     original_settings = *tty_settings;
643     return my_fd;
644 }
645
646 void
647 restore_tty_settings(void)
648 {
649     if (can_restore)
650         SET_TTY(my_fd, &original_settings);
651 }
652
653 /* Set the modes if they've changed. */
654 void
655 update_tty_settings(TTY * old_settings, TTY * new_settings)
656 {
657     if (memcmp(new_settings, old_settings, sizeof(TTY))) {
658         SET_TTY(my_fd, new_settings);
659     }
660 }
661
662 #if HAVE_SIZECHANGE
663 /* Set window size if not set already */
664 void
665 set_window_size(int fd, int high, int wide)
666 {
667     STRUCT_WINSIZE win;
668     (void) ioctl(fd, IOCTL_GET_WINSIZE, &win);
669     if (WINSIZE_ROWS(win) == 0 &&
670         WINSIZE_COLS(win) == 0 &&
671         high > 0 && wide > 0) {
672         WINSIZE_ROWS(win) = (unsigned short) high;
673         WINSIZE_COLS(win) = (unsigned short) wide;
674         (void) ioctl(fd, IOCTL_SET_WINSIZE, &win);
675     }
676 }
677 #endif