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