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