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