ncurses 6.1 - patch 20190914
[ncurses.git] / progs / reset_cmd.c
1 /****************************************************************************
2  * Copyright (c) 2016-2017,2019 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 #include <tty_settings.h>
35
36 #include <errno.h>
37 #include <stdio.h>
38 #include <fcntl.h>
39
40 #if HAVE_SIZECHANGE
41 # if !defined(sun) || !TERMIOS
42 #  if HAVE_SYS_IOCTL_H
43 #   include <sys/ioctl.h>
44 #  endif
45 # endif
46 #endif
47
48 #if NEED_PTEM_H
49 /* they neglected to define struct winsize in termios.h -- it's only
50    in termio.h  */
51 #include <sys/stream.h>
52 #include <sys/ptem.h>
53 #endif
54
55 MODULE_ID("$Id: reset_cmd.c,v 1.18 2019/07/13 21:35:13 tom Exp $")
56
57 /*
58  * SCO defines TIOCGSIZE and the corresponding struct.  Other systems (SunOS,
59  * Solaris, IRIX) define TIOCGWINSZ and struct winsize.
60  */
61 #ifdef TIOCGSIZE
62 # define IOCTL_GET_WINSIZE TIOCGSIZE
63 # define IOCTL_SET_WINSIZE TIOCSSIZE
64 # define STRUCT_WINSIZE struct ttysize
65 # define WINSIZE_ROWS(n) n.ts_lines
66 # define WINSIZE_COLS(n) n.ts_cols
67 #else
68 # ifdef TIOCGWINSZ
69 #  define IOCTL_GET_WINSIZE TIOCGWINSZ
70 #  define IOCTL_SET_WINSIZE TIOCSWINSZ
71 #  define STRUCT_WINSIZE struct winsize
72 #  define WINSIZE_ROWS(n) n.ws_row
73 #  define WINSIZE_COLS(n) n.ws_col
74 # endif
75 #endif
76
77 static FILE *my_file;
78
79 static bool use_reset = FALSE;  /* invoked as reset */
80 static bool use_init = FALSE;   /* invoked as init */
81
82 static void
83 failed(const char *msg)
84 {
85     int code = errno;
86
87     (void) fprintf(stderr, "%s: %s: %s\n", _nc_progname, msg, strerror(code));
88     restore_tty_settings();
89     (void) fprintf(my_file, "\n");
90     fflush(my_file);
91     ExitProgram(ErrSystem(code));
92     /* NOTREACHED */
93 }
94
95 static bool
96 cat_file(char *file)
97 {
98     FILE *fp;
99     size_t nr;
100     char buf[BUFSIZ];
101     bool sent = FALSE;
102
103     if (file != 0) {
104         if ((fp = fopen(file, "r")) == 0)
105             failed(file);
106
107         while ((nr = fread(buf, sizeof(char), sizeof(buf), fp)) != 0) {
108             if (fwrite(buf, sizeof(char), nr, my_file) != nr) {
109                 failed(file);
110             }
111             sent = TRUE;
112         }
113         fclose(fp);
114     }
115     return sent;
116 }
117
118 static int
119 out_char(int c)
120 {
121     return putc(c, my_file);
122 }
123
124 /**************************************************************************
125  * Mode-setting logic
126  **************************************************************************/
127
128 /* some BSD systems have these built in, some systems are missing
129  * one or more definitions. The safest solution is to override unless the
130  * commonly-altered ones are defined.
131  */
132 #if !(defined(CERASE) && defined(CINTR) && defined(CKILL) && defined(CQUIT))
133 #undef CEOF
134 #undef CERASE
135 #undef CINTR
136 #undef CKILL
137 #undef CLNEXT
138 #undef CRPRNT
139 #undef CQUIT
140 #undef CSTART
141 #undef CSTOP
142 #undef CSUSP
143 #endif
144
145 /* control-character defaults */
146 #ifndef CEOF
147 #define CEOF    CTRL('D')
148 #endif
149 #ifndef CERASE
150 #define CERASE  CTRL('H')
151 #endif
152 #ifndef CINTR
153 #define CINTR   127             /* ^? */
154 #endif
155 #ifndef CKILL
156 #define CKILL   CTRL('U')
157 #endif
158 #ifndef CLNEXT
159 #define CLNEXT  CTRL('v')
160 #endif
161 #ifndef CRPRNT
162 #define CRPRNT  CTRL('r')
163 #endif
164 #ifndef CQUIT
165 #define CQUIT   CTRL('\\')
166 #endif
167 #ifndef CSTART
168 #define CSTART  CTRL('Q')
169 #endif
170 #ifndef CSTOP
171 #define CSTOP   CTRL('S')
172 #endif
173 #ifndef CSUSP
174 #define CSUSP   CTRL('Z')
175 #endif
176
177 #if defined(_POSIX_VDISABLE)
178 #define DISABLED(val)   (((_POSIX_VDISABLE != -1) \
179                        && ((val) == _POSIX_VDISABLE)) \
180                       || ((val) <= 0))
181 #else
182 #define DISABLED(val)   ((int)(val) <= 0)
183 #endif
184
185 #define CHK(val, dft)   (unsigned char) (DISABLED(val) ? dft : val)
186
187 #define reset_char(item, value) \
188     tty_settings->c_cc[item] = CHK(tty_settings->c_cc[item], value)
189
190 /*
191  * Reset the terminal mode bits to a sensible state.  Very useful after
192  * a child program dies in raw mode.
193  */
194 void
195 reset_tty_settings(int fd, TTY * tty_settings)
196 {
197     GET_TTY(fd, tty_settings);
198
199 #ifdef TERMIOS
200 #if defined(VDISCARD) && defined(CDISCARD)
201     reset_char(VDISCARD, CDISCARD);
202 #endif
203     reset_char(VEOF, CEOF);
204     reset_char(VERASE, CERASE);
205 #if defined(VFLUSH) && defined(CFLUSH)
206     reset_char(VFLUSH, CFLUSH);
207 #endif
208     reset_char(VINTR, CINTR);
209     reset_char(VKILL, CKILL);
210 #if defined(VLNEXT) && defined(CLNEXT)
211     reset_char(VLNEXT, CLNEXT);
212 #endif
213     reset_char(VQUIT, CQUIT);
214 #if defined(VREPRINT) && defined(CRPRNT)
215     reset_char(VREPRINT, CRPRNT);
216 #endif
217 #if defined(VSTART) && defined(CSTART)
218     reset_char(VSTART, CSTART);
219 #endif
220 #if defined(VSTOP) && defined(CSTOP)
221     reset_char(VSTOP, CSTOP);
222 #endif
223 #if defined(VSUSP) && defined(CSUSP)
224     reset_char(VSUSP, CSUSP);
225 #endif
226 #if defined(VWERASE) && defined(CWERASE)
227     reset_char(VWERASE, CWERASE);
228 #endif
229
230     tty_settings->c_iflag &= ~((unsigned) (IGNBRK
231                                            | PARMRK
232                                            | INPCK
233                                            | ISTRIP
234                                            | INLCR
235                                            | IGNCR
236 #ifdef IUCLC
237                                            | IUCLC
238 #endif
239 #ifdef IXANY
240                                            | IXANY
241 #endif
242                                            | IXOFF));
243
244     tty_settings->c_iflag |= (BRKINT
245                               | IGNPAR
246                               | ICRNL
247                               | IXON
248 #ifdef IMAXBEL
249                               | IMAXBEL
250 #endif
251         );
252
253     tty_settings->c_oflag &= ~((unsigned) (0
254 #ifdef OLCUC
255                                            | OLCUC
256 #endif
257 #ifdef OCRNL
258                                            | OCRNL
259 #endif
260 #ifdef ONOCR
261                                            | ONOCR
262 #endif
263 #ifdef ONLRET
264                                            | ONLRET
265 #endif
266 #ifdef OFILL
267                                            | OFILL
268 #endif
269 #ifdef OFDEL
270                                            | OFDEL
271 #endif
272 #ifdef NLDLY
273                                            | NLDLY
274 #endif
275 #ifdef CRDLY
276                                            | CRDLY
277 #endif
278 #ifdef TABDLY
279                                            | TABDLY
280 #endif
281 #ifdef BSDLY
282                                            | BSDLY
283 #endif
284 #ifdef VTDLY
285                                            | VTDLY
286 #endif
287 #ifdef FFDLY
288                                            | FFDLY
289 #endif
290                                ));
291
292     tty_settings->c_oflag |= (OPOST
293 #ifdef ONLCR
294                               | ONLCR
295 #endif
296         );
297
298     tty_settings->c_cflag &= ~((unsigned) (CSIZE
299                                            | CSTOPB
300                                            | PARENB
301                                            | PARODD
302                                            | CLOCAL));
303     tty_settings->c_cflag |= (CS8 | CREAD);
304     tty_settings->c_lflag &= ~((unsigned) (ECHONL
305                                            | NOFLSH
306 #ifdef TOSTOP
307                                            | TOSTOP
308 #endif
309 #ifdef ECHOPTR
310                                            | ECHOPRT
311 #endif
312 #ifdef XCASE
313                                            | XCASE
314 #endif
315                                ));
316
317     tty_settings->c_lflag |= (ISIG
318                               | ICANON
319                               | ECHO
320                               | ECHOE
321                               | ECHOK
322 #ifdef ECHOCTL
323                               | ECHOCTL
324 #endif
325 #ifdef ECHOKE
326                               | ECHOKE
327 #endif
328         );
329 #endif
330
331     SET_TTY(fd, tty_settings);
332 }
333
334 /*
335  * Returns a "good" value for the erase character.  This is loosely based on
336  * the BSD4.4 logic.
337  */
338 static int
339 default_erase(void)
340 {
341     int result;
342
343     if (over_strike
344         && VALID_STRING(key_backspace)
345         && strlen(key_backspace) == 1) {
346         result = key_backspace[0];
347     } else {
348         result = CERASE;
349     }
350
351     return result;
352 }
353
354 /*
355  * Update the values of the erase, interrupt, and kill characters in the TTY
356  * parameter.
357  *
358  * SVr4 tset (e.g., Solaris 2.5) only modifies the intr, quit or erase
359  * characters if they're unset, or if we specify them as options.  This differs
360  * from BSD 4.4 tset, which always sets erase.
361  */
362 void
363 set_control_chars(TTY * tty_settings, int my_erase, int my_intr, int my_kill)
364 {
365     if (DISABLED(tty_settings->c_cc[VERASE]) || my_erase >= 0) {
366         tty_settings->c_cc[VERASE] = UChar((my_erase >= 0)
367                                            ? my_erase
368                                            : default_erase());
369     }
370
371     if (DISABLED(tty_settings->c_cc[VINTR]) || my_intr >= 0) {
372         tty_settings->c_cc[VINTR] = UChar((my_intr >= 0)
373                                           ? my_intr
374                                           : CINTR);
375     }
376
377     if (DISABLED(tty_settings->c_cc[VKILL]) || my_kill >= 0) {
378         tty_settings->c_cc[VKILL] = UChar((my_kill >= 0)
379                                           ? my_kill
380                                           : CKILL);
381     }
382 }
383
384 /*
385  * Set up various conversions in the TTY parameter, including parity, tabs,
386  * returns, echo, and case, according to the termcap entry.
387  */
388 void
389 set_conversions(TTY * tty_settings)
390 {
391 #ifdef ONLCR
392     tty_settings->c_oflag |= ONLCR;
393 #endif
394     tty_settings->c_iflag |= ICRNL;
395     tty_settings->c_lflag |= ECHO;
396 #ifdef OXTABS
397     tty_settings->c_oflag |= OXTABS;
398 #endif /* OXTABS */
399
400     /* test used to be tgetflag("NL") */
401     if (VALID_STRING(newline) && newline[0] == '\n' && !newline[1]) {
402         /* Newline, not linefeed. */
403 #ifdef ONLCR
404         tty_settings->c_oflag &= ~((unsigned) ONLCR);
405 #endif
406         tty_settings->c_iflag &= ~((unsigned) ICRNL);
407     }
408 #ifdef OXTABS
409     /* test used to be tgetflag("pt") */
410     if (VALID_STRING(set_tab) && VALID_STRING(clear_all_tabs))
411         tty_settings->c_oflag &= ~OXTABS;
412 #endif /* OXTABS */
413     tty_settings->c_lflag |= (ECHOE | ECHOK);
414 }
415
416 static bool
417 sent_string(const char *s)
418 {
419     bool sent = FALSE;
420     if (VALID_STRING(s)) {
421         tputs(s, 0, out_char);
422         sent = TRUE;
423     }
424     return sent;
425 }
426
427 static bool
428 to_left_margin(void)
429 {
430     if (VALID_STRING(carriage_return)) {
431         sent_string(carriage_return);
432     } else {
433         out_char('\r');
434     }
435     return TRUE;
436 }
437
438 /*
439  * Set the hardware tabs on the terminal, using the 'ct' (clear all tabs),
440  * 'st' (set one tab) and 'ch' (horizontal cursor addressing) capabilities.
441  * This is done before 'if' and 'is', so they can recover in case of error.
442  *
443  * Return TRUE if we set any tab stops, FALSE if not.
444  */
445 static bool
446 reset_tabstops(int wide)
447 {
448     if ((init_tabs != 8)
449         && VALID_NUMERIC(init_tabs)
450         && VALID_STRING(set_tab)
451         && VALID_STRING(clear_all_tabs)) {
452         int c;
453
454         to_left_margin();
455         tputs(clear_all_tabs, 0, out_char);
456         if (init_tabs > 1) {
457             if (init_tabs > wide)
458                 init_tabs = (short) wide;
459             for (c = init_tabs; c < wide; c += init_tabs) {
460                 fprintf(my_file, "%*s", init_tabs, " ");
461                 tputs(set_tab, 0, out_char);
462             }
463             to_left_margin();
464         }
465         return (TRUE);
466     }
467     return (FALSE);
468 }
469
470 /* Output startup string. */
471 bool
472 send_init_strings(int fd GCC_UNUSED, TTY * old_settings)
473 {
474     int i;
475     bool need_flush = FALSE;
476
477     (void) old_settings;
478 #ifdef TAB3
479     if (old_settings != 0 &&
480         old_settings->c_oflag & (TAB3 | ONLCR | OCRNL | ONLRET)) {
481         old_settings->c_oflag &= (TAB3 | ONLCR | OCRNL | ONLRET);
482         SET_TTY(fd, old_settings);
483     }
484 #endif
485     if (use_reset || use_init) {
486         if (VALID_STRING(init_prog)) {
487             IGNORE_RC(system(init_prog));
488         }
489
490         need_flush |= sent_string((use_reset && (reset_1string != 0))
491                                   ? reset_1string
492                                   : init_1string);
493
494         need_flush |= sent_string((use_reset && (reset_2string != 0))
495                                   ? reset_2string
496                                   : init_2string);
497
498         if (VALID_STRING(clear_margins)) {
499             need_flush |= sent_string(clear_margins);
500         } else
501 #if defined(set_lr_margin)
502         if (VALID_STRING(set_lr_margin)) {
503             need_flush |= sent_string(TPARM_2(set_lr_margin, 0,
504                                               columns - 1));
505         } else
506 #endif
507 #if defined(set_left_margin_parm) && defined(set_right_margin_parm)
508             if (VALID_STRING(set_left_margin_parm)
509                 && VALID_STRING(set_right_margin_parm)) {
510             need_flush |= sent_string(TPARM_1(set_left_margin_parm, 0));
511             need_flush |= sent_string(TPARM_1(set_right_margin_parm,
512                                               columns - 1));
513         } else
514 #endif
515             if (VALID_STRING(set_left_margin)
516                 && VALID_STRING(set_right_margin)) {
517             need_flush |= to_left_margin();
518             need_flush |= sent_string(set_left_margin);
519             if (VALID_STRING(parm_right_cursor)) {
520                 need_flush |= sent_string(TPARM_1(parm_right_cursor,
521                                                   columns - 1));
522             } else {
523                 for (i = 0; i < columns - 1; i++) {
524                     out_char(' ');
525                     need_flush = TRUE;
526                 }
527             }
528             need_flush |= sent_string(set_right_margin);
529             need_flush |= to_left_margin();
530         }
531
532         need_flush |= reset_tabstops(columns);
533
534         need_flush |= cat_file((use_reset && reset_file) ? reset_file : init_file);
535
536         need_flush |= sent_string((use_reset && (reset_3string != 0))
537                                   ? reset_3string
538                                   : init_3string);
539     }
540
541     return need_flush;
542 }
543
544 /*
545  * Tell the user if a control key has been changed from the default value.
546  */
547 static void
548 show_tty_change(TTY * old_settings,
549                 TTY * new_settings,
550                 const char *name,
551                 int which,
552                 unsigned def)
553 {
554     unsigned older, newer;
555     char *p;
556
557     newer = new_settings->c_cc[which];
558     older = old_settings->c_cc[which];
559
560     if (older == newer && older == def)
561         return;
562
563     (void) fprintf(stderr, "%s %s ", name, older == newer ? "is" : "set to");
564
565     if (DISABLED(newer)) {
566         (void) fprintf(stderr, "undef.\n");
567         /*
568          * Check 'delete' before 'backspace', since the key_backspace value
569          * is ambiguous.
570          */
571     } else if (newer == 0177) {
572         (void) fprintf(stderr, "delete.\n");
573     } else if ((p = key_backspace) != 0
574                && newer == (unsigned char) p[0]
575                && p[1] == '\0') {
576         (void) fprintf(stderr, "backspace.\n");
577     } else if (newer < 040) {
578         newer ^= 0100;
579         (void) fprintf(stderr, "control-%c (^%c).\n", UChar(newer), UChar(newer));
580     } else
581         (void) fprintf(stderr, "%c.\n", UChar(newer));
582 }
583
584 /**************************************************************************
585  * Miscellaneous.
586  **************************************************************************/
587
588 void
589 reset_start(FILE *fp, bool is_reset, bool is_init)
590 {
591     my_file = fp;
592     use_reset = is_reset;
593     use_init = is_init;
594 }
595
596 void
597 reset_flush(void)
598 {
599     if (my_file != 0)
600         fflush(my_file);
601 }
602
603 void
604 print_tty_chars(TTY * old_settings, TTY * new_settings)
605 {
606     show_tty_change(old_settings, new_settings, "Erase", VERASE, CERASE);
607     show_tty_change(old_settings, new_settings, "Kill", VKILL, CKILL);
608     show_tty_change(old_settings, new_settings, "Interrupt", VINTR, CINTR);
609 }
610
611 #if HAVE_SIZECHANGE
612 /*
613  * Set window size if not set already, but update our copy of the values if the
614  * size was set.
615  */
616 void
617 set_window_size(int fd, short *high, short *wide)
618 {
619     STRUCT_WINSIZE win;
620     (void) ioctl(fd, IOCTL_GET_WINSIZE, &win);
621     if (WINSIZE_ROWS(win) == 0 &&
622         WINSIZE_COLS(win) == 0) {
623         if (*high > 0 && *wide > 0) {
624             WINSIZE_ROWS(win) = (unsigned short) *high;
625             WINSIZE_COLS(win) = (unsigned short) *wide;
626             (void) ioctl(fd, IOCTL_SET_WINSIZE, &win);
627         }
628     } else if (WINSIZE_ROWS(win) > 0 &&
629                WINSIZE_COLS(win) > 0) {
630         *high = (short) WINSIZE_ROWS(win);
631         *wide = (short) WINSIZE_COLS(win);
632     }
633 }
634 #endif