ptydata.c revision 894e0ac8
1/* $XTermId: ptydata.c,v 1.104 2014/05/26 14:46:18 tom Exp $ */
2
3/*
4 * Copyright 1999-2013,2014 by Thomas E. Dickey
5 *
6 *                         All Rights Reserved
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 * Except as contained in this notice, the name(s) of the above copyright
28 * holders shall not be used in advertising or otherwise to promote the
29 * sale, use or other dealings in this Software without prior written
30 * authorization.
31 */
32
33#include <data.h>
34
35#if OPT_WIDE_CHARS
36#include <menu.h>
37#endif
38
39/*
40 * Check for both EAGAIN and EWOULDBLOCK, because some supposedly POSIX
41 * systems are broken and return EWOULDBLOCK when they should return EAGAIN.
42 * Note that this macro may evaluate its argument more than once.
43 */
44#if defined(EAGAIN) && defined(EWOULDBLOCK)
45#define E_TEST(err) ((err) == EAGAIN || (err) == EWOULDBLOCK)
46#else
47#ifdef EAGAIN
48#define E_TEST(err) ((err) == EAGAIN)
49#else
50#define E_TEST(err) ((err) == EWOULDBLOCK)
51#endif
52#endif
53
54#if OPT_WIDE_CHARS
55/*
56 * Convert the 8-bit codes in data->buffer[] into Unicode in data->utf_data.
57 * The number of bytes converted will be nonzero iff there is data.
58 */
59Bool
60decodeUtf8(TScreen *screen, PtyData *data)
61{
62    int i;
63    int length = (int) (data->last - data->next);
64    int utf_count = 0;
65    unsigned utf_char = 0;
66
67    data->utf_size = 0;
68    for (i = 0; i < length; i++) {
69	unsigned c = data->next[i];
70
71	/* Combine UTF-8 into Unicode */
72	if (c < 0x80) {
73	    /* We received an ASCII character */
74	    if (utf_count > 0) {
75		data->utf_data = UCS_REPL;	/* prev. sequence incomplete */
76		data->utf_size = i;
77	    } else {
78		data->utf_data = (IChar) c;
79		data->utf_size = 1;
80	    }
81	    break;
82	} else if (c < 0xc0) {
83	    /* We received a continuation byte */
84	    if (utf_count < 1) {
85		/*
86		 * We received a continuation byte before receiving a sequence
87		 * state.  Or an attempt to use a C1 control string.  Either
88		 * way, it is mapped to the replacement character, unless
89		 * allowed by optional feature.
90		 */
91		data->utf_data = (IChar) (screen->c1_printable ? c : UCS_REPL);
92		data->utf_size = (i + 1);
93		break;
94	    } else {
95		/* Check for overlong UTF-8 sequences for which a shorter
96		 * encoding would exist and replace them with UCS_REPL.
97		 * An overlong UTF-8 sequence can have any of the following
98		 * forms:
99		 *   1100000x 10xxxxxx
100		 *   11100000 100xxxxx 10xxxxxx
101		 *   11110000 1000xxxx 10xxxxxx 10xxxxxx
102		 *   11111000 10000xxx 10xxxxxx 10xxxxxx 10xxxxxx
103		 *   11111100 100000xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
104		 */
105		if (!utf_char && !((c & 0x7f) >> (7 - utf_count))) {
106		    utf_char = UCS_REPL;
107		}
108		utf_char <<= 6;
109		utf_char |= (c & 0x3f);
110		if ((utf_char >= 0xd800 &&
111		     utf_char <= 0xdfff) ||
112		    (utf_char == 0xfffe) ||
113		    (utf_char == HIDDEN_CHAR)) {
114		    utf_char = UCS_REPL;
115		}
116		utf_count--;
117		if (utf_count == 0) {
118#if !OPT_WIDER_ICHAR
119		    /* characters outside UCS-2 become UCS_REPL */
120		    if (utf_char > 0xffff) {
121			TRACE(("using replacement for %#x\n", utf_char));
122			utf_char = UCS_REPL;
123		    }
124#endif
125		    data->utf_data = (IChar) utf_char;
126		    data->utf_size = (i + 1);
127		    break;
128		}
129	    }
130	} else {
131	    /* We received a sequence start byte */
132	    if (utf_count > 0) {
133		data->utf_data = UCS_REPL;	/* prev. sequence incomplete */
134		data->utf_size = (i + 1);
135		break;
136	    }
137	    if (c < 0xe0) {
138		utf_count = 1;
139		utf_char = (c & 0x1f);
140		if (!(c & 0x1e)) {
141		    utf_char = UCS_REPL;	/* overlong sequence */
142		}
143	    } else if (c < 0xf0) {
144		utf_count = 2;
145		utf_char = (c & 0x0f);
146	    } else if (c < 0xf8) {
147		utf_count = 3;
148		utf_char = (c & 0x07);
149	    } else if (c < 0xfc) {
150		utf_count = 4;
151		utf_char = (c & 0x03);
152	    } else if (c < 0xfe) {
153		utf_count = 5;
154		utf_char = (c & 0x01);
155	    } else {
156		data->utf_data = UCS_REPL;
157		data->utf_size = (i + 1);
158		break;
159	    }
160	}
161    }
162#if OPT_TRACE > 1
163    TRACE(("UTF-8 char %04X [%d..%d]\n",
164	   data->utf_data,
165	   (int) (data->next - data->buffer),
166	   (int) (data->next - data->buffer + data->utf_size - 1)));
167#endif
168
169    return (data->utf_size != 0);
170}
171#endif
172
173int
174readPtyData(XtermWidget xw, PtySelect * select_mask, PtyData *data)
175{
176    TScreen *screen = TScreenOf(xw);
177    int size = 0;
178
179#ifdef VMS
180    if (*select_mask & pty_mask) {
181	trimPtyData(xw, data);
182	if (read_queue.flink != 0) {
183	    size = tt_read(data->next);
184	    if (size == 0) {
185		Panic("input: read returned zero\n", 0);
186	    }
187	} else {
188	    sys$hiber();
189	}
190    }
191#else /* !VMS */
192    if (FD_ISSET(screen->respond, select_mask)) {
193	int save_err;
194	trimPtyData(xw, data);
195
196	size = (int) read(screen->respond, (char *) data->last, (size_t) FRG_SIZE);
197	save_err = errno;
198#if (defined(i386) && defined(SVR4) && defined(sun)) || defined(__CYGWIN__)
199	/*
200	 * Yes, I know this is a majorly f*ugly hack, however it seems to
201	 * be necessary for Solaris x86.  DWH 11/15/94
202	 * Dunno why though..
203	 * (and now CYGWIN, alanh@xfree86.org 08/15/01
204	 */
205	if (size <= 0) {
206	    if (save_err == EIO || save_err == 0)
207		NormalExit();
208	    else if (!E_TEST(save_err))
209		Panic("input: read returned unexpected error (%d)\n", save_err);
210	    size = 0;
211	}
212#else /* !f*ugly */
213	if (size < 0) {
214	    if (save_err == EIO)
215		NormalExit();
216	    else if (!E_TEST(save_err))
217		Panic("input: read returned unexpected error (%d)\n", save_err);
218	    size = 0;
219	} else if (size == 0) {
220#if defined(__FreeBSD__)
221	    NormalExit();
222#else
223	    Panic("input: read returned zero\n", 0);
224#endif
225	}
226#endif /* f*ugly */
227    }
228#endif /* VMS */
229
230    if (size) {
231#if OPT_TRACE
232	int i;
233
234	TRACE(("read %d bytes from pty\n", size));
235	for (i = 0; i < size; i++) {
236	    if (!(i % 16))
237		TRACE(("%s", i ? "\n    " : "READ"));
238	    TRACE((" %02X", data->last[i]));
239	}
240	TRACE(("\n"));
241#endif
242	data->last += size;
243#ifdef ALLOWLOGGING
244	TScreenOf(term)->logstart = VTbuffer->next;
245#endif
246    }
247
248    return (size);
249}
250
251/*
252 * Return the next value from the input buffer.  Note that morePtyData() is
253 * always called before this function, so we can do the UTF-8 input conversion
254 * in that function and simply return the result here.
255 */
256#if OPT_WIDE_CHARS
257IChar
258nextPtyData(TScreen *screen, PtyData *data)
259{
260    IChar result;
261    if (screen->utf8_inparse) {
262	result = skipPtyData(data);
263    } else {
264	result = *((data)->next++);
265	if (!screen->output_eight_bits) {
266	    result = (IChar) (result & 0x7f);
267	}
268    }
269    TRACE2(("nextPtyData returns %#x\n", result));
270    return result;
271}
272
273/*
274 * Simply return the data and skip past it.
275 */
276IChar
277skipPtyData(PtyData *data)
278{
279    IChar result = data->utf_data;
280
281    data->next += data->utf_size;
282    data->utf_size = 0;
283
284    return result;
285}
286#endif
287
288#if OPT_WIDE_CHARS
289/*
290 * Called when UTF-8 mode has been turned on/off.
291 */
292void
293switchPtyData(TScreen *screen, int flag)
294{
295    if (screen->utf8_mode != flag) {
296	screen->utf8_mode = flag;
297	screen->utf8_inparse = (Boolean) (flag != 0);
298
299	TRACE(("turning UTF-8 mode %s\n", BtoS(flag)));
300	update_font_utf8_mode();
301    }
302}
303#endif
304
305/*
306 * Allocate a buffer.
307 */
308void
309initPtyData(PtyData **result)
310{
311    PtyData *data;
312
313    TRACE(("initPtyData given minBufSize %d, maxBufSize %d\n",
314	   FRG_SIZE, BUF_SIZE));
315
316    if (FRG_SIZE < 64)
317	FRG_SIZE = 64;
318    if (BUF_SIZE < FRG_SIZE)
319	BUF_SIZE = FRG_SIZE;
320    if (BUF_SIZE % FRG_SIZE)
321	BUF_SIZE = BUF_SIZE + FRG_SIZE - (BUF_SIZE % FRG_SIZE);
322
323    TRACE(("initPtyData using minBufSize %d, maxBufSize %d\n",
324	   FRG_SIZE, BUF_SIZE));
325
326    data = TypeXtMallocX(PtyData, (BUF_SIZE + FRG_SIZE));
327
328    memset(data, 0, sizeof(*data));
329    data->next = data->buffer;
330    data->last = data->buffer;
331    *result = data;
332}
333
334/*
335 * Initialize a buffer for the caller, using its data in 'next'.
336 */
337#if OPT_WIDE_CHARS
338PtyData *
339fakePtyData(PtyData *result, Char *next, Char *last)
340{
341    PtyData *data = result;
342
343    memset(data, 0, sizeof(*data));
344    data->next = next;
345    data->last = last;
346
347    return data;
348}
349#endif
350
351/*
352 * Remove used data by shifting the buffer down, to make room for more data,
353 * e.g., a continuation-read.
354 */
355void
356trimPtyData(XtermWidget xw GCC_UNUSED, PtyData *data)
357{
358    int i;
359
360    FlushLog(xw);
361
362    if (data->next != data->buffer) {
363	int n = (int) (data->last - data->next);
364
365	TRACE(("shifting buffer down by %d\n", n));
366	for (i = 0; i < n; ++i) {
367	    data->buffer[i] = data->next[i];
368	}
369	data->next = data->buffer;
370	data->last = data->next + n;
371    }
372
373}
374
375/*
376 * Insert new data into the input buffer so the next calls to morePtyData()
377 * and nextPtyData() will return that.
378 */
379void
380fillPtyData(XtermWidget xw, PtyData *data, const char *value, int length)
381{
382    int size;
383    int n;
384
385    /* remove the used portion of the buffer */
386    trimPtyData(xw, data);
387
388    VTbuffer->last += length;
389    size = (int) (VTbuffer->last - VTbuffer->next);
390
391    /* shift the unused portion up to make room */
392    for (n = size; n >= length; --n)
393	VTbuffer->next[n] = VTbuffer->next[n - length];
394
395    /* insert the new bytes to interpret */
396    for (n = 0; n < length; n++)
397	VTbuffer->next[n] = CharOf(value[n]);
398}
399
400#if OPT_WIDE_CHARS
401Char *
402convertToUTF8(Char *lp, unsigned c)
403{
404#define CH(n) (Char)((c) >> ((n) * 8))
405    if (c < 0x80) {
406	/*  0*******  */
407	*lp++ = (Char) CH(0);
408    } else if (c < 0x800) {
409	/*  110***** 10******  */
410	*lp++ = (Char) (0xc0 | (CH(0) >> 6) | ((CH(1) & 0x07) << 2));
411	*lp++ = (Char) (0x80 | (CH(0) & 0x3f));
412    } else if (c < 0x00010000) {
413	/*  1110**** 10****** 10******  */
414	*lp++ = (Char) (0xe0 | ((int) (CH(1) & 0xf0) >> 4));
415	*lp++ = (Char) (0x80 | (CH(0) >> 6) | ((CH(1) & 0x0f) << 2));
416	*lp++ = (Char) (0x80 | (CH(0) & 0x3f));
417    } else if (c < 0x00200000) {
418	*lp++ = (Char) (0xf0 | ((int) (CH(2) & 0x1f) >> 2));
419	*lp++ = (Char) (0x80 |
420			((int) (CH(1) & 0xf0) >> 4) |
421			((int) (CH(2) & 0x03) << 4));
422	*lp++ = (Char) (0x80 | (CH(0) >> 6) | ((CH(1) & 0x0f) << 2));
423	*lp++ = (Char) (0x80 | (CH(0) & 0x3f));
424    } else if (c < 0x04000000) {
425	*lp++ = (Char) (0xf8 | (CH(3) & 0x03));
426	*lp++ = (Char) (0x80 | (CH(2) >> 2));
427	*lp++ = (Char) (0x80 |
428			((int) (CH(1) & 0xf0) >> 4) |
429			((int) (CH(2) & 0x03) << 4));
430	*lp++ = (Char) (0x80 | (CH(0) >> 6) | ((CH(1) & 0x0f) << 2));
431	*lp++ = (Char) (0x80 | (CH(0) & 0x3f));
432    } else {
433	*lp++ = (Char) (0xfc | ((int) (CH(3) & 0x40) >> 6));
434	*lp++ = (Char) (0x80 | (CH(3) & 0x3f));
435	*lp++ = (Char) (0x80 | (CH(2) >> 2));
436	*lp++ = (Char) (0x80 | (CH(1) >> 4) | ((CH(2) & 0x03) << 4));
437	*lp++ = (Char) (0x80 | (CH(0) >> 6) | ((CH(1) & 0x0f) << 2));
438	*lp++ = (Char) (0x80 | (CH(0) & 0x3f));
439    }
440    return lp;
441#undef CH
442}
443
444/*
445 * Write data back to the PTY
446 */
447void
448writePtyData(int f, IChar *d, unsigned len)
449{
450    unsigned n = (len << 1);
451
452    if (VTbuffer->write_len <= len) {
453	VTbuffer->write_len = n;
454	VTbuffer->write_buf = (Char *) XtRealloc((char *)
455						 VTbuffer->write_buf, VTbuffer->write_len);
456    }
457
458    for (n = 0; n < len; n++)
459	VTbuffer->write_buf[n] = (Char) d[n];
460
461    TRACE(("writePtyData %d:%s\n", n,
462	   visibleChars(VTbuffer->write_buf, n)));
463    v_write(f, VTbuffer->write_buf, n);
464}
465#endif /* OPT_WIDE_CHARS */
466
467#ifdef NO_LEAKS
468void
469noleaks_ptydata(void)
470{
471    if (VTbuffer != 0) {
472#if OPT_WIDE_CHARS
473	if (VTbuffer->write_buf != 0)
474	    free(VTbuffer->write_buf);
475#endif
476	free(VTbuffer);
477	VTbuffer = 0;
478    }
479}
480#endif
481