File: | src/usr.bin/mail/tty.c |
Warning: | line 161, column 2 Address of stack memory associated with local variable 'oldtio' is still referred to by the global variable 'tty' upon returning to the caller. This will be a dangling reference |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: tty.c,v 1.22 2019/06/28 13:35:02 deraadt Exp $ */ | |||
2 | /* $NetBSD: tty.c,v 1.7 1997/07/09 05:25:46 mikel Exp $ */ | |||
3 | ||||
4 | /* | |||
5 | * Copyright (c) 1980, 1993 | |||
6 | * The Regents of the University of California. All rights reserved. | |||
7 | * | |||
8 | * Redistribution and use in source and binary forms, with or without | |||
9 | * modification, are permitted provided that the following conditions | |||
10 | * are met: | |||
11 | * 1. Redistributions of source code must retain the above copyright | |||
12 | * notice, this list of conditions and the following disclaimer. | |||
13 | * 2. Redistributions in binary form must reproduce the above copyright | |||
14 | * notice, this list of conditions and the following disclaimer in the | |||
15 | * documentation and/or other materials provided with the distribution. | |||
16 | * 3. Neither the name of the University nor the names of its contributors | |||
17 | * may be used to endorse or promote products derived from this software | |||
18 | * without specific prior written permission. | |||
19 | * | |||
20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |||
21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||
23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |||
24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||
25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||
26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||
27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||
28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||
29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
30 | * SUCH DAMAGE. | |||
31 | */ | |||
32 | ||||
33 | /* | |||
34 | * Mail -- a mail program | |||
35 | * | |||
36 | * Generally useful tty stuff. | |||
37 | */ | |||
38 | ||||
39 | #include "rcv.h" | |||
40 | #include "extern.h" | |||
41 | #include <sys/ioctl.h> | |||
42 | #include <errno(*__errno()).h> | |||
43 | #include <fcntl.h> | |||
44 | ||||
45 | #define TABWIDTH8 8 | |||
46 | ||||
47 | struct tty { | |||
48 | int fdin; | |||
49 | int fdout; | |||
50 | int flags; | |||
51 | #define TTY_ALTWERASE0x1 0x1 | |||
52 | #define TTY_ERR0x2 0x2 | |||
53 | cc_t *keys; | |||
54 | char *buf; | |||
55 | size_t size; | |||
56 | size_t len; | |||
57 | size_t cursor; | |||
58 | }; | |||
59 | ||||
60 | static void tty_flush(struct tty *); | |||
61 | static int tty_getc(struct tty *); | |||
62 | static int tty_insert(struct tty *, int, int); | |||
63 | static void tty_putc(struct tty *, int); | |||
64 | static void tty_reset(struct tty *); | |||
65 | static void tty_visc(struct tty *, int); | |||
66 | ||||
67 | static struct tty tty; | |||
68 | static volatile sig_atomic_t ttysignal; /* Interrupted by a signal? */ | |||
69 | ||||
70 | /* | |||
71 | * Read all relevant header fields. | |||
72 | */ | |||
73 | int | |||
74 | grabh(struct header *hp, int gflags) | |||
75 | { | |||
76 | struct termios newtio, oldtio; | |||
77 | #ifdef TIOCEXT((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('t')) << 8) | ((96))) | |||
78 | int extproc; | |||
79 | int flag; | |||
80 | #endif | |||
81 | struct sigaction savetstp; | |||
82 | struct sigaction savettou; | |||
83 | struct sigaction savettin; | |||
84 | struct sigaction act; | |||
85 | char *s; | |||
86 | int error; | |||
87 | ||||
88 | sigemptyset(&act.sa_mask); | |||
89 | act.sa_flags = SA_RESTART0x0002; | |||
90 | act.sa_handler__sigaction_u.__sa_handler = SIG_DFL(void (*)(int))0; | |||
91 | (void)sigaction(SIGTSTP18, &act, &savetstp); | |||
92 | (void)sigaction(SIGTTOU22, &act, &savettou); | |||
93 | (void)sigaction(SIGTTIN21, &act, &savettin); | |||
94 | error = 1; | |||
95 | memset(&tty, 0, sizeof(tty)); | |||
96 | tty.fdin = fileno(stdin)(!__isthreaded ? (((&__sF[0]))->_file) : (fileno)((& __sF[0]))); | |||
| ||||
97 | tty.fdout = fileno(stdout)(!__isthreaded ? (((&__sF[1]))->_file) : (fileno)((& __sF[1]))); | |||
98 | if (tcgetattr(tty.fdin, &oldtio) == -1) { | |||
99 | warn("tcgetattr"); | |||
100 | return(-1); | |||
101 | } | |||
102 | tty.keys = oldtio.c_cc; | |||
103 | if (oldtio.c_lflag & ALTWERASE0x00000200) | |||
104 | tty.flags |= TTY_ALTWERASE0x1; | |||
105 | ||||
106 | newtio = oldtio; | |||
107 | newtio.c_lflag &= ~(ECHO0x00000008 | ICANON0x00000100); | |||
108 | newtio.c_cc[VMIN16] = 1; | |||
109 | newtio.c_cc[VTIME17] = 0; | |||
110 | if (tcsetattr(tty.fdin, TCSADRAIN1, &newtio) == -1) { | |||
111 | warn("tcsetattr"); | |||
112 | return(-1); | |||
113 | } | |||
114 | ||||
115 | #ifdef TIOCEXT((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('t')) << 8) | ((96))) | |||
116 | extproc = ((oldtio.c_lflag & EXTPROC0x00000800) ? 1 : 0); | |||
117 | if (extproc
| |||
118 | flag = 0; | |||
119 | if (ioctl(fileno(stdin)(!__isthreaded ? (((&__sF[0]))->_file) : (fileno)((& __sF[0]))), TIOCEXT((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('t')) << 8) | ((96))), &flag) == -1) | |||
120 | warn("TIOCEXT: off"); | |||
121 | } | |||
122 | #endif | |||
123 | if (gflags & GTO1) { | |||
124 | s = readtty("To: ", detract(hp->h_to, 0)); | |||
125 | if (s == NULL((void *)0)) | |||
126 | goto out; | |||
127 | hp->h_to = extract(s, GTO1); | |||
128 | } | |||
129 | if (gflags & GSUBJECT2) { | |||
130 | s = readtty("Subject: ", hp->h_subject); | |||
131 | if (s == NULL((void *)0)) | |||
132 | goto out; | |||
133 | hp->h_subject = s; | |||
134 | } | |||
135 | if (gflags & GCC4) { | |||
136 | s = readtty("Cc: ", detract(hp->h_cc, 0)); | |||
137 | if (s == NULL((void *)0)) | |||
138 | goto out; | |||
139 | hp->h_cc = extract(s, GCC4); | |||
140 | } | |||
141 | if (gflags & GBCC8) { | |||
142 | s = readtty("Bcc: ", detract(hp->h_bcc, 0)); | |||
143 | if (s == NULL((void *)0)) | |||
144 | goto out; | |||
145 | hp->h_bcc = extract(s, GBCC8); | |||
146 | } | |||
147 | error = 0; | |||
148 | out: | |||
149 | (void)sigaction(SIGTSTP18, &savetstp, NULL((void *)0)); | |||
150 | (void)sigaction(SIGTTOU22, &savettou, NULL((void *)0)); | |||
151 | (void)sigaction(SIGTTIN21, &savettin, NULL((void *)0)); | |||
152 | #ifdef TIOCEXT((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('t')) << 8) | ((96))) | |||
153 | if (extproc
| |||
154 | flag = 1; | |||
155 | if (ioctl(fileno(stdin)(!__isthreaded ? (((&__sF[0]))->_file) : (fileno)((& __sF[0]))), TIOCEXT((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('t')) << 8) | ((96))), &flag) == -1) | |||
156 | warn("TIOCEXT: on"); | |||
157 | } | |||
158 | #endif | |||
159 | if (tcsetattr(tty.fdin, TCSADRAIN1, &oldtio) == -1) | |||
160 | warn("tcsetattr"); | |||
161 | return(error); | |||
| ||||
162 | } | |||
163 | ||||
164 | /* | |||
165 | * Read up a header from standard input. | |||
166 | * The source string has the preliminary contents to | |||
167 | * be read. | |||
168 | * | |||
169 | */ | |||
170 | char * | |||
171 | readtty(char *pr, char *src) | |||
172 | { | |||
173 | struct sigaction act, saveint; | |||
174 | unsigned char canonb[BUFSIZ1024]; | |||
175 | char *cp; | |||
176 | sigset_t oset; | |||
177 | int c, done; | |||
178 | ||||
179 | memset(canonb, 0, sizeof(canonb)); | |||
180 | tty.buf = canonb; | |||
181 | tty.size = sizeof(canonb) - 1; | |||
182 | ||||
183 | for (cp = pr; *cp != '\0'; cp++) | |||
184 | tty_insert(&tty, *cp, 1); | |||
185 | tty_flush(&tty); | |||
186 | tty_reset(&tty); | |||
187 | ||||
188 | if (src != NULL((void *)0) && strlen(src) > sizeof(canonb) - 2) { | |||
189 | puts("too long to edit"); | |||
190 | return(src); | |||
191 | } | |||
192 | if (src != NULL((void *)0)) { | |||
193 | for (cp = src; *cp != '\0'; cp++) | |||
194 | tty_insert(&tty, *cp, 1); | |||
195 | tty_flush(&tty); | |||
196 | } | |||
197 | ||||
198 | sigemptyset(&act.sa_mask); | |||
199 | act.sa_flags = 0; /* Note: will not restart syscalls */ | |||
200 | act.sa_handler__sigaction_u.__sa_handler = ttyint; | |||
201 | (void)sigaction(SIGINT2, &act, &saveint); | |||
202 | act.sa_handler__sigaction_u.__sa_handler = ttystop; | |||
203 | (void)sigaction(SIGTSTP18, &act, NULL((void *)0)); | |||
204 | (void)sigaction(SIGTTOU22, &act, NULL((void *)0)); | |||
205 | (void)sigaction(SIGTTIN21, &act, NULL((void *)0)); | |||
206 | (void)sigprocmask(SIG_UNBLOCK2, &intset, &oset); | |||
207 | for (;;) { | |||
208 | c = tty_getc(&tty); | |||
209 | switch (ttysignal) { | |||
210 | case SIGINT2: | |||
211 | tty_visc(&tty, '\003'); /* output ^C */ | |||
212 | /* FALLTHROUGH */ | |||
213 | case 0: | |||
214 | break; | |||
215 | default: | |||
216 | ttysignal = 0; | |||
217 | goto redo; | |||
218 | } | |||
219 | if (c == 0) { | |||
220 | done = 1; | |||
221 | } else if (c == '\n') { | |||
222 | tty_putc(&tty, c); | |||
223 | done = 1; | |||
224 | } else { | |||
225 | done = tty_insert(&tty, c, 0); | |||
226 | tty_flush(&tty); | |||
227 | } | |||
228 | if (done) | |||
229 | break; | |||
230 | } | |||
231 | act.sa_handler__sigaction_u.__sa_handler = SIG_DFL(void (*)(int))0; | |||
232 | sigemptyset(&act.sa_mask); | |||
233 | act.sa_flags = SA_RESTART0x0002; | |||
234 | (void)sigprocmask(SIG_SETMASK3, &oset, NULL((void *)0)); | |||
235 | (void)sigaction(SIGTSTP18, &act, NULL((void *)0)); | |||
236 | (void)sigaction(SIGTTOU22, &act, NULL((void *)0)); | |||
237 | (void)sigaction(SIGTTIN21, &act, NULL((void *)0)); | |||
238 | (void)sigaction(SIGINT2, &saveint, NULL((void *)0)); | |||
239 | if (tty.flags & TTY_ERR0x2) { | |||
240 | if (ttysignal == SIGINT2) { | |||
241 | ttysignal = 0; | |||
242 | return(NULL((void *)0)); /* user hit ^C */ | |||
243 | } | |||
244 | ||||
245 | redo: | |||
246 | cp = strlen(canonb) > 0 ? canonb : NULL((void *)0); | |||
247 | /* XXX - make iterative, not recursive */ | |||
248 | return(readtty(pr, cp)); | |||
249 | } | |||
250 | if (equal("", canonb)(strcmp("",canonb)==0)) | |||
251 | return(""); | |||
252 | return(savestr(canonb)); | |||
253 | } | |||
254 | ||||
255 | /* | |||
256 | * Receipt continuation. | |||
257 | */ | |||
258 | void | |||
259 | ttystop(int s) | |||
260 | { | |||
261 | struct sigaction act, oact; | |||
262 | sigset_t nset; | |||
263 | int save_errno; | |||
264 | ||||
265 | /* | |||
266 | * Save old handler and set to default. | |||
267 | * Unblock receipt of 's' and then resend it. | |||
268 | */ | |||
269 | save_errno = errno(*__errno()); | |||
270 | (void)sigemptyset(&act.sa_mask); | |||
271 | act.sa_flags = SA_RESTART0x0002; | |||
272 | act.sa_handler__sigaction_u.__sa_handler = SIG_DFL(void (*)(int))0; | |||
273 | (void)sigaction(s, &act, &oact); | |||
274 | (void)sigemptyset(&nset); | |||
275 | (void)sigaddset(&nset, s); | |||
276 | (void)sigprocmask(SIG_UNBLOCK2, &nset, NULL((void *)0)); | |||
277 | (void)kill(0, s); | |||
278 | (void)sigprocmask(SIG_BLOCK1, &nset, NULL((void *)0)); | |||
279 | (void)sigaction(s, &oact, NULL((void *)0)); | |||
280 | ttysignal = s; | |||
281 | errno(*__errno()) = save_errno; | |||
282 | } | |||
283 | ||||
284 | /*ARGSUSED*/ | |||
285 | void | |||
286 | ttyint(int s) | |||
287 | { | |||
288 | ||||
289 | ttysignal = s; | |||
290 | } | |||
291 | ||||
292 | static void | |||
293 | tty_flush(struct tty *t) | |||
294 | { | |||
295 | size_t i, len; | |||
296 | int c; | |||
297 | ||||
298 | if (t->cursor < t->len) { | |||
299 | for (; t->cursor < t->len; t->cursor++) | |||
300 | tty_visc(t, t->buf[t->cursor]); | |||
301 | } else if (t->cursor > t->len) { | |||
302 | len = t->cursor - t->len; | |||
303 | for (i = len; i > 0; i--) { | |||
304 | c = t->buf[--t->cursor]; | |||
305 | if (c == '\t') | |||
306 | len += TABWIDTH8 - 1; | |||
307 | else if (iscntrl(c)) | |||
308 | len++; /* account for leading ^ */ | |||
309 | } | |||
310 | for (i = 0; i < len; i++) | |||
311 | tty_putc(t, '\b'); | |||
312 | for (i = 0; i < len; i++) | |||
313 | tty_putc(t, ' '); | |||
314 | for (i = 0; i < len; i++) | |||
315 | tty_putc(t, '\b'); | |||
316 | t->cursor = t->len; | |||
317 | } | |||
318 | ||||
319 | t->buf[t->len] = '\0'; | |||
320 | } | |||
321 | ||||
322 | static int | |||
323 | tty_getc(struct tty *t) | |||
324 | { | |||
325 | ssize_t n; | |||
326 | unsigned char c; | |||
327 | ||||
328 | n = read(t->fdin, &c, 1); | |||
329 | switch (n) { | |||
330 | case -1: | |||
331 | t->flags |= TTY_ERR0x2; | |||
332 | /* FALLTHROUGH */ | |||
333 | case 0: | |||
334 | return 0; | |||
335 | default: | |||
336 | return c & 0x7f; | |||
337 | } | |||
338 | } | |||
339 | ||||
340 | static int | |||
341 | tty_insert(struct tty *t, int c, int nocntrl) | |||
342 | { | |||
343 | const unsigned char *ws = " \t"; | |||
344 | ||||
345 | if (CCEQ(t->keys[VERASE], c)(c == t->keys[3] ? t->keys[3] != (0377) : 0)) { | |||
346 | if (nocntrl) | |||
347 | return 0; | |||
348 | if (t->len > 0) | |||
349 | t->len--; | |||
350 | } else if (CCEQ(t->keys[VWERASE], c)(c == t->keys[4] ? t->keys[4] != (0377) : 0)) { | |||
351 | if (nocntrl) | |||
352 | return 0; | |||
353 | for (; t->len > 0; t->len--) | |||
354 | if (strchr(ws, t->buf[t->len - 1]) == NULL((void *)0) | |||
355 | && ((t->flags & TTY_ALTWERASE0x1) == 0 | |||
356 | || isalpha(t->buf[t->len - 1]))) | |||
357 | break; | |||
358 | for (; t->len > 0; t->len--) | |||
359 | if (strchr(ws, t->buf[t->len - 1]) != NULL((void *)0) | |||
360 | || ((t->flags & TTY_ALTWERASE0x1) | |||
361 | && !isalpha(t->buf[t->len - 1]))) | |||
362 | break; | |||
363 | } else if (CCEQ(t->keys[VKILL], c)(c == t->keys[5] ? t->keys[5] != (0377) : 0)) { | |||
364 | if (nocntrl) | |||
365 | return 0; | |||
366 | t->len = 0; | |||
367 | } else { | |||
368 | if (t->len == t->size) | |||
369 | return 1; | |||
370 | t->buf[t->len++] = c; | |||
371 | } | |||
372 | ||||
373 | return 0; | |||
374 | } | |||
375 | ||||
376 | static void | |||
377 | tty_putc(struct tty *t, int c) | |||
378 | { | |||
379 | unsigned char cc = c; | |||
380 | ||||
381 | write(t->fdout, &cc, 1); | |||
382 | } | |||
383 | ||||
384 | static void | |||
385 | tty_reset(struct tty *t) | |||
386 | { | |||
387 | memset(t->buf, 0, t->len); | |||
388 | t->len = t->cursor = 0; | |||
389 | } | |||
390 | ||||
391 | static void | |||
392 | tty_visc(struct tty *t, int c) | |||
393 | { | |||
394 | int i; | |||
395 | ||||
396 | if (c == '\t') { | |||
397 | for (i = 0; i < TABWIDTH8; i++) | |||
398 | tty_putc(t, ' '); | |||
399 | } else if (iscntrl(c)) { | |||
400 | tty_putc(t, '^'); | |||
401 | if (c == 0x7F) | |||
402 | tty_putc(t, '?'); | |||
403 | else | |||
404 | tty_putc(t, (c | 0x40)); | |||
405 | } else { | |||
406 | tty_putc(t, c); | |||
407 | } | |||
408 | } |