ncurses 6.1 - patch 20190105
[ncurses.git] / progs / reset_cmd.c
1 /****************************************************************************
2  * Copyright (c) 2016,2017 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.13 2017/10/07 20:56:03 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         && key_backspace != 0
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 (newline != (char *) 0 && 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 (has_hardware_tabs)      /* Print tabs. */
411         tty_settings->c_oflag &= ~OXTABS;
412 #endif /* OXTABS */
413     tty_settings->c_lflag |= (ECHOE | ECHOK);
414 }
415
416 /*
417  * Set the hardware tabs on the terminal, using the 'ct' (clear all tabs),
418  * 'st' (set one tab) and 'ch' (horizontal cursor addressing) capabilities.
419  * This is done before 'if' and 'is', so they can recover in case of error.
420  *
421  * Return TRUE if we set any tab stops, FALSE if not.
422  */
423 static bool
424 reset_tabstops(int wide)
425 {
426     if ((init_tabs != 8) && (set_tab && clear_all_tabs)) {
427         int c;
428
429         (void) putc('\r', my_file);     /* Force to left margin. */
430         tputs(clear_all_tabs, 0, out_char);
431
432         for (c = 8; c < wide; c += 8) {
433             /* Get to the right column.  In BSD tset, this used to try a bunch
434              * of half-clever things with cup and hpa, for an average saving of
435              * somewhat less than two character times per tab stop, less than
436              * .01 sec at 2400cps.  We lost all this cruft because it seemed to
437              * be introducing some odd bugs.
438              * -----------12345678----------- */
439             (void) fputs("        ", my_file);
440             tputs(set_tab, 0, out_char);
441         }
442         putc('\r', my_file);
443         return (TRUE);
444     }
445     return (FALSE);
446 }
447
448 static bool
449 sent_string(const char *s)
450 {
451     bool sent = FALSE;
452     if (s != 0) {
453         tputs(s, 0, out_char);
454         sent = TRUE;
455     }
456     return sent;
457 }
458
459 #define PUTCHAR(c) fputc(c, my_file)
460
461 /* Output startup string. */
462 bool
463 send_init_strings(int fd GCC_UNUSED, TTY * old_settings)
464 {
465     int i;
466     bool need_flush = FALSE;
467
468     (void) old_settings;
469 #ifdef TAB3
470     if (old_settings != 0 &&
471         old_settings->c_oflag & (TAB3 | ONLCR | OCRNL | ONLRET)) {
472         old_settings->c_oflag &= (TAB3 | ONLCR | OCRNL | ONLRET);
473         SET_TTY(fd, old_settings);
474     }
475 #endif
476     if (use_reset || use_init) {
477         if (init_prog != 0) {
478             IGNORE_RC(system(init_prog));
479         }
480
481         need_flush |= sent_string((use_reset && (reset_1string != 0))
482                                   ? reset_1string
483                                   : init_1string);
484
485         need_flush |= sent_string((use_reset && (reset_2string != 0))
486                                   ? reset_2string
487                                   : init_2string);
488
489 #if defined(set_lr_margin)
490         if (set_lr_margin != 0) {
491             need_flush |= sent_string(TPARM_2(set_lr_margin, 0,
492                                               columns - 1));
493         } else
494 #endif
495 #if defined(set_left_margin_parm) && defined(set_right_margin_parm)
496             if (set_left_margin_parm != 0
497                 && set_right_margin_parm != 0) {
498             need_flush |= sent_string(TPARM_1(set_left_margin_parm, 0));
499             need_flush |= sent_string(TPARM_1(set_right_margin_parm,
500                                               columns - 1));
501         } else
502 #endif
503             if (clear_margins != 0
504                 && set_left_margin != 0
505                 && set_right_margin != 0) {
506             need_flush |= sent_string(clear_margins);
507             if (carriage_return != 0) {
508                 need_flush |= sent_string(carriage_return);
509             } else {
510                 PUTCHAR('\r');
511             }
512             need_flush |= sent_string(set_left_margin);
513             if (parm_right_cursor) {
514                 need_flush |= sent_string(TPARM_1(parm_right_cursor,
515                                                   columns - 1));
516             } else {
517                 for (i = 0; i < columns - 1; i++) {
518                     PUTCHAR(' ');
519                 }
520             }
521             need_flush |= sent_string(set_right_margin);
522             if (carriage_return != 0) {
523                 need_flush |= sent_string(carriage_return);
524             } else {
525                 PUTCHAR('\r');
526             }
527         }
528
529         need_flush |= reset_tabstops(columns);
530
531         need_flush |= cat_file((use_reset && reset_file) ? reset_file : init_file);
532
533         need_flush |= sent_string((use_reset && (reset_3string != 0))
534                                   ? reset_3string
535                                   : init_3string);
536     }
537
538     return need_flush;
539 }
540
541 /*
542  * Tell the user if a control key has been changed from the default value.
543  */
544 static void
545 show_tty_change(TTY * old_settings,
546                 TTY * new_settings,
547                 const char *name,
548                 int which,
549                 unsigned def)
550 {
551     unsigned older, newer;
552     char *p;
553
554     newer = new_settings->c_cc[which];
555     older = old_settings->c_cc[which];
556
557     if (older == newer && older == def)
558         return;
559
560     (void) fprintf(stderr, "%s %s ", name, older == newer ? "is" : "set to");
561
562     if (DISABLED(newer)) {
563         (void) fprintf(stderr, "undef.\n");
564         /*
565          * Check 'delete' before 'backspace', since the key_backspace value
566          * is ambiguous.
567          */
568     } else if (newer == 0177) {
569         (void) fprintf(stderr, "delete.\n");
570     } else if ((p = key_backspace) != 0
571                && newer == (unsigned char) p[0]
572                && p[1] == '\0') {
573         (void) fprintf(stderr, "backspace.\n");
574     } else if (newer < 040) {
575         newer ^= 0100;
576         (void) fprintf(stderr, "control-%c (^%c).\n", UChar(newer), UChar(newer));
577     } else
578         (void) fprintf(stderr, "%c.\n", UChar(newer));
579 }
580
581 /**************************************************************************
582  * Miscellaneous.
583  **************************************************************************/
584
585 void
586 reset_start(FILE *fp, bool is_reset, bool is_init)
587 {
588     my_file = fp;
589     use_reset = is_reset;
590     use_init = is_init;
591 }
592
593 void
594 reset_flush(void)
595 {
596     if (my_file != 0)
597         fflush(my_file);
598 }
599
600 void
601 print_tty_chars(TTY * old_settings, TTY * new_settings)
602 {
603     show_tty_change(old_settings, new_settings, "Erase", VERASE, CERASE);
604     show_tty_change(old_settings, new_settings, "Kill", VKILL, CKILL);
605     show_tty_change(old_settings, new_settings, "Interrupt", VINTR, CINTR);
606 }
607
608 #if HAVE_SIZECHANGE
609 /*
610  * Set window size if not set already, but update our copy of the values if the
611  * size was set.
612  */
613 void
614 set_window_size(int fd, short *high, short *wide)
615 {
616     STRUCT_WINSIZE win;
617     (void) ioctl(fd, IOCTL_GET_WINSIZE, &win);
618     if (WINSIZE_ROWS(win) == 0 &&
619         WINSIZE_COLS(win) == 0) {
620         if (*high > 0 && *wide > 0) {
621             WINSIZE_ROWS(win) = (unsigned short) *high;
622             WINSIZE_COLS(win) = (unsigned short) *wide;
623             (void) ioctl(fd, IOCTL_SET_WINSIZE, &win);
624         }
625     } else if (WINSIZE_ROWS(win) > 0 &&
626                WINSIZE_COLS(win) > 0) {
627         *high = (short) WINSIZE_ROWS(win);
628         *wide = (short) WINSIZE_COLS(win);
629     }
630 }
631 #endif