diff options
Diffstat (limited to 'ptydata.c')
-rw-r--r-- | ptydata.c | 510 |
1 files changed, 510 insertions, 0 deletions
diff --git a/ptydata.c b/ptydata.c new file mode 100644 index 0000000..1fafe41 --- /dev/null +++ b/ptydata.c @@ -0,0 +1,510 @@ +/* $XTermId: ptydata.c,v 1.101 2011/09/11 14:59:38 tom Exp $ */ + +/* + * Copyright 1999-2010,2011 by Thomas E. Dickey + * + * All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name(s) of the above copyright + * holders shall not be used in advertising or otherwise to promote the + * sale, use or other dealings in this Software without prior written + * authorization. + */ + +#include <data.h> + +#if OPT_WIDE_CHARS +#include <menu.h> +#endif + +/* + * Check for both EAGAIN and EWOULDBLOCK, because some supposedly POSIX + * systems are broken and return EWOULDBLOCK when they should return EAGAIN. + * Note that this macro may evaluate its argument more than once. + */ +#if defined(EAGAIN) && defined(EWOULDBLOCK) +#define E_TEST(err) ((err) == EAGAIN || (err) == EWOULDBLOCK) +#else +#ifdef EAGAIN +#define E_TEST(err) ((err) == EAGAIN) +#else +#define E_TEST(err) ((err) == EWOULDBLOCK) +#endif +#endif + +#if OPT_WIDE_CHARS +/* + * Convert the 8-bit codes in data->buffer[] into Unicode in data->utf_data. + * The number of bytes converted will be nonzero iff there is data. + */ +Bool +decodeUtf8(PtyData * data) +{ + int i; + int length = (int) (data->last - data->next); + int utf_count = 0; + unsigned utf_char = 0; + + data->utf_size = 0; + for (i = 0; i < length; i++) { + unsigned c = data->next[i]; + + /* Combine UTF-8 into Unicode */ + if (c < 0x80) { + /* We received an ASCII character */ + if (utf_count > 0) { + data->utf_data = UCS_REPL; /* prev. sequence incomplete */ + data->utf_size = i; + } else { + data->utf_data = (IChar) c; + data->utf_size = 1; + } + break; + } else if (c < 0xc0) { + /* We received a continuation byte */ + if (utf_count < 1) { + /* + * We received a continuation byte before receiving a sequence + * state. Or an attempt to use a C1 control string. Either + * way, it is mapped to the replacement character. + */ + data->utf_data = UCS_REPL; /* ... unexpectedly */ + data->utf_size = (i + 1); + break; + } else { + /* Check for overlong UTF-8 sequences for which a shorter + * encoding would exist and replace them with UCS_REPL. + * An overlong UTF-8 sequence can have any of the following + * forms: + * 1100000x 10xxxxxx + * 11100000 100xxxxx 10xxxxxx + * 11110000 1000xxxx 10xxxxxx 10xxxxxx + * 11111000 10000xxx 10xxxxxx 10xxxxxx 10xxxxxx + * 11111100 100000xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if (!utf_char && !((c & 0x7f) >> (7 - utf_count))) { + utf_char = UCS_REPL; + } + utf_char <<= 6; + utf_char |= (c & 0x3f); + if ((utf_char >= 0xd800 && + utf_char <= 0xdfff) || + (utf_char == 0xfffe) || + (utf_char == HIDDEN_CHAR)) { + utf_char = UCS_REPL; + } + utf_count--; + if (utf_count == 0) { +#if !OPT_WIDER_ICHAR + /* characters outside UCS-2 become UCS_REPL */ + if (utf_char > 0xffff) { + TRACE(("using replacement for %#x\n", utf_char)); + utf_char = UCS_REPL; + } +#endif + data->utf_data = (IChar) utf_char; + data->utf_size = (i + 1); + break; + } + } + } else { + /* We received a sequence start byte */ + if (utf_count > 0) { + data->utf_data = UCS_REPL; /* prev. sequence incomplete */ + data->utf_size = (i + 1); + break; + } + if (c < 0xe0) { + utf_count = 1; + utf_char = (c & 0x1f); + if (!(c & 0x1e)) { + utf_char = UCS_REPL; /* overlong sequence */ + } + } else if (c < 0xf0) { + utf_count = 2; + utf_char = (c & 0x0f); + } else if (c < 0xf8) { + utf_count = 3; + utf_char = (c & 0x07); + } else if (c < 0xfc) { + utf_count = 4; + utf_char = (c & 0x03); + } else if (c < 0xfe) { + utf_count = 5; + utf_char = (c & 0x01); + } else { + data->utf_data = UCS_REPL; + data->utf_size = (i + 1); + break; + } + } + } +#if OPT_TRACE > 1 + TRACE(("UTF-8 char %04X [%d..%d]\n", + data->utf_data, + data->next - data->buffer, + data->next - data->buffer + data->utf_size - 1)); +#endif + + return (data->utf_size != 0); +} +#endif + +int +readPtyData(XtermWidget xw, PtySelect * select_mask, PtyData * data) +{ + TScreen *screen = TScreenOf(xw); + int size = 0; + +#ifdef VMS + if (*select_mask & pty_mask) { + trimPtyData(xw, data); + if (read_queue.flink != 0) { + size = tt_read(data->next); + if (size == 0) { + Panic("input: read returned zero\n", 0); + } + } else { + sys$hiber(); + } + } +#else /* !VMS */ + if (FD_ISSET(screen->respond, select_mask)) { + int save_err; + trimPtyData(xw, data); + + size = (int) read(screen->respond, (char *) data->last, (size_t) FRG_SIZE); + save_err = errno; +#if (defined(i386) && defined(SVR4) && defined(sun)) || defined(__CYGWIN__) + /* + * Yes, I know this is a majorly f*ugly hack, however it seems to + * be necessary for Solaris x86. DWH 11/15/94 + * Dunno why though.. + * (and now CYGWIN, alanh@xfree86.org 08/15/01 + */ + if (size <= 0) { + if (save_err == EIO || save_err == 0) + Cleanup(0); + else if (!E_TEST(save_err)) + Panic("input: read returned unexpected error (%d)\n", save_err); + size = 0; + } +#else /* !f*ugly */ + if (size < 0) { + if (save_err == EIO) + Cleanup(0); + else if (!E_TEST(save_err)) + Panic("input: read returned unexpected error (%d)\n", save_err); + size = 0; + } else if (size == 0) { +#if defined(__UNIXOS2__) || defined(__FreeBSD__) + Cleanup(0); +#else + Panic("input: read returned zero\n", 0); +#endif + } +#endif /* f*ugly */ + } +#endif /* VMS */ + + if (size) { +#if OPT_TRACE + int i; + + TRACE(("read %d bytes from pty\n", size)); + for (i = 0; i < size; i++) { + if (!(i % 16)) + TRACE(("%s", i ? "\n " : "READ")); + TRACE((" %02X", data->last[i])); + } + TRACE(("\n")); +#endif + data->last += size; +#ifdef ALLOWLOGGING + TScreenOf(term)->logstart = VTbuffer->next; +#endif + } + + return (size); +} + +/* + * Return the next value from the input buffer. Note that morePtyData() is + * always called before this function, so we can do the UTF-8 input conversion + * in that function and simply return the result here. + */ +#if OPT_WIDE_CHARS +IChar +nextPtyData(TScreen * screen, PtyData * data) +{ + IChar result; + if (screen->utf8_inparse) { + result = skipPtyData(data); + } else { + result = *((data)->next++); + if (!screen->output_eight_bits) { + result = (IChar) (result & 0x7f); + } + } + TRACE2(("nextPtyData returns %#x\n", result)); + return result; +} + +/* + * Simply return the data and skip past it. + */ +IChar +skipPtyData(PtyData * data) +{ + IChar result = data->utf_data; + + data->next += data->utf_size; + data->utf_size = 0; + + return result; +} +#endif + +#if OPT_WIDE_CHARS +/* + * Called when UTF-8 mode has been turned on/off. + */ +void +switchPtyData(TScreen * screen, int flag) +{ + if (screen->utf8_mode != flag) { + screen->utf8_mode = flag; + screen->utf8_inparse = (Boolean) (flag != 0); + + TRACE(("turning UTF-8 mode %s\n", BtoS(flag))); + update_font_utf8_mode(); + } +} +#endif + +/* + * Allocate a buffer. + */ +void +initPtyData(PtyData ** result) +{ + PtyData *data; + + TRACE(("initPtyData given minBufSize %d, maxBufSize %d\n", + FRG_SIZE, BUF_SIZE)); + + if (FRG_SIZE < 64) + FRG_SIZE = 64; + if (BUF_SIZE < FRG_SIZE) + BUF_SIZE = FRG_SIZE; + if (BUF_SIZE % FRG_SIZE) + BUF_SIZE = BUF_SIZE + FRG_SIZE - (BUF_SIZE % FRG_SIZE); + + TRACE(("initPtyData using minBufSize %d, maxBufSize %d\n", + FRG_SIZE, BUF_SIZE)); + + data = TypeXtMallocX(PtyData, (BUF_SIZE + FRG_SIZE)); + + memset(data, 0, sizeof(*data)); + data->next = data->buffer; + data->last = data->buffer; + *result = data; +} + +/* + * Initialize a buffer for the caller, using its data in 'next'. + */ +#if OPT_WIDE_CHARS +PtyData * +fakePtyData(PtyData * result, Char * next, Char * last) +{ + PtyData *data = result; + + memset(data, 0, sizeof(*data)); + data->next = next; + data->last = last; + + return data; +} +#endif + +/* + * Remove used data by shifting the buffer down, to make room for more data, + * e.g., a continuation-read. + */ +void +trimPtyData(XtermWidget xw GCC_UNUSED, PtyData * data) +{ + int i; + + FlushLog(xw); + + if (data->next != data->buffer) { + int n = (int) (data->last - data->next); + + TRACE(("shifting buffer down by %d\n", n)); + for (i = 0; i < n; ++i) { + data->buffer[i] = data->next[i]; + } + data->next = data->buffer; + data->last = data->next + n; + } + +} + +/* + * Insert new data into the input buffer so the next calls to morePtyData() + * and nextPtyData() will return that. + */ +void +fillPtyData(XtermWidget xw, PtyData * data, const char *value, int length) +{ + int size; + int n; + + /* remove the used portion of the buffer */ + trimPtyData(xw, data); + + VTbuffer->last += length; + size = (int) (VTbuffer->last - VTbuffer->next); + + /* shift the unused portion up to make room */ + for (n = size; n >= length; --n) + VTbuffer->next[n] = VTbuffer->next[n - length]; + + /* insert the new bytes to interpret */ + for (n = 0; n < length; n++) + VTbuffer->next[n] = CharOf(value[n]); +} + +#if OPT_WIDE_CHARS +Char * +convertToUTF8(Char * lp, unsigned c) +{ +#define CH(n) (Char)((c) >> ((n) * 8)) + if (c < 0x80) { + /* 0******* */ + *lp++ = (Char) CH(0); + } else if (c < 0x800) { + /* 110***** 10****** */ + *lp++ = (Char) (0xc0 | (CH(0) >> 6) | ((CH(1) & 0x07) << 2)); + *lp++ = (Char) (0x80 | (CH(0) & 0x3f)); + } else if (c < 0x00010000) { + /* 1110**** 10****** 10****** */ + *lp++ = (Char) (0xe0 | ((int) (CH(1) & 0xf0) >> 4)); + *lp++ = (Char) (0x80 | (CH(0) >> 6) | ((CH(1) & 0x0f) << 2)); + *lp++ = (Char) (0x80 | (CH(0) & 0x3f)); + } else if (c < 0x00200000) { + *lp++ = (Char) (0xf0 | ((int) (CH(2) & 0x1f) >> 2)); + *lp++ = (Char) (0x80 | + ((int) (CH(1) & 0xf0) >> 4) | + ((int) (CH(2) & 0x03) << 4)); + *lp++ = (Char) (0x80 | (CH(0) >> 6) | ((CH(1) & 0x0f) << 2)); + *lp++ = (Char) (0x80 | (CH(0) & 0x3f)); + } else if (c < 0x04000000) { + *lp++ = (Char) (0xf8 | (CH(3) & 0x03)); + *lp++ = (Char) (0x80 | (CH(2) >> 2)); + *lp++ = (Char) (0x80 | + ((int) (CH(1) & 0xf0) >> 4) | + ((int) (CH(2) & 0x03) << 4)); + *lp++ = (Char) (0x80 | (CH(0) >> 6) | ((CH(1) & 0x0f) << 2)); + *lp++ = (Char) (0x80 | (CH(0) & 0x3f)); + } else { + *lp++ = (Char) (0xfc | ((int) (CH(3) & 0x40) >> 6)); + *lp++ = (Char) (0x80 | (CH(3) & 0x3f)); + *lp++ = (Char) (0x80 | (CH(2) >> 2)); + *lp++ = (Char) (0x80 | (CH(1) >> 4) | ((CH(2) & 0x03) << 4)); + *lp++ = (Char) (0x80 | (CH(0) >> 6) | ((CH(1) & 0x0f) << 2)); + *lp++ = (Char) (0x80 | (CH(0) & 0x3f)); + } + return lp; +#undef CH +} + +/* + * Write data back to the PTY + */ +void +writePtyData(int f, IChar * d, unsigned len) +{ + unsigned n = (len << 1); + + if (VTbuffer->write_len <= len) { + VTbuffer->write_len = n; + VTbuffer->write_buf = (Char *) XtRealloc((char *) + VTbuffer->write_buf, VTbuffer->write_len); + } + + for (n = 0; n < len; n++) + VTbuffer->write_buf[n] = (Char) d[n]; + + TRACE(("writePtyData %d:%s\n", n, + visibleChars(VTbuffer->write_buf, n))); + v_write(f, VTbuffer->write_buf, n); +} +#endif /* OPT_WIDE_CHARS */ + +#ifdef NO_LEAKS +void +noleaks_ptydata(void) +{ + if (VTbuffer != 0) { +#if OPT_WIDE_CHARS + if (VTbuffer->write_buf != 0) + free(VTbuffer->write_buf); +#endif + free(VTbuffer); + VTbuffer = 0; + } +} +#endif + +#if 0 +void +test_ptydata(void) +{ + PtyData *data; + unsigned code; + + initPtyData(&data); + TRACE(("test_ptydata\n")); + for (code = 0; code <= 0x7fffffff; ++code) { + int use_size; + + memset(data, 0, sizeof(*data)); + data->next = data->buffer; + data->last = convertToUTF8(data->buffer, code); + + use_size = (data->last - data->next); + + if (decodeUtf8(data)) { + if (code != data->utf_data) { + TRACE(("code %#x ->%#x\n", code, data->utf_data)); + } else if (use_size != data->utf_size) { + TRACE(("size %#x %d->%d\n", code, use_size, data->utf_size)); + } + } else { + TRACE(("fail %#x\n", code)); + } + } +} +#endif |