GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mail/tty.c Lines: 0 195 0.0 %
Date: 2017-11-13 Branches: 0 132 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: tty.c,v 1.21 2017/06/28 14:58:23 anton 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.h>
43
#include <fcntl.h>
44
45
#define	TABWIDTH	8
46
47
struct tty {
48
	int	 fdin;
49
	int	 fdout;
50
	int	 flags;
51
#define	TTY_ALTWERASE	0x1
52
#define	TTY_ERR		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
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_RESTART;
90
	act.sa_handler = SIG_DFL;
91
	(void)sigaction(SIGTSTP, &act, &savetstp);
92
	(void)sigaction(SIGTTOU, &act, &savettou);
93
	(void)sigaction(SIGTTIN, &act, &savettin);
94
	error = 1;
95
	memset(&tty, 0, sizeof(tty));
96
	tty.fdin = fileno(stdin);
97
	tty.fdout = fileno(stdout);
98
	if (tcgetattr(tty.fdin, &oldtio) < 0) {
99
		warn("tcgetattr");
100
		return(-1);
101
	}
102
	tty.keys = oldtio.c_cc;
103
	if (oldtio.c_lflag & ALTWERASE)
104
		tty.flags |= TTY_ALTWERASE;
105
106
	newtio = oldtio;
107
	newtio.c_lflag &= ~(ECHO | ICANON);
108
	newtio.c_cc[VMIN] = 1;
109
	newtio.c_cc[VTIME] = 0;
110
	if (tcsetattr(tty.fdin, TCSADRAIN, &newtio) < 0) {
111
		warn("tcsetattr");
112
		return(-1);
113
	}
114
115
#ifdef TIOCEXT
116
	extproc = ((oldtio.c_lflag & EXTPROC) ? 1 : 0);
117
	if (extproc) {
118
		flag = 0;
119
		if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0)
120
			warn("TIOCEXT: off");
121
	}
122
#endif
123
	if (gflags & GTO) {
124
		s = readtty("To: ", detract(hp->h_to, 0));
125
		if (s == NULL)
126
			goto out;
127
		hp->h_to = extract(s, GTO);
128
	}
129
	if (gflags & GSUBJECT) {
130
		s = readtty("Subject: ", hp->h_subject);
131
		if (s == NULL)
132
			goto out;
133
		hp->h_subject = s;
134
	}
135
	if (gflags & GCC) {
136
		s = readtty("Cc: ", detract(hp->h_cc, 0));
137
		if (s == NULL)
138
			goto out;
139
		hp->h_cc = extract(s, GCC);
140
	}
141
	if (gflags & GBCC) {
142
		s = readtty("Bcc: ", detract(hp->h_bcc, 0));
143
		if (s == NULL)
144
			goto out;
145
		hp->h_bcc = extract(s, GBCC);
146
	}
147
	error = 0;
148
out:
149
	(void)sigaction(SIGTSTP, &savetstp, NULL);
150
	(void)sigaction(SIGTTOU, &savettou, NULL);
151
	(void)sigaction(SIGTTIN, &savettin, NULL);
152
#ifdef TIOCEXT
153
	if (extproc) {
154
		flag = 1;
155
		if (ioctl(fileno(stdin), TIOCEXT, &flag) < 0)
156
			warn("TIOCEXT: on");
157
	}
158
#endif
159
	if (tcsetattr(tty.fdin, TCSADRAIN, &oldtio) < 0)
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[BUFSIZ];
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 && strlen(src) > sizeof(canonb) - 2) {
189
		puts("too long to edit");
190
		return(src);
191
	}
192
	if (src != NULL) {
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 = ttyint;
201
	(void)sigaction(SIGINT, &act, &saveint);
202
	act.sa_handler = ttystop;
203
	(void)sigaction(SIGTSTP, &act, NULL);
204
	(void)sigaction(SIGTTOU, &act, NULL);
205
	(void)sigaction(SIGTTIN, &act, NULL);
206
	(void)sigprocmask(SIG_UNBLOCK, &intset, &oset);
207
	for (;;) {
208
		c = tty_getc(&tty);
209
		switch (ttysignal) {
210
			case SIGINT:
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 = SIG_DFL;
232
	sigemptyset(&act.sa_mask);
233
	act.sa_flags = SA_RESTART;
234
	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
235
	(void)sigaction(SIGTSTP, &act, NULL);
236
	(void)sigaction(SIGTTOU, &act, NULL);
237
	(void)sigaction(SIGTTIN, &act, NULL);
238
	(void)sigaction(SIGINT, &saveint, NULL);
239
	if (tty.flags & TTY_ERR) {
240
		if (ttysignal == SIGINT) {
241
			ttysignal = 0;
242
			return(NULL);	/* user hit ^C */
243
		}
244
245
redo:
246
		cp = strlen(canonb) > 0 ? canonb : NULL;
247
		/* XXX - make iterative, not recursive */
248
		return(readtty(pr, cp));
249
	}
250
	if (equal("", canonb))
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;
270
	(void)sigemptyset(&act.sa_mask);
271
	act.sa_flags = SA_RESTART;
272
	act.sa_handler = SIG_DFL;
273
	(void)sigaction(s, &act, &oact);
274
	(void)sigemptyset(&nset);
275
	(void)sigaddset(&nset, s);
276
	(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
277
	(void)kill(0, s);
278
	(void)sigprocmask(SIG_BLOCK, &nset, NULL);
279
	(void)sigaction(s, &oact, NULL);
280
	ttysignal = s;
281
	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 += TABWIDTH - 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_ERR;
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)) {
346
		if (nocntrl)
347
			return 0;
348
		if (t->len > 0)
349
			t->len--;
350
	} else if (CCEQ(t->keys[VWERASE], c)) {
351
		if (nocntrl)
352
			return 0;
353
		for (; t->len > 0; t->len--)
354
			if (strchr(ws, t->buf[t->len - 1]) == NULL
355
			    && ((t->flags & TTY_ALTWERASE) == 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
360
			    || ((t->flags & TTY_ALTWERASE)
361
				    && !isalpha(t->buf[t->len - 1])))
362
				break;
363
	} else if (CCEQ(t->keys[VKILL], c)) {
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 < TABWIDTH; 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
}