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