1 /* $XTermId: ptydata.c,v 1.163 2024/12/01 23:48:07 tom Exp $ */ 2 3 /* 4 * Copyright 1999-2023,2024 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 #include <wcwidth.h> 38 #endif 39 40 #ifdef TEST_DRIVER 41 #undef TRACE 42 #define TRACE(p) if (1) printf p 43 #undef TRACE2 44 #define TRACE2(p) if (0) printf p 45 #define visibleChars(buf, len) "buffer" 46 #endif 47 48 /* 49 * Check for both EAGAIN and EWOULDBLOCK, because some supposedly POSIX 50 * systems are broken and return EWOULDBLOCK when they should return EAGAIN. 51 * Note that this macro may evaluate its argument more than once. 52 */ 53 #if defined(EAGAIN) && defined(EWOULDBLOCK) 54 #define E_TEST(err) ((err) == EAGAIN || (err) == EWOULDBLOCK) 55 #else 56 #ifdef EAGAIN 57 #define E_TEST(err) ((err) == EAGAIN) 58 #else 59 #define E_TEST(err) ((err) == EWOULDBLOCK) 60 #endif 61 #endif 62 63 #if OPT_WIDE_CHARS 64 /* 65 * Convert the 8-bit codes in data->buffer[] into Unicode in data->utf_data. 66 * The number of bytes converted will be nonzero iff there is data. 67 */ 68 Bool 69 decodeUtf8(TScreen *screen, PtyData *data) 70 { 71 size_t i; 72 size_t length = (size_t) (data->last - data->next); 73 int utf_count = 0; 74 unsigned utf_char = 0; 75 76 data->utf_size = 0; 77 for (i = 0; i < length; i++) { 78 unsigned c = data->next[i]; 79 80 /* Combine UTF-8 into Unicode */ 81 if (c < 0x80) { 82 /* We received an ASCII character */ 83 if (utf_count > 0) { 84 data->utf_data = UCS_REPL; /* prev. sequence incomplete */ 85 data->utf_size = i; 86 } else { 87 data->utf_data = (IChar) c; 88 data->utf_size = 1; 89 } 90 break; 91 } else if (screen->vt100_graphics 92 && (c < 0x100) 93 && (utf_count == 0) 94 && screen->gsets[(int) screen->curgr] != nrc_ASCII) { 95 data->utf_data = (IChar) c; 96 data->utf_size = 1; 97 break; 98 } else if (c < 0xc0) { 99 /* We received a continuation byte */ 100 if (utf_count < 1) { 101 if (screen->c1_printable) { 102 data->utf_data = (IChar) c; 103 } else if ((i + 1) < length 104 && data->next[i + 1] > 0x20 105 && data->next[i + 1] < 0x80) { 106 /* 107 * Allow for C1 control string if the next byte is 108 * available for inspection. 109 */ 110 data->utf_data = (IChar) c; 111 } else { 112 /* 113 * We received a continuation byte before receiving a 114 * sequence state, or a failed attempt to use a C1 control 115 * string. 116 */ 117 data->utf_data = (IChar) UCS_REPL; 118 } 119 data->utf_size = (i + 1); 120 break; 121 } else if (screen->utf8_weblike 122 && (utf_count == 3 123 && utf_char == 0x04 124 && c >= 0x90)) { 125 /* The encoding would form a code point beyond U+10FFFF. */ 126 data->utf_size = i; 127 data->utf_data = UCS_REPL; 128 break; 129 } else if (screen->utf8_weblike 130 && (utf_count == 2 131 && utf_char == 0x0d 132 && c >= 0xa0)) { 133 /* The encoding would form a surrogate code point. */ 134 data->utf_size = i; 135 data->utf_data = UCS_REPL; 136 break; 137 } else { 138 /* Check for overlong UTF-8 sequences for which a shorter 139 * encoding would exist and replace them with UCS_REPL. 140 * An overlong UTF-8 sequence can have any of the following 141 * forms: 142 * 1100000x 10xxxxxx 143 * 11100000 100xxxxx 10xxxxxx 144 * 11110000 1000xxxx 10xxxxxx 10xxxxxx 145 * 11111000 10000xxx 10xxxxxx 10xxxxxx 10xxxxxx 146 * 11111100 100000xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 147 */ 148 if (!utf_char && !((c & 0x7f) >> (7 - utf_count))) { 149 if (screen->utf8_weblike) { 150 /* overlong sequence continued */ 151 data->utf_data = UCS_REPL; 152 data->utf_size = i; 153 break; 154 } else { 155 utf_char = UCS_REPL; 156 } 157 } 158 utf_char <<= 6; 159 utf_char |= (c & 0x3f); 160 if ((utf_char >= 0xd800 && 161 utf_char <= 0xdfff) || 162 (utf_char == 0xfffe) || 163 (utf_char == HIDDEN_CHAR)) { 164 utf_char = UCS_REPL; 165 } 166 utf_count--; 167 if (utf_count == 0) { 168 #if !OPT_WIDER_ICHAR 169 /* characters outside UCS-2 become UCS_REPL */ 170 if (utf_char > NARROW_ICHAR) { 171 TRACE(("using replacement for %#x\n", utf_char)); 172 utf_char = UCS_REPL; 173 } 174 #endif 175 data->utf_data = (IChar) utf_char; 176 data->utf_size = (i + 1); 177 break; 178 } 179 } 180 } else { 181 /* We received a sequence start byte */ 182 if (utf_count > 0) { 183 /* previous sequence is incomplete */ 184 data->utf_data = UCS_REPL; 185 data->utf_size = i; 186 break; 187 } 188 if (screen->utf8_weblike) { 189 if (c < 0xe0) { 190 if (!(c & 0x1e)) { 191 /* overlong sequence start */ 192 data->utf_data = UCS_REPL; 193 data->utf_size = (i + 1); 194 break; 195 } 196 utf_count = 1; 197 utf_char = (c & 0x1f); 198 } else if (c < 0xf0) { 199 utf_count = 2; 200 utf_char = (c & 0x0f); 201 } else if (c < 0xf5) { 202 utf_count = 3; 203 utf_char = (c & 0x07); 204 } else { 205 data->utf_data = UCS_REPL; 206 data->utf_size = (i + 1); 207 break; 208 } 209 } else { 210 if (c < 0xe0) { 211 utf_count = 1; 212 utf_char = (c & 0x1f); 213 if (!(c & 0x1e)) { 214 /* overlong sequence */ 215 utf_char = UCS_REPL; 216 } 217 } else if (c < 0xf0) { 218 utf_count = 2; 219 utf_char = (c & 0x0f); 220 } else if (c < 0xf8) { 221 utf_count = 3; 222 utf_char = (c & 0x07); 223 } else if (c < 0xfc) { 224 utf_count = 4; 225 utf_char = (c & 0x03); 226 } else if (c < 0xfe) { 227 utf_count = 5; 228 utf_char = (c & 0x01); 229 } else { 230 data->utf_data = UCS_REPL; 231 data->utf_size = (i + 1); 232 break; 233 } 234 } 235 } 236 } 237 #if OPT_TRACE > 1 238 TRACE(("UTF-8 char %04X [%lu..%lu]\n", 239 data->utf_data, 240 (unsigned long) (data->next - data->buffer), 241 (unsigned long) (data->next - data->buffer + data->utf_size - 1))); 242 #endif 243 244 return (data->utf_size != 0); 245 } 246 #endif 247 248 int 249 readPtyData(XtermWidget xw, PtySelect * select_mask, PtyData *data) 250 { 251 TScreen *screen = TScreenOf(xw); 252 int size = 0; 253 254 if (FD_ISSET(screen->respond, select_mask)) { 255 int save_err; 256 trimPtyData(xw, data); 257 258 size = (int) read(screen->respond, (char *) data->last, (size_t) FRG_SIZE); 259 save_err = errno; 260 #if (defined(i386) && defined(SVR4) && defined(sun)) || defined(__CYGWIN__) 261 /* 262 * Yes, I know this is a majorly f*ugly hack, however it seems to 263 * be necessary for Solaris x86. DWH 11/15/94 264 * Dunno why though.. 265 * (and now CYGWIN, alanh (at) xfree86.org 08/15/01 266 */ 267 if (size <= 0) { 268 if (save_err == EIO || save_err == 0) 269 NormalExit(); 270 else if (!E_TEST(save_err)) 271 Panic("input: read returned unexpected error (%d)\n", save_err); 272 size = 0; 273 } 274 #else /* !f*ugly */ 275 if (size < 0) { 276 if (save_err == EIO) 277 NormalExit(); 278 else if (!E_TEST(save_err)) 279 Panic("input: read returned unexpected error (%d)\n", save_err); 280 size = 0; 281 } else if (size == 0) { 282 #if defined(__FreeBSD__) 283 NormalExit(); 284 #else 285 Panic("input: read returned zero\n", 0); 286 #endif 287 } 288 #endif /* f*ugly */ 289 } 290 291 if (size) { 292 #if OPT_TRACE 293 int i; 294 295 TRACE(("read %d bytes from pty\n", size)); 296 for (i = 0; i < size; i++) { 297 if (!(i % 16)) 298 TRACE(("%s", i ? "\n " : "READ")); 299 TRACE((" %02X", data->last[i])); 300 } 301 TRACE(("\n")); 302 #endif 303 data->last += size; 304 #ifdef ALLOWLOGGING 305 TScreenOf(term)->logstart = VTbuffer->next; 306 #endif 307 } 308 309 return (size); 310 } 311 312 /* 313 * Return the next value from the input buffer. Note that morePtyData() is 314 * always called before this function, so we can do the UTF-8 input conversion 315 * in that function and simply return the result here. 316 */ 317 #if OPT_WIDE_CHARS 318 IChar 319 nextPtyData(TScreen *screen, PtyData *data) 320 { 321 IChar result; 322 if (screen->utf8_inparse) { 323 skipPtyData(data, result); 324 } else { 325 result = *((data)->next++); 326 if (!screen->output_eight_bits) { 327 result = (IChar) (result & 0x7f); 328 } 329 } 330 TRACE2(("nextPtyData returns %#x\n", result)); 331 return result; 332 } 333 #endif 334 335 #if OPT_WIDE_CHARS 336 /* 337 * Called when UTF-8 mode has been turned on/off. 338 */ 339 void 340 switchPtyData(TScreen *screen, int flag) 341 { 342 if (screen->utf8_mode != flag) { 343 screen->utf8_mode = flag; 344 screen->utf8_inparse = (Boolean) (flag != 0); 345 mk_wcwidth_init(screen->utf8_mode); 346 347 TRACE(("turning UTF-8 mode %s\n", BtoS(flag))); 348 if (flag) { 349 saveCharsets(screen, screen->ansi_save_gsets); 350 resetCharsets(screen); 351 } else { 352 restoreCharsets(screen, screen->ansi_save_gsets); 353 } 354 update_font_utf8_mode(); 355 } 356 } 357 #endif 358 359 /* 360 * Allocate a buffer. 361 */ 362 void 363 initPtyData(PtyData **result) 364 { 365 PtyData *data; 366 367 TRACE2(("initPtyData given minBufSize %d, maxBufSize %d\n", 368 FRG_SIZE, BUF_SIZE)); 369 370 if (FRG_SIZE < 64) 371 FRG_SIZE = 64; 372 if (BUF_SIZE < FRG_SIZE) 373 BUF_SIZE = FRG_SIZE; 374 if (BUF_SIZE % FRG_SIZE) 375 BUF_SIZE = BUF_SIZE + FRG_SIZE - (BUF_SIZE % FRG_SIZE); 376 377 TRACE2(("initPtyData using minBufSize %d, maxBufSize %d\n", 378 FRG_SIZE, BUF_SIZE)); 379 380 data = TypeXtMallocX(PtyData, (BUF_SIZE + FRG_SIZE)); 381 382 memset(data, 0, sizeof(*data)); 383 data->next = data->buffer; 384 data->last = data->buffer; 385 *result = data; 386 } 387 388 /* 389 * Initialize a buffer for the caller, using its data in 'next'. 390 */ 391 #if OPT_WIDE_CHARS 392 PtyData * 393 fakePtyData(PtyData *result, Char *next, Char *last) 394 { 395 PtyData *data = result; 396 397 memset(data, 0, sizeof(*data)); 398 data->next = next; 399 data->last = last; 400 401 return data; 402 } 403 #endif 404 405 /* 406 * Remove used data by shifting the buffer down, to make room for more data, 407 * e.g., a continuation-read. 408 */ 409 void 410 trimPtyData(XtermWidget xw, PtyData *data) 411 { 412 (void) xw; 413 FlushLog(xw); 414 415 if (data->next != data->buffer) { 416 size_t i; 417 size_t n = (size_t) (data->last - data->next); 418 419 TRACE(("shifting buffer down by %lu\n", (unsigned long) n)); 420 for (i = 0; i < n; ++i) { 421 data->buffer[i] = data->next[i]; 422 } 423 data->next = data->buffer; 424 data->last = data->next + n; 425 } 426 427 } 428 429 /* 430 * Insert new data into the input buffer so the next calls to morePtyData() 431 * and nextPtyData() will return that. 432 */ 433 void 434 fillPtyData(XtermWidget xw, PtyData *data, const char *value, size_t length) 435 { 436 size_t size; 437 size_t n; 438 439 /* remove the used portion of the buffer */ 440 trimPtyData(xw, data); 441 442 VTbuffer->last += length; 443 size = (size_t) (VTbuffer->last - VTbuffer->next); 444 445 /* shift the unused portion up to make room */ 446 for (n = size; n >= length; --n) 447 VTbuffer->next[n] = VTbuffer->next[n - length]; 448 449 /* insert the new bytes to interpret */ 450 for (n = 0; n < length; n++) 451 VTbuffer->next[n] = CharOf(value[n]); 452 } 453 454 #if OPT_WIDE_CHARS 455 /* 456 * Convert an ISO-8859-1 code 'c' to UTF-8, storing the result in the target 457 * 'lp', and returning a pointer past the converted character. 458 */ 459 Char * 460 convertToUTF8(Char *lp, unsigned c) 461 { 462 #define CH(n) (Char)((c) >> ((n) * 8)) 463 if (c < 0x80) { 464 /* 0******* */ 465 *lp++ = (Char) CH(0); 466 } else if (c < 0x800) { 467 /* 110***** 10****** */ 468 *lp++ = (Char) (0xc0 | (CH(0) >> 6) | ((CH(1) & 0x07) << 2)); 469 *lp++ = (Char) (0x80 | (CH(0) & 0x3f)); 470 } else if (c < 0x00010000) { 471 /* 1110**** 10****** 10****** */ 472 *lp++ = (Char) (0xe0 | ((int) (CH(1) & 0xf0) >> 4)); 473 *lp++ = (Char) (0x80 | (CH(0) >> 6) | ((CH(1) & 0x0f) << 2)); 474 *lp++ = (Char) (0x80 | (CH(0) & 0x3f)); 475 } else if (c < 0x00200000) { 476 *lp++ = (Char) (0xf0 | ((int) (CH(2) & 0x1f) >> 2)); 477 *lp++ = (Char) (0x80 | 478 ((int) (CH(1) & 0xf0) >> 4) | 479 ((int) (CH(2) & 0x03) << 4)); 480 *lp++ = (Char) (0x80 | (CH(0) >> 6) | ((CH(1) & 0x0f) << 2)); 481 *lp++ = (Char) (0x80 | (CH(0) & 0x3f)); 482 } else if (c < 0x04000000) { 483 *lp++ = (Char) (0xf8 | (CH(3) & 0x03)); 484 *lp++ = (Char) (0x80 | (CH(2) >> 2)); 485 *lp++ = (Char) (0x80 | 486 ((int) (CH(1) & 0xf0) >> 4) | 487 ((int) (CH(2) & 0x03) << 4)); 488 *lp++ = (Char) (0x80 | (CH(0) >> 6) | ((CH(1) & 0x0f) << 2)); 489 *lp++ = (Char) (0x80 | (CH(0) & 0x3f)); 490 } else { 491 *lp++ = (Char) (0xfc | ((int) (CH(3) & 0x40) >> 6)); 492 *lp++ = (Char) (0x80 | (CH(3) & 0x3f)); 493 *lp++ = (Char) (0x80 | (CH(2) >> 2)); 494 *lp++ = (Char) (0x80 | (CH(1) >> 4) | ((CH(2) & 0x03) << 4)); 495 *lp++ = (Char) (0x80 | (CH(0) >> 6) | ((CH(1) & 0x0f) << 2)); 496 *lp++ = (Char) (0x80 | (CH(0) & 0x3f)); 497 } 498 return lp; 499 #undef CH 500 } 501 502 /* 503 * Convert a UTF-8 multibyte character to an Unicode value, returning a pointer 504 * past the converted UTF-8 input. The first 256 values align with ISO-8859-1, 505 * making it possible to use this to convert to Latin-1. 506 * 507 * If the conversion fails, return null. 508 */ 509 Char * 510 convertFromUTF8(Char *lp, unsigned *cp) 511 { 512 int want; 513 514 /* 515 * Find the number of bytes we will need from the source. 516 */ 517 if ((*lp & 0x80) == 0) { 518 want = 1; 519 } else if ((*lp & 0xe0) == 0xc0) { 520 want = 2; 521 } else if ((*lp & 0xf0) == 0xe0) { 522 want = 3; 523 } else if ((*lp & 0xf8) == 0xf0) { 524 want = 4; 525 } else if ((*lp & 0xfc) == 0xf8) { 526 want = 5; 527 } else if ((*lp & 0xfe) == 0xfc) { 528 want = 6; 529 } else { 530 want = 0; 531 } 532 533 if (want) { 534 int have = 1; 535 536 while (lp[have] != '\0') { 537 if ((lp[have] & 0xc0) != 0x80) 538 break; 539 ++have; 540 } 541 if (want == have) { 542 unsigned mask = 0; 543 int j; 544 int shift = 0; 545 546 *cp = 0; 547 switch (want) { 548 case 1: 549 mask = (*lp); 550 break; 551 case 2: 552 mask = (*lp & 0x1f); 553 break; 554 case 3: 555 mask = (*lp & 0x0f); 556 break; 557 case 4: 558 mask = (*lp & 0x07); 559 break; 560 case 5: 561 mask = (*lp & 0x03); 562 break; 563 case 6: 564 mask = (*lp & 0x01); 565 break; 566 default: 567 mask = 0; 568 break; 569 } 570 571 for (j = 1; j < want; j++) { 572 *cp |= (unsigned) ((lp[want - j] & 0x3f) << shift); 573 shift += 6; 574 } 575 *cp |= mask << shift; 576 lp += want; 577 } else { 578 *cp = BAD_ASCII; 579 lp = NULL; 580 } 581 } else { 582 *cp = BAD_ASCII; 583 lp = NULL; 584 } 585 return lp; 586 } 587 588 /* 589 * Returns true if the entire string is valid UTF-8. 590 */ 591 Boolean 592 isValidUTF8(Char *lp) 593 { 594 Boolean result = True; 595 while (*lp) { 596 unsigned ch; 597 Char *next = convertFromUTF8(lp, &ch); 598 if (next == NULL || ch == 0) { 599 result = False; 600 break; 601 } 602 lp = next; 603 } 604 return result; 605 } 606 607 /* 608 * Write data back to the PTY 609 */ 610 void 611 writePtyData(int f, IChar *d, size_t len) 612 { 613 size_t n = (len << 1); 614 615 if (VTbuffer->write_len <= len) { 616 VTbuffer->write_len = n; 617 VTbuffer->write_buf = realloc(VTbuffer->write_buf, VTbuffer->write_len); 618 } 619 620 for (n = 0; n < len; n++) 621 VTbuffer->write_buf[n] = (Char) d[n]; 622 623 TRACE(("writePtyData %lu:%s\n", (unsigned long) n, 624 visibleChars(VTbuffer->write_buf, n))); 625 v_write(f, VTbuffer->write_buf, n); 626 } 627 #endif /* OPT_WIDE_CHARS */ 628 629 #ifdef NO_LEAKS 630 void 631 noleaks_ptydata(void) 632 { 633 if (VTbuffer != NULL) { 634 #if OPT_WIDE_CHARS 635 free(VTbuffer->write_buf); 636 #endif 637 FreeAndNull(VTbuffer); 638 } 639 } 640 #endif 641 642 #ifdef TEST_DRIVER 643 644 #include "data.c" 645 646 void 647 NormalExit(void) 648 { 649 fprintf(stderr, "NormalExit!\n"); 650 exit(EXIT_SUCCESS); 651 } 652 653 void 654 Panic(const char *s, int a) 655 { 656 (void) s; 657 (void) a; 658 fprintf(stderr, "Panic!\n"); 659 exit(EXIT_FAILURE); 660 } 661 662 #if OPT_WIDE_CHARS 663 void 664 saveCharsets(TScreen *screen, DECNRCM_codes * target) 665 { 666 (void) screen; 667 (void) target; 668 } 669 670 void 671 restoreCharsets(TScreen *screen, const DECNRCM_codes * target) 672 { 673 (void) screen; 674 (void) target; 675 } 676 677 void 678 resetCharsets(TScreen *screen) 679 { 680 (void) screen; 681 } 682 683 #ifdef ALLOWLOGGING 684 void 685 FlushLog(XtermWidget xw) 686 { 687 (void) xw; 688 } 689 #endif 690 691 void 692 v_write(int f, const Char *data, size_t len) 693 { 694 (void) f; 695 (void) data; 696 (void) len; 697 } 698 699 void 700 mk_wcwidth_init(int mode) 701 { 702 (void) mode; 703 } 704 705 void 706 update_font_utf8_mode(void) 707 { 708 } 709 710 static int message_level = 0; 711 static int opt_all = 0; 712 static int opt_illegal = 0; 713 static int opt_convert = 0; 714 static int opt_reverse = 0; 715 static long total_test = 0; 716 static long total_errs = 0; 717 718 static void 719 usage(void) 720 { 721 static const char *msg[] = 722 { 723 "Usage: test_ptydata [options] [c1[-c1b] [c2-[c2b] [...]]]", 724 "", 725 "Options:", 726 " -a exercise all legal encode/decode to/from UTF-8", 727 " -c call convertFromUTF8 rather than decodeUTF8", 728 " -i ignore illegal UTF-8 when testing -r option", 729 " -q quieter", 730 " -r reverse/decode from UTF-8 byte-string to/from Unicode", 731 " -v more verbose" 732 }; 733 size_t n; 734 for (n = 0; n < sizeof(msg) / sizeof(msg[0]); ++n) { 735 fprintf(stderr, "%s\n", msg[n]); 736 } 737 exit(EXIT_FAILURE); 738 } 739 740 /* 741 * http://www.unicode.org/versions/corrigendum1.html, table 3.1B 742 */ 743 #define OkRange(n,lo,hi) \ 744 if (value[n] < lo || value[n] > hi) { \ 745 result = False; \ 746 break; \ 747 } 748 static Bool 749 is_legal_utf8(const Char *value) 750 { 751 Bool result = True; 752 Char ch; 753 while ((ch = *value) != '\0') { 754 if (ch <= 0x7f) { 755 ++value; 756 } else if (ch >= 0xc2 && ch <= 0xdf) { 757 OkRange(1, 0x80, 0xbf); 758 value += 2; 759 } else if (ch == 0xe0) { 760 OkRange(1, 0xa0, 0xbf); 761 OkRange(2, 0x80, 0xbf); 762 value += 3; 763 } else if (ch >= 0xe1 && ch <= 0xef) { 764 OkRange(1, 0x80, 0xbf); 765 OkRange(2, 0x80, 0xbf); 766 value += 3; 767 } else if (ch == 0xf0) { 768 OkRange(1, 0x90, 0xbf); 769 OkRange(2, 0x80, 0xbf); 770 OkRange(3, 0x80, 0xbf); 771 value += 4; 772 } else if (ch >= 0xf1 && ch <= 0xf3) { 773 OkRange(1, 0x80, 0xbf); 774 OkRange(2, 0x80, 0xbf); 775 OkRange(3, 0x80, 0xbf); 776 value += 4; 777 } else if (ch == 0xf4) { 778 OkRange(1, 0x80, 0x8f); 779 OkRange(2, 0x80, 0xbf); 780 OkRange(3, 0x80, 0xbf); 781 value += 4; 782 } else { 783 result = False; 784 break; 785 } 786 } 787 return result; 788 } 789 790 static void 791 test_utf8_convert(void) 792 { 793 unsigned c_in, c_out; 794 Char buffer[10]; 795 Char *result; 796 unsigned limit = 0x110000; 797 unsigned success = 0; 798 unsigned bucket[256]; 799 800 memset(bucket, 0, sizeof(bucket)); 801 for (c_in = 0; c_in < limit; ++c_in) { 802 memset(buffer, 0, sizeof(buffer)); 803 if ((result = convertToUTF8(buffer, c_in)) == NULL) { 804 TRACE(("conversion of U+%04X to UTF-8 failed\n", c_in)); 805 } else { 806 if ((result = convertFromUTF8(buffer, &c_out)) == NULL) { 807 TRACE(("conversion of U+%04X from UTF-8 failed\n", c_in)); 808 } else if (c_in != c_out) { 809 TRACE(("conversion of U+%04X to/from UTF-8 gave U+%04X\n", 810 c_in, c_out)); 811 } else { 812 while (result-- != buffer) { 813 bucket[*result]++; 814 } 815 ++success; 816 } 817 } 818 } 819 TRACE(("%u/%u successful\n", success, limit)); 820 for (c_in = 0; c_in < 256; ++c_in) { 821 if ((c_in % 8) == 0) { 822 TRACE((" %02X:", c_in)); 823 } 824 TRACE((" %8X", bucket[c_in])); 825 if (((c_in + 1) % 8) == 0) { 826 TRACE(("\n")); 827 } 828 } 829 } 830 831 static int 832 decode_one(const char *source, char **target) 833 { 834 int result = -1; 835 long check; 836 int radix = 0; 837 if ((source[0] == 'u' || source[0] == 'U') && source[1] == '+') { 838 source += 2; 839 radix = 16; 840 } else if (source[0] == '0' && source[1] == 'b') { 841 source += 2; 842 radix = 2; 843 } 844 check = strtol(source, target, radix); 845 if (*target != NULL && *target != source) 846 result = (int) check; 847 return result; 848 } 849 850 static int 851 decode_range(const char *source, int *lo, int *hi) 852 { 853 int result = 0; 854 char *after1; 855 char *after2; 856 if ((*lo = decode_one(source, &after1)) >= 0) { 857 after1 += strspn(after1, ":-.\t "); 858 if ((*hi = decode_one(after1, &after2)) < 0) { 859 *hi = *lo; 860 } 861 result = 1; 862 } 863 return result; 864 } 865 866 #define MAX_BYTES 6 867 868 static void 869 do_range(const char *source) 870 { 871 int lo, hi; 872 873 TScreen screen; 874 memset(&screen, 0, sizeof(screen)); 875 876 if (decode_range(source, &lo, &hi)) { 877 while (lo <= hi) { 878 unsigned c_in = (unsigned) lo++; 879 PtyData *data; 880 Char *next; 881 Char buffer[MAX_BYTES + 1]; 882 883 if (opt_reverse) { 884 Bool skip = False; 885 Bool first = True; 886 int j, k; 887 for (j = 0; j < MAX_BYTES; ++j) { 888 unsigned long bits = ((unsigned long) c_in >> (8 * j)); 889 if ((buffer[j] = (Char) bits) == 0) { 890 skip = (bits != 0); 891 break; 892 } 893 } 894 if (skip) 895 continue; 896 initPtyData(&data); 897 for (k = 0; k <= j; ++k) { 898 data->buffer[k] = buffer[j - k - 1]; 899 } 900 if (opt_illegal && !is_legal_utf8(data->buffer)) { 901 free(data); 902 continue; 903 } 904 if (message_level > 1) { 905 printf("TEST "); 906 for (k = 0; k < j; ++k) { 907 printf("%02X", data->buffer[k]); 908 } 909 } 910 data->next = data->buffer; 911 data->last = data->buffer + j; 912 while (decodeUtf8(&screen, data)) { 913 total_test++; 914 if (is_UCS_SPECIAL(data->utf_data)) 915 total_errs++; 916 data->next += data->utf_size; 917 if (message_level > 1) { 918 printf("%s%04X", first ? " ->" : ", ", data->utf_data); 919 } 920 first = False; 921 } 922 if (!first) 923 total_test--; 924 if (message_level > 1) { 925 printf("\n"); 926 fflush(stdout); 927 } 928 free(data); 929 } else if (opt_convert) { 930 unsigned c_out; 931 Char *result; 932 933 memset(buffer, 0, sizeof(buffer)); 934 if ((result = next = convertToUTF8(buffer, c_in)) == NULL) { 935 fprintf(stderr, 936 "conversion of U+%04X to UTF-8 failed\n", c_in); 937 } else if ((result = convertFromUTF8(buffer, &c_out)) == NULL) { 938 fprintf(stderr, 939 "conversion of U+%04X from UTF-8 failed\n", c_in); 940 total_errs++; 941 } else if (c_in != c_out) { 942 fprintf(stderr, 943 "conversion of U+%04X to/from UTF-8 gave U+%04X\n", 944 c_in, c_out); 945 } else if (message_level > 1) { 946 *next = '\0'; 947 printf("TEST %04X (%lu:%s) ->%04X\n", c_in, 948 (unsigned long) (next - buffer), 949 buffer, 950 c_out); 951 fflush(stdout); 952 } 953 } else { 954 initPtyData(&data); 955 next = convertToUTF8(data->buffer, c_in); 956 *next = 0; 957 data->next = data->buffer; 958 data->last = next; 959 decodeUtf8(&screen, data); 960 if (message_level > 1) { 961 printf("TEST %04X (%lu:%s) ->%04X\n", c_in, 962 (unsigned long) (next - data->buffer), 963 data->buffer, 964 data->utf_data); 965 fflush(stdout); 966 } 967 if (c_in != data->utf_data) { 968 fprintf(stderr, "Mismatch: %04X vs %04X\n", c_in, data->utf_data); 969 total_errs++; 970 } 971 free(data); 972 } 973 total_test++; 974 } 975 } 976 } 977 978 int 979 main(int argc, char **argv) 980 { 981 int ch; 982 983 setlocale(LC_ALL, ""); 984 while ((ch = getopt(argc, argv, "aciqrv")) != -1) { 985 switch (ch) { 986 case 'a': 987 opt_all = 1; 988 break; 989 case 'c': 990 opt_convert = 1; 991 break; 992 case 'i': 993 opt_illegal = 1; 994 break; 995 case 'q': 996 message_level--; 997 break; 998 case 'r': 999 opt_reverse = 1; 1000 break; 1001 case 'v': 1002 message_level++; 1003 break; 1004 default: 1005 usage(); 1006 } 1007 } 1008 if (opt_all) { 1009 test_utf8_convert(); 1010 } else { 1011 if (optind >= argc) 1012 usage(); 1013 while (optind < argc) { 1014 do_range(argv[optind++]); 1015 } 1016 if (total_test) { 1017 printf("%ld/%ld mismatches (%.0f%%)\n", 1018 total_errs, 1019 total_test, 1020 (100.0 * (double) total_errs) / (double) total_test); 1021 } 1022 } 1023 return EXIT_SUCCESS; 1024 } 1025 #else 1026 int 1027 main(int argc, char **argv) 1028 { 1029 (void) argc; 1030 (void) argv; 1031 printf("Nothing to be done here...\n"); 1032 return EXIT_SUCCESS; 1033 } 1034 #endif /* OPT_WIDE_CHARS */ 1035 #endif 1036