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