4d05c6e985ef1d022197b45dc55ab6b5b03c1b80
[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.12 2017/09/20 00:49:25 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 defined(set_lr_margin)
500         if (set_lr_margin != 0) {
501             need_flush |= sent_string(TPARM_2(set_lr_margin, 0,
502                                               columns - 1));
503         } else
504 #endif
505 #if defined(set_left_margin_parm) && defined(set_right_margin_parm)
506             if (set_left_margin_parm != 0
507                 && set_right_margin_parm != 0) {
508             need_flush |= sent_string(TPARM_1(set_left_margin_parm, 0));
509             need_flush |= sent_string(TPARM_1(set_right_margin_parm,
510                                               columns - 1));
511         } else
512 #endif
513             if (clear_margins != 0
514                 && set_left_margin != 0
515                 && set_right_margin != 0) {
516             need_flush |= sent_string(clear_margins);
517             if (carriage_return != 0) {
518                 need_flush |= sent_string(carriage_return);
519             } else {
520                 PUTCHAR('\r');
521             }
522             need_flush |= sent_string(set_left_margin);
523             if (parm_right_cursor) {
524                 need_flush |= sent_string(TPARM_1(parm_right_cursor,
525                                                   columns - 1));
526             } else {
527                 for (i = 0; i < columns - 1; i++) {
528                     PUTCHAR(' ');
529                 }
530             }
531             need_flush |= sent_string(set_right_margin);
532             if (carriage_return != 0) {
533                 need_flush |= sent_string(carriage_return);
534             } else {
535                 PUTCHAR('\r');
536             }
537         }
538
539         need_flush |= reset_tabstops(columns);
540
541         need_flush |= cat_file((use_reset && reset_file) ? reset_file : init_file);
542
543         need_flush |= sent_string((use_reset && (reset_3string != 0))
544                                   ? reset_3string
545                                   : init_3string);
546     }
547
548     return need_flush;
549 }
550
551 /*
552  * Tell the user if a control key has been changed from the default value.
553  */
554 static void
555 show_tty_change(TTY * old_settings,
556                 TTY * new_settings,
557                 const char *name,
558                 int which,
559                 unsigned def)
560 {
561     unsigned older, newer;
562     char *p;
563
564     newer = new_settings->c_cc[which];
565     older = old_settings->c_cc[which];
566
567     if (older == newer && older == def)
568         return;
569
570     (void) fprintf(stderr, "%s %s ", name, older == newer ? "is" : "set to");
571
572     if (DISABLED(newer)) {
573         (void) fprintf(stderr, "undef.\n");
574         /*
575          * Check 'delete' before 'backspace', since the key_backspace value
576          * is ambiguous.
577          */
578     } else if (newer == 0177) {
579         (void) fprintf(stderr, "delete.\n");
580     } else if ((p = key_backspace) != 0
581                && newer == (unsigned char) p[0]
582                && p[1] == '\0') {
583         (void) fprintf(stderr, "backspace.\n");
584     } else if (newer < 040) {
585         newer ^= 0100;
586         (void) fprintf(stderr, "control-%c (^%c).\n", UChar(newer), UChar(newer));
587     } else
588         (void) fprintf(stderr, "%c.\n", UChar(newer));
589 }
590
591 /**************************************************************************
592  * Miscellaneous.
593  **************************************************************************/
594
595 void
596 reset_start(FILE *fp, bool is_reset, bool is_init)
597 {
598     my_file = fp;
599     use_reset = is_reset;
600     use_init = is_init;
601 }
602
603 void
604 reset_flush(void)
605 {
606     if (my_file != 0)
607         fflush(my_file);
608 }
609
610 void
611 print_tty_chars(TTY * old_settings, TTY * new_settings)
612 {
613     show_tty_change(old_settings, new_settings, "Erase", VERASE, CERASE);
614     show_tty_change(old_settings, new_settings, "Kill", VKILL, CKILL);
615     show_tty_change(old_settings, new_settings, "Interrupt", VINTR, CINTR);
616 }
617
618 #if HAVE_SIZECHANGE
619 /*
620  * Set window size if not set already, but update our copy of the values if the
621  * size was set.
622  */
623 void
624 set_window_size(int fd, short *high, short *wide)
625 {
626     STRUCT_WINSIZE win;
627     (void) ioctl(fd, IOCTL_GET_WINSIZE, &win);
628     if (WINSIZE_ROWS(win) == 0 &&
629         WINSIZE_COLS(win) == 0) {
630         if (*high > 0 && *wide > 0) {
631             WINSIZE_ROWS(win) = (unsigned short) *high;
632             WINSIZE_COLS(win) = (unsigned short) *wide;
633             (void) ioctl(fd, IOCTL_SET_WINSIZE, &win);
634         }
635     } else if (WINSIZE_ROWS(win) > 0 &&
636                WINSIZE_COLS(win) > 0) {
637         *high = (short) WINSIZE_ROWS(win);
638         *wide = (short) WINSIZE_COLS(win);
639     }
640 }
641 #endif