ucpgba.c revision 1.1.1.3
1/* $NetBSD: ucpgba.c,v 1.1.1.3 2010/12/12 15:21:58 adam Exp $ */ 2 3/* OpenLDAP: pkg/ldap/libraries/liblunicode/ucdata/ucpgba.c,v 1.7.2.5 2010/04/13 20:23:04 kurt Exp */ 4/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1998-2010 The OpenLDAP Foundation. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted only as authorized by the OpenLDAP 11 * Public License. 12 * 13 * A copy of this license is available in file LICENSE in the 14 * top-level directory of the distribution or, alternatively, at 15 * <http://www.OpenLDAP.org/license.html>. 16 */ 17/* Copyright 2001 Computing Research Labs, New Mexico State University 18 * 19 * Permission is hereby granted, free of charge, to any person obtaining a 20 * copy of this software and associated documentation files (the "Software"), 21 * to deal in the Software without restriction, including without limitation 22 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 23 * and/or sell copies of the Software, and to permit persons to whom the 24 * Software is furnished to do so, subject to the following conditions: 25 * 26 * The above copyright notice and this permission notice shall be included in 27 * all copies or substantial portions of the Software. 28 * 29 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 32 * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY 33 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 34 * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR 35 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. 36 */ 37/* Id: ucpgba.c,v 1.5 2001/01/02 18:46:20 mleisher Exp */ 38 39#include "portable.h" 40 41#include <stdio.h> 42#include <stdlib.h> 43 44#include "ucdata.h" 45#include "ucpgba.h" 46 47/* 48 * These macros are used while reordering of RTL runs of text for the 49 * special case of non-spacing characters being in runs of weakly 50 * directional text. They check for weak and non-spacing, and digits and 51 * non-spacing. 52 */ 53#define ISWEAKSPECIAL(cc) ucisprop(cc, UC_EN|UC_ES|UC_MN, UC_ET|UC_AN|UC_CS) 54#define ISDIGITSPECIAL(cc) ucisprop(cc, UC_ND|UC_MN, 0) 55 56/* 57 * These macros are used while breaking a string into runs of text in 58 * different directions. Descriptions: 59 * 60 * ISLTR_LTR - Test for members of an LTR run in an LTR context. This looks 61 * for characters with ltr, non-spacing, weak, and neutral 62 * properties. 63 * 64 * ISRTL_RTL - Test for members of an RTL run in an RTL context. This looks 65 * for characters with rtl, non-spacing, weak, and neutral 66 * properties. 67 * 68 * ISRTL_NEUTRAL - Test for RTL or neutral characters. 69 * 70 * ISWEAK_NEUTRAL - Test for weak or neutral characters. 71 */ 72#define ISLTR_LTR(cc) ucisprop(cc, UC_L|UC_MN|UC_EN|UC_ES,\ 73 UC_ET|UC_CS|UC_B|UC_S|UC_WS|UC_ON) 74 75#define ISRTL_RTL(cc) ucisprop(cc, UC_R|UC_MN|UC_EN|UC_ES,\ 76 UC_ET|UC_AN|UC_CS|UC_B|UC_S|UC_WS|UC_ON) 77 78#define ISRTL_NEUTRAL(cc) ucisprop(cc, UC_R, UC_B|UC_S|UC_WS|UC_ON) 79#define ISWEAK_NEUTRAL(cc) ucisprop(cc, UC_EN|UC_ES, \ 80 UC_B|UC_S|UC_WS|UC_ON|UC_ET|UC_AN|UC_CS) 81 82/* 83 * This table is temporarily hard-coded here until it can be constructed 84 * automatically somehow. 85 */ 86static unsigned long _symmetric_pairs[] = { 87 0x0028, 0x0029, 0x0029, 0x0028, 0x003C, 0x003E, 0x003E, 0x003C, 88 0x005B, 0x005D, 0x005D, 0x005B, 0x007B, 0x007D, 0x007D, 0x007B, 89 0x2045, 0x2046, 0x2046, 0x2045, 0x207D, 0x207E, 0x207E, 0x207D, 90 0x208D, 0x208E, 0x208E, 0x208D, 0x3008, 0x3009, 0x3009, 0x3008, 91 0x300A, 0x300B, 0x300B, 0x300A, 0x300C, 0x300D, 0x300D, 0x300C, 92 0x300E, 0x300F, 0x300F, 0x300E, 0x3010, 0x3011, 0x3011, 0x3010, 93 0x3014, 0x3015, 0x3015, 0x3014, 0x3016, 0x3017, 0x3017, 0x3016, 94 0x3018, 0x3019, 0x3019, 0x3018, 0x301A, 0x301B, 0x301B, 0x301A, 95 0xFD3E, 0xFD3F, 0xFD3F, 0xFD3E, 0xFE59, 0xFE5A, 0xFE5A, 0xFE59, 96 0xFE5B, 0xFE5C, 0xFE5C, 0xFE5B, 0xFE5D, 0xFE5E, 0xFE5E, 0xFE5D, 97 0xFF08, 0xFF09, 0xFF09, 0xFF08, 0xFF3B, 0xFF3D, 0xFF3D, 0xFF3B, 98 0xFF5B, 0xFF5D, 0xFF5D, 0xFF5B, 0xFF62, 0xFF63, 0xFF63, 0xFF62, 99}; 100 101static int _symmetric_pairs_size = 102sizeof(_symmetric_pairs)/sizeof(_symmetric_pairs[0]); 103 104/* 105 * This routine looks up the other form of a symmetric pair. 106 */ 107static unsigned long 108_ucsymmetric_pair(unsigned long c) 109{ 110 int i; 111 112 for (i = 0; i < _symmetric_pairs_size; i += 2) { 113 if (_symmetric_pairs[i] == c) 114 return _symmetric_pairs[i+1]; 115 } 116 return c; 117} 118 119/* 120 * This routine creates a new run, copies the text into it, links it into the 121 * logical text order chain and returns it to the caller to be linked into 122 * the visual text order chain. 123 */ 124static ucrun_t * 125_add_run(ucstring_t *str, unsigned long *src, 126 unsigned long start, unsigned long end, int direction) 127{ 128 long i, t; 129 ucrun_t *run; 130 131 run = (ucrun_t *) malloc(sizeof(ucrun_t)); 132 run->visual_next = run->visual_prev = 0; 133 run->direction = direction; 134 135 run->cursor = ~0; 136 137 run->chars = (unsigned long *) 138 malloc(sizeof(unsigned long) * ((end - start) << 1)); 139 run->positions = run->chars + (end - start); 140 141 run->source = src; 142 run->start = start; 143 run->end = end; 144 145 if (direction == UCPGBA_RTL) { 146 /* 147 * Copy the source text into the run in reverse order and select 148 * replacements for the pairwise punctuation and the <> characters. 149 */ 150 for (i = 0, t = end - 1; start < end; start++, t--, i++) { 151 run->positions[i] = t; 152 if (ucissymmetric(src[t]) || src[t] == '<' || src[t] == '>') 153 run->chars[i] = _ucsymmetric_pair(src[t]); 154 else 155 run->chars[i] = src[t]; 156 } 157 } else { 158 /* 159 * Copy the source text into the run directly. 160 */ 161 for (i = start; i < end; i++) { 162 run->positions[i - start] = i; 163 run->chars[i - start] = src[i]; 164 } 165 } 166 167 /* 168 * Add the run to the logical list for cursor traversal. 169 */ 170 if (str->logical_first == 0) 171 str->logical_first = str->logical_last = run; 172 else { 173 run->logical_prev = str->logical_last; 174 str->logical_last->logical_next = run; 175 str->logical_last = run; 176 } 177 178 return run; 179} 180 181static void 182_ucadd_rtl_segment(ucstring_t *str, unsigned long *source, unsigned long start, 183 unsigned long end) 184{ 185 unsigned long s, e; 186 ucrun_t *run, *lrun; 187 188 /* 189 * This is used to splice runs into strings with overall LTR direction. 190 * The `lrun' variable will never be NULL because at least one LTR run was 191 * added before this RTL run. 192 */ 193 lrun = str->visual_last; 194 195 for (e = s = start; s < end;) { 196 for (; e < end && ISRTL_NEUTRAL(source[e]); e++) ; 197 198 if (e > s) { 199 run = _add_run(str, source, s, e, UCPGBA_RTL); 200 201 /* 202 * Add the run to the visual list for cursor traversal. 203 */ 204 if (str->visual_first != 0) { 205 if (str->direction == UCPGBA_LTR) { 206 run->visual_prev = lrun; 207 run->visual_next = lrun->visual_next; 208 if (lrun->visual_next != 0) 209 lrun->visual_next->visual_prev = run; 210 lrun->visual_next = run; 211 if (lrun == str->visual_last) 212 str->visual_last = run; 213 } else { 214 run->visual_next = str->visual_first; 215 str->visual_first->visual_prev = run; 216 str->visual_first = run; 217 } 218 } else 219 str->visual_first = str->visual_last = run; 220 } 221 222 /* 223 * Handle digits in a special way. This makes sure the weakly 224 * directional characters appear on the expected sides of a number 225 * depending on whether that number is Arabic or not. 226 */ 227 for (s = e; e < end && ISWEAKSPECIAL(source[e]); e++) { 228 if (!ISDIGITSPECIAL(source[e]) && 229 (e + 1 == end || !ISDIGITSPECIAL(source[e + 1]))) 230 break; 231 } 232 233 if (e > s) { 234 run = _add_run(str, source, s, e, UCPGBA_LTR); 235 236 /* 237 * Add the run to the visual list for cursor traversal. 238 */ 239 if (str->visual_first != 0) { 240 if (str->direction == UCPGBA_LTR) { 241 run->visual_prev = lrun; 242 run->visual_next = lrun->visual_next; 243 if (lrun->visual_next != 0) 244 lrun->visual_next->visual_prev = run; 245 lrun->visual_next = run; 246 if (lrun == str->visual_last) 247 str->visual_last = run; 248 } else { 249 run->visual_next = str->visual_first; 250 str->visual_first->visual_prev = run; 251 str->visual_first = run; 252 } 253 } else 254 str->visual_first = str->visual_last = run; 255 } 256 257 /* 258 * Collect all weak non-digit sequences for an RTL segment. These 259 * will appear as part of the next RTL segment or will be added as 260 * an RTL segment by themselves. 261 */ 262 for (s = e; e < end && ucisweak(source[e]) && !ucisdigit(source[e]); 263 e++) ; 264 } 265 266 /* 267 * Capture any weak non-digit sequences that occur at the end of the RTL 268 * run. 269 */ 270 if (e > s) { 271 run = _add_run(str, source, s, e, UCPGBA_RTL); 272 273 /* 274 * Add the run to the visual list for cursor traversal. 275 */ 276 if (str->visual_first != 0) { 277 if (str->direction == UCPGBA_LTR) { 278 run->visual_prev = lrun; 279 run->visual_next = lrun->visual_next; 280 if (lrun->visual_next != 0) 281 lrun->visual_next->visual_prev = run; 282 lrun->visual_next = run; 283 if (lrun == str->visual_last) 284 str->visual_last = run; 285 } else { 286 run->visual_next = str->visual_first; 287 str->visual_first->visual_prev = run; 288 str->visual_first = run; 289 } 290 } else 291 str->visual_first = str->visual_last = run; 292 } 293} 294 295static void 296_ucadd_ltr_segment(ucstring_t *str, unsigned long *source, unsigned long start, 297 unsigned long end) 298{ 299 ucrun_t *run; 300 301 run = _add_run(str, source, start, end, UCPGBA_LTR); 302 303 /* 304 * Add the run to the visual list for cursor traversal. 305 */ 306 if (str->visual_first != 0) { 307 if (str->direction == UCPGBA_LTR) { 308 run->visual_prev = str->visual_last; 309 str->visual_last->visual_next = run; 310 str->visual_last = run; 311 } else { 312 run->visual_next = str->visual_first; 313 str->visual_first->visual_prev = run; 314 str->visual_first = run; 315 } 316 } else 317 str->visual_first = str->visual_last = run; 318} 319 320ucstring_t * 321ucstring_create(unsigned long *source, unsigned long start, unsigned long end, 322 int default_direction, int cursor_motion) 323{ 324 int rtl_first; 325 unsigned long s, e, ld; 326 ucstring_t *str; 327 328 str = (ucstring_t *) malloc(sizeof(ucstring_t)); 329 330 /* 331 * Set the initial values. 332 */ 333 str->cursor_motion = cursor_motion; 334 str->logical_first = str->logical_last = 0; 335 str->visual_first = str->visual_last = str->cursor = 0; 336 str->source = source; 337 str->start = start; 338 str->end = end; 339 340 /* 341 * If the length of the string is 0, then just return it at this point. 342 */ 343 if (start == end) 344 return str; 345 346 /* 347 * This flag indicates whether the collection loop for RTL is called 348 * before the LTR loop the first time. 349 */ 350 rtl_first = 0; 351 352 /* 353 * Look for the first character in the string that has strong 354 * directionality. 355 */ 356 for (s = start; s < end && !ucisstrong(source[s]); s++) ; 357 358 if (s == end) 359 /* 360 * If the string contains no characters with strong directionality, use 361 * the default direction. 362 */ 363 str->direction = default_direction; 364 else 365 str->direction = ucisrtl(source[s]) ? UCPGBA_RTL : UCPGBA_LTR; 366 367 if (str->direction == UCPGBA_RTL) 368 /* 369 * Set the flag that causes the RTL collection loop to run first. 370 */ 371 rtl_first = 1; 372 373 /* 374 * This loop now separates the string into runs based on directionality. 375 */ 376 for (s = e = 0; s < end; s = e) { 377 if (!rtl_first) { 378 /* 379 * Determine the next run of LTR text. 380 */ 381 382 ld = s; 383 while (e < end && ISLTR_LTR(source[e])) { 384 if (ucisdigit(source[e]) && 385 !(0x660 <= source[e] && source[e] <= 0x669)) 386 ld = e; 387 e++; 388 } 389 if (str->direction != UCPGBA_LTR) { 390 while (e > ld && ISWEAK_NEUTRAL(source[e - 1])) 391 e--; 392 } 393 394 /* 395 * Add the LTR segment to the string. 396 */ 397 if (e > s) 398 _ucadd_ltr_segment(str, source, s, e); 399 } 400 401 /* 402 * Determine the next run of RTL text. 403 */ 404 ld = s = e; 405 while (e < end && ISRTL_RTL(source[e])) { 406 if (ucisdigit(source[e]) && 407 !(0x660 <= source[e] && source[e] <= 0x669)) 408 ld = e; 409 e++; 410 } 411 if (str->direction != UCPGBA_RTL) { 412 while (e > ld && ISWEAK_NEUTRAL(source[e - 1])) 413 e--; 414 } 415 416 /* 417 * Add the RTL segment to the string. 418 */ 419 if (e > s) 420 _ucadd_rtl_segment(str, source, s, e); 421 422 /* 423 * Clear the flag that allowed the RTL collection loop to run first 424 * for strings with overall RTL directionality. 425 */ 426 rtl_first = 0; 427 } 428 429 /* 430 * Set up the initial cursor run. 431 */ 432 str->cursor = str->logical_first; 433 if (str != 0) 434 str->cursor->cursor = (str->cursor->direction == UCPGBA_RTL) ? 435 str->cursor->end - str->cursor->start : 0; 436 437 return str; 438} 439 440void 441ucstring_free(ucstring_t *s) 442{ 443 ucrun_t *l, *r; 444 445 if (s == 0) 446 return; 447 448 for (l = 0, r = s->visual_first; r != 0; r = r->visual_next) { 449 if (r->end > r->start) 450 free((char *) r->chars); 451 if (l) 452 free((char *) l); 453 l = r; 454 } 455 if (l) 456 free((char *) l); 457 458 free((char *) s); 459} 460 461int 462ucstring_set_cursor_motion(ucstring_t *str, int cursor_motion) 463{ 464 int n; 465 466 if (str == 0) 467 return -1; 468 469 n = str->cursor_motion; 470 str->cursor_motion = cursor_motion; 471 return n; 472} 473 474static int 475_ucstring_visual_cursor_right(ucstring_t *str, int count) 476{ 477 int cnt = count; 478 unsigned long size; 479 ucrun_t *cursor; 480 481 if (str == 0) 482 return 0; 483 484 cursor = str->cursor; 485 while (cnt > 0) { 486 size = cursor->end - cursor->start; 487 if ((cursor->direction == UCPGBA_RTL && cursor->cursor + 1 == size) || 488 cursor->cursor + 1 > size) { 489 /* 490 * If the next run is NULL, then the cursor is already on the 491 * far right end already. 492 */ 493 if (cursor->visual_next == 0) 494 /* 495 * If movement occured, then report it. 496 */ 497 return (cnt != count); 498 499 /* 500 * Move to the next run. 501 */ 502 str->cursor = cursor = cursor->visual_next; 503 cursor->cursor = (cursor->direction == UCPGBA_RTL) ? -1 : 0; 504 size = cursor->end - cursor->start; 505 } else 506 cursor->cursor++; 507 cnt--; 508 } 509 return 1; 510} 511 512static int 513_ucstring_logical_cursor_right(ucstring_t *str, int count) 514{ 515 int cnt = count; 516 unsigned long size; 517 ucrun_t *cursor; 518 519 if (str == 0) 520 return 0; 521 522 cursor = str->cursor; 523 while (cnt > 0) { 524 size = cursor->end - cursor->start; 525 if (str->direction == UCPGBA_RTL) { 526 if (cursor->direction == UCPGBA_RTL) { 527 if (cursor->cursor + 1 == size) { 528 if (cursor == str->logical_first) 529 /* 530 * Already at the beginning of the string. 531 */ 532 return (cnt != count); 533 534 str->cursor = cursor = cursor->logical_prev; 535 size = cursor->end - cursor->start; 536 cursor->cursor = (cursor->direction == UCPGBA_LTR) ? 537 size : 0; 538 } else 539 cursor->cursor++; 540 } else { 541 if (cursor->cursor == 0) { 542 if (cursor == str->logical_first) 543 /* 544 * At the beginning of the string already. 545 */ 546 return (cnt != count); 547 548 str->cursor = cursor = cursor->logical_prev; 549 size = cursor->end - cursor->start; 550 cursor->cursor = (cursor->direction == UCPGBA_LTR) ? 551 size : 0; 552 } else 553 cursor->cursor--; 554 } 555 } else { 556 if (cursor->direction == UCPGBA_RTL) { 557 if (cursor->cursor == 0) { 558 if (cursor == str->logical_last) 559 /* 560 * Already at the end of the string. 561 */ 562 return (cnt != count); 563 564 str->cursor = cursor = cursor->logical_next; 565 size = cursor->end - cursor->start; 566 cursor->cursor = (cursor->direction == UCPGBA_LTR) ? 567 0 : size - 1; 568 } else 569 cursor->cursor--; 570 } else { 571 if (cursor->cursor + 1 > size) { 572 if (cursor == str->logical_last) 573 /* 574 * Already at the end of the string. 575 */ 576 return (cnt != count); 577 578 str->cursor = cursor = cursor->logical_next; 579 cursor->cursor = (cursor->direction == UCPGBA_LTR) ? 580 0 : size - 1; 581 } else 582 cursor->cursor++; 583 } 584 } 585 cnt--; 586 } 587 return 1; 588} 589 590int 591ucstring_cursor_right(ucstring_t *str, int count) 592{ 593 if (str == 0) 594 return 0; 595 return (str->cursor_motion == UCPGBA_CURSOR_VISUAL) ? 596 _ucstring_visual_cursor_right(str, count) : 597 _ucstring_logical_cursor_right(str, count); 598} 599 600static int 601_ucstring_visual_cursor_left(ucstring_t *str, int count) 602{ 603 int cnt = count; 604 unsigned long size; 605 ucrun_t *cursor; 606 607 if (str == 0) 608 return 0; 609 610 cursor = str->cursor; 611 while (cnt > 0) { 612 size = cursor->end - cursor->start; 613 if ((cursor->direction == UCPGBA_LTR && cursor->cursor == 0) || 614 cursor->cursor - 1 < -1) { 615 /* 616 * If the preceding run is NULL, then the cursor is already on the 617 * far left end already. 618 */ 619 if (cursor->visual_prev == 0) 620 /* 621 * If movement occured, then report it. 622 */ 623 return (cnt != count); 624 625 /* 626 * Move to the previous run. 627 */ 628 str->cursor = cursor = cursor->visual_prev; 629 size = cursor->end - cursor->start; 630 cursor->cursor = (cursor->direction == UCPGBA_RTL) ? 631 size : size - 1; 632 } else 633 cursor->cursor--; 634 cnt--; 635 } 636 return 1; 637} 638 639static int 640_ucstring_logical_cursor_left(ucstring_t *str, int count) 641{ 642 int cnt = count; 643 unsigned long size; 644 ucrun_t *cursor; 645 646 if (str == 0) 647 return 0; 648 649 cursor = str->cursor; 650 while (cnt > 0) { 651 size = cursor->end - cursor->start; 652 if (str->direction == UCPGBA_RTL) { 653 if (cursor->direction == UCPGBA_RTL) { 654 if (cursor->cursor == -1) { 655 if (cursor == str->logical_last) 656 /* 657 * Already at the end of the string. 658 */ 659 return (cnt != count); 660 661 str->cursor = cursor = cursor->logical_next; 662 size = cursor->end - cursor->start; 663 cursor->cursor = (cursor->direction == UCPGBA_LTR) ? 664 0 : size - 1; 665 } else 666 cursor->cursor--; 667 } else { 668 if (cursor->cursor + 1 > size) { 669 if (cursor == str->logical_last) 670 /* 671 * At the end of the string already. 672 */ 673 return (cnt != count); 674 675 str->cursor = cursor = cursor->logical_next; 676 size = cursor->end - cursor->start; 677 cursor->cursor = (cursor->direction == UCPGBA_LTR) ? 678 0 : size - 1; 679 } else 680 cursor->cursor++; 681 } 682 } else { 683 if (cursor->direction == UCPGBA_RTL) { 684 if (cursor->cursor + 1 == size) { 685 if (cursor == str->logical_first) 686 /* 687 * Already at the beginning of the string. 688 */ 689 return (cnt != count); 690 691 str->cursor = cursor = cursor->logical_prev; 692 size = cursor->end - cursor->start; 693 cursor->cursor = (cursor->direction == UCPGBA_LTR) ? 694 size : 0; 695 } else 696 cursor->cursor++; 697 } else { 698 if (cursor->cursor == 0) { 699 if (cursor == str->logical_first) 700 /* 701 * Already at the beginning of the string. 702 */ 703 return (cnt != count); 704 705 str->cursor = cursor = cursor->logical_prev; 706 cursor->cursor = (cursor->direction == UCPGBA_LTR) ? 707 size : 0; 708 } else 709 cursor->cursor--; 710 } 711 } 712 cnt--; 713 } 714 return 1; 715} 716 717int 718ucstring_cursor_left(ucstring_t *str, int count) 719{ 720 if (str == 0) 721 return 0; 722 return (str->cursor_motion == UCPGBA_CURSOR_VISUAL) ? 723 _ucstring_visual_cursor_left(str, count) : 724 _ucstring_logical_cursor_left(str, count); 725} 726 727void 728ucstring_cursor_info(ucstring_t *str, int *direction, unsigned long *position) 729{ 730 long c; 731 unsigned long size; 732 ucrun_t *cursor; 733 734 if (str == 0 || direction == 0 || position == 0) 735 return; 736 737 cursor = str->cursor; 738 739 *direction = cursor->direction; 740 741 c = cursor->cursor; 742 size = cursor->end - cursor->start; 743 744 if (c == size) 745 *position = (cursor->direction == UCPGBA_RTL) ? 746 cursor->start : cursor->positions[c - 1]; 747 else if (c == -1) 748 *position = (cursor->direction == UCPGBA_RTL) ? 749 cursor->end : cursor->start; 750 else 751 *position = cursor->positions[c]; 752} 753