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