1 /*
2 *
3 * CLEX File Manager
4 *
5 * Copyright (C) 2001-2018 Vlado Potisk <vlado_potisk@clex.sk>
6 *
7 * CLEX is free software without warranty of any kind; see the
8 * GNU General Public License as set out in the "COPYING" document
9 * which accompanies the CLEX File Manager package.
10 *
11 * CLEX can be downloaded from http://www.clex.sk
12 *
13 */
14
15 #include "clexheaders.h"
16
17 #include <ctype.h> /* tolower() */
18 #include <errno.h> /* errno */
19 #include <fcntl.h> /* fcntl */
20 #include <stdio.h> /* fputs() */
21 #include <signal.h> /* SIGTTIN */
22 #include <termios.h> /* struct termios */
23 #include <unistd.h> /* STDIN_FILENO */
24
25 #include "tty.h"
26
27 #include "control.h" /* err_exit() */
28
29 static struct termios *p_raw, *p_text = 0, *p_save = 0;
30
31 #ifdef _POSIX_JOB_CONTROL
32 static pid_t save_pgid = 0;
33 #endif
34
35 void
jc_initialize(void)36 jc_initialize(void)
37 {
38 #ifdef _POSIX_JOB_CONTROL
39 struct sigaction act;
40
41 /* Wait until we are in the foreground */
42 while (tcgetpgrp(STDIN_FILENO) != (save_pgid = getpgrp()))
43 kill(-save_pgid,SIGTTIN);
44
45 /* ignore job control signals */
46 act.sa_handler = SIG_IGN;
47 act.sa_flags = 0;
48 sigemptyset(&act.sa_mask);
49 sigaction(SIGTSTP,&act,0);
50 sigaction(SIGTTIN,&act,0);
51 sigaction(SIGTTOU,&act,0);
52
53 /* put CLEX into its own process group */
54 setpgid(clex_data.pid,clex_data.pid);
55 /* make it the foreground process group */
56 tcsetpgrp(STDIN_FILENO,clex_data.pid);
57 #endif
58 }
59
60 /* this is a cleanup function (see err_exit() in control.c) */
61 void
jc_reset(void)62 jc_reset(void)
63 {
64 #ifdef _POSIX_JOB_CONTROL
65 if (save_pgid)
66 tcsetpgrp(STDIN_FILENO,save_pgid);
67 #endif
68 }
69
70 void
tty_initialize(void)71 tty_initialize(void)
72 {
73 static struct termios text, raw;
74
75 if (!isatty(STDIN_FILENO))
76 err_exit("This is an interactive program, but the standard input is not a terminal");
77
78 if (tcgetattr(STDIN_FILENO,&text) < 0)
79 err_exit("Cannot read the terminal parameters");
80
81 raw = text; /* struct copy */
82 raw.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN);
83 raw.c_cc[VMIN] = 1;
84 raw.c_cc[VTIME] = 0;
85 p_text = &text;
86 p_raw = &raw;
87 }
88
89 void
tty_save()90 tty_save()
91 {
92 static struct termios save;
93
94 /* errors are silently ignored */
95 p_save = tcgetattr(STDIN_FILENO,&save) == 0 ? &save : 0;
96 }
97
98 void
tty_restore(void)99 tty_restore(void)
100 {
101 if (p_save)
102 tcsetattr(STDIN_FILENO,TCSAFLUSH,p_save);
103 }
104
105 /*
106 * make sure interrupt key is ctrl-C
107 * usage: tty_save(); tty_ctrlc();
108 * install-SIGINT-handler
109 * do-stuff
110 * disable-SIGINT
111 * tty_restore();
112 */
113 void
tty_ctrlc()114 tty_ctrlc()
115 {
116 struct termios ctrlc;
117
118 if (p_save) {
119 ctrlc = *p_save;
120 ctrlc.c_cc[VINTR] = CH_CTRL('C');
121 tcsetattr(STDIN_FILENO,TCSAFLUSH,&ctrlc);
122 }
123 }
124
125 /* noncanonical, no echo */
126 void
tty_setraw(void)127 tty_setraw(void)
128 {
129 if (p_raw)
130 tcsetattr(STDIN_FILENO,TCSAFLUSH,p_raw);
131 }
132
133 /* this is a cleanup function (see err_exit() in control.c) */
134 void
tty_reset(void)135 tty_reset(void)
136 {
137 if (p_text)
138 tcsetattr(STDIN_FILENO,TCSAFLUSH,p_text);
139 }
140
141 static int
tty_getchar(void)142 tty_getchar(void)
143 {
144 int in, flags, loops = 0;
145
146 while ((in = getchar()) == EOF) {
147 if (errno == EINTR)
148 continue;
149 if (errno == EAGAIN) {
150 flags = fcntl(STDIN_FILENO,F_GETFL);
151 if ((flags & O_NONBLOCK) == O_NONBLOCK) {
152 /* clear the non-blocking flag */
153 fcntl(STDIN_FILENO,F_SETFL,flags & ~O_NONBLOCK);
154 continue;
155 }
156 }
157 if (++loops >= 3)
158 err_exit("Cannot read from standard input");
159 }
160 return in;
161 }
162
163 void
tty_press_enter(void)164 tty_press_enter(void)
165 {
166 int in;
167
168 if (disp_data.noenter) {
169 fputs("Returning to CLEX.",stdout);
170 disp_data.noenter = 0;
171 }
172 else {
173 fputs("Press <enter> to continue. ",stdout);
174 fflush(stdout);
175 tty_setraw();
176 while ((in = tty_getchar()) != '\n' && in != '\r')
177 ;
178 tty_reset();
179 }
180
181 puts("\n----------------------------------------------");
182 fflush(stdout);
183 disp_data.wait = 0;
184 }
185
186 /*
187 * - if 'yeschar' is set, msg is a yes/no question where 'yeschar' (in lower or upper case)
188 * means confirmation ('yeschar' parameter itself should be entered in lower case)
189 */
190 int
tty_dialog(int yeschar,const char * msg)191 tty_dialog(int yeschar, const char *msg)
192 {
193 int code;
194
195 putchar('\n');
196 fputs(msg,stdout);
197 if (yeschar)
198 fprintf(stdout," (%c = %s) ",yeschar,"yes");
199 fflush(stdout);
200 tty_setraw();
201 code = tolower(tty_getchar());
202 tty_reset();
203 if (yeschar) {
204 code = code == yeschar;
205 puts(code ? "yes" : "no");
206 }
207 putchar('\n');
208 fflush(stdout);
209 return code;
210 }
211