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