133965Sjdp/* $NetBSD$ */ 233965Sjdp 333965Sjdp/* OpenLDAP: pkg/ldap/libraries/liblunicode/ucdata/ucpgba.c,v 1.7.2.5 2010/04/13 20:23:04 kurt Exp */ 4130561Sobrien/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5130561Sobrien * 633965Sjdp * Copyright 1998-2010 The OpenLDAP Foundation. 733965Sjdp * All rights reserved. 833965Sjdp * 933965Sjdp * Redistribution and use in source and binary forms, with or without 1033965Sjdp * modification, are permitted only as authorized by the OpenLDAP 1133965Sjdp * Public License. 1233965Sjdp * 1333965Sjdp * A copy of this license is available in file LICENSE in the 1433965Sjdp * top-level directory of the distribution or, alternatively, at 1533965Sjdp * <http://www.OpenLDAP.org/license.html>. 1633965Sjdp */ 1733965Sjdp/* Copyright 2001 Computing Research Labs, New Mexico State University 1833965Sjdp * 1933965Sjdp * Permission is hereby granted, free of charge, to any person obtaining a 2033965Sjdp * copy of this software and associated documentation files (the "Software"), 2133965Sjdp * to deal in the Software without restriction, including without limitation 2233965Sjdp * the rights to use, copy, modify, merge, publish, distribute, sublicense, 2333965Sjdp * and/or sell copies of the Software, and to permit persons to whom the 2433965Sjdp * Software is furnished to do so, subject to the following conditions: 2533965Sjdp * 2633965Sjdp * The above copyright notice and this permission notice shall be included in 2733965Sjdp * all copies or substantial portions of the Software. 2833965Sjdp * 2933965Sjdp * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3033965Sjdp * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 3133965Sjdp * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 3233965Sjdp * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY 33130561Sobrien * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 34130561Sobrien * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR 3533965Sjdp * THE USE OR OTHER DEALINGS IN THE SOFTWARE. 3633965Sjdp */ 3733965Sjdp/* Id: ucpgba.c,v 1.5 2001/01/02 18:46:20 mleisher Exp */ 3833965Sjdp 3933965Sjdp#include "portable.h" 4033965Sjdp 4133965Sjdp#include <stdio.h> 4233965Sjdp#include <stdlib.h> 4333965Sjdp 4433965Sjdp#include "ucdata.h" 4533965Sjdp#include "ucpgba.h" 4633965Sjdp 4733965Sjdp/* 4833965Sjdp * These macros are used while reordering of RTL runs of text for the 4933965Sjdp * special case of non-spacing characters being in runs of weakly 5033965Sjdp * directional text. They check for weak and non-spacing, and digits and 5133965Sjdp * non-spacing. 5233965Sjdp */ 5333965Sjdp#define ISWEAKSPECIAL(cc) ucisprop(cc, UC_EN|UC_ES|UC_MN, UC_ET|UC_AN|UC_CS) 5433965Sjdp#define ISDIGITSPECIAL(cc) ucisprop(cc, UC_ND|UC_MN, 0) 5533965Sjdp 5633965Sjdp/* 5733965Sjdp * These macros are used while breaking a string into runs of text in 5833965Sjdp * different directions. Descriptions: 5933965Sjdp * 6033965Sjdp * ISLTR_LTR - Test for members of an LTR run in an LTR context. This looks 6133965Sjdp * for characters with ltr, non-spacing, weak, and neutral 6233965Sjdp * properties. 6333965Sjdp * 6433965Sjdp * ISRTL_RTL - Test for members of an RTL run in an RTL context. This looks 6533965Sjdp * for characters with rtl, non-spacing, weak, and neutral 6633965Sjdp * properties. 6733965Sjdp * 6833965Sjdp * ISRTL_NEUTRAL - Test for RTL or neutral characters. 6933965Sjdp * 7033965Sjdp * ISWEAK_NEUTRAL - Test for weak or neutral characters. 7133965Sjdp */ 7233965Sjdp#define ISLTR_LTR(cc) ucisprop(cc, UC_L|UC_MN|UC_EN|UC_ES,\ 7333965Sjdp UC_ET|UC_CS|UC_B|UC_S|UC_WS|UC_ON) 7433965Sjdp 7533965Sjdp#define ISRTL_RTL(cc) ucisprop(cc, UC_R|UC_MN|UC_EN|UC_ES,\ 7633965Sjdp UC_ET|UC_AN|UC_CS|UC_B|UC_S|UC_WS|UC_ON) 7733965Sjdp 7899461Sobrien#define ISRTL_NEUTRAL(cc) ucisprop(cc, UC_R, UC_B|UC_S|UC_WS|UC_ON) 7933965Sjdp#define ISWEAK_NEUTRAL(cc) ucisprop(cc, UC_EN|UC_ES, \ 8033965Sjdp UC_B|UC_S|UC_WS|UC_ON|UC_ET|UC_AN|UC_CS) 8133965Sjdp 8233965Sjdp/* 8333965Sjdp * This table is temporarily hard-coded here until it can be constructed 8433965Sjdp * automatically somehow. 8533965Sjdp */ 8633965Sjdpstatic unsigned long _symmetric_pairs[] = { 8733965Sjdp 0x0028, 0x0029, 0x0029, 0x0028, 0x003C, 0x003E, 0x003E, 0x003C, 8833965Sjdp 0x005B, 0x005D, 0x005D, 0x005B, 0x007B, 0x007D, 0x007D, 0x007B, 8933965Sjdp 0x2045, 0x2046, 0x2046, 0x2045, 0x207D, 0x207E, 0x207E, 0x207D, 9033965Sjdp 0x208D, 0x208E, 0x208E, 0x208D, 0x3008, 0x3009, 0x3009, 0x3008, 9133965Sjdp 0x300A, 0x300B, 0x300B, 0x300A, 0x300C, 0x300D, 0x300D, 0x300C, 9233965Sjdp 0x300E, 0x300F, 0x300F, 0x300E, 0x3010, 0x3011, 0x3011, 0x3010, 9333965Sjdp 0x3014, 0x3015, 0x3015, 0x3014, 0x3016, 0x3017, 0x3017, 0x3016, 9433965Sjdp 0x3018, 0x3019, 0x3019, 0x3018, 0x301A, 0x301B, 0x301B, 0x301A, 9533965Sjdp 0xFD3E, 0xFD3F, 0xFD3F, 0xFD3E, 0xFE59, 0xFE5A, 0xFE5A, 0xFE59, 9633965Sjdp 0xFE5B, 0xFE5C, 0xFE5C, 0xFE5B, 0xFE5D, 0xFE5E, 0xFE5E, 0xFE5D, 9733965Sjdp 0xFF08, 0xFF09, 0xFF09, 0xFF08, 0xFF3B, 0xFF3D, 0xFF3D, 0xFF3B, 9833965Sjdp 0xFF5B, 0xFF5D, 0xFF5D, 0xFF5B, 0xFF62, 0xFF63, 0xFF63, 0xFF62, 9933965Sjdp}; 10033965Sjdp 10133965Sjdpstatic int _symmetric_pairs_size = 10233965Sjdpsizeof(_symmetric_pairs)/sizeof(_symmetric_pairs[0]); 10333965Sjdp 10433965Sjdp/* 10533965Sjdp * This routine looks up the other form of a symmetric pair. 10633965Sjdp */ 10733965Sjdpstatic unsigned long 10833965Sjdp_ucsymmetric_pair(unsigned long c) 10933965Sjdp{ 11033965Sjdp int i; 11133965Sjdp 11233965Sjdp for (i = 0; i < _symmetric_pairs_size; i += 2) { 11333965Sjdp if (_symmetric_pairs[i] == c) 11433965Sjdp return _symmetric_pairs[i+1]; 11533965Sjdp } 11633965Sjdp return c; 11733965Sjdp} 11833965Sjdp 11933965Sjdp/* 12033965Sjdp * This routine creates a new run, copies the text into it, links it into the 12133965Sjdp * logical text order chain and returns it to the caller to be linked into 12233965Sjdp * the visual text order chain. 12333965Sjdp */ 12433965Sjdpstatic ucrun_t * 12533965Sjdp_add_run(ucstring_t *str, unsigned long *src, 12633965Sjdp unsigned long start, unsigned long end, int direction) 12733965Sjdp{ 12833965Sjdp long i, t; 12933965Sjdp ucrun_t *run; 13033965Sjdp 13133965Sjdp run = (ucrun_t *) malloc(sizeof(ucrun_t)); 13233965Sjdp run->visual_next = run->visual_prev = 0; 13333965Sjdp run->direction = direction; 13433965Sjdp 13533965Sjdp run->cursor = ~0; 13633965Sjdp 13733965Sjdp run->chars = (unsigned long *) 13833965Sjdp malloc(sizeof(unsigned long) * ((end - start) << 1)); 13933965Sjdp run->positions = run->chars + (end - start); 14033965Sjdp 14133965Sjdp run->source = src; 14233965Sjdp run->start = start; 14333965Sjdp run->end = end; 14433965Sjdp 14533965Sjdp if (direction == UCPGBA_RTL) { 14633965Sjdp /* 14733965Sjdp * Copy the source text into the run in reverse order and select 14833965Sjdp * replacements for the pairwise punctuation and the <> characters. 14933965Sjdp */ 15033965Sjdp for (i = 0, t = end - 1; start < end; start++, t--, i++) { 15133965Sjdp run->positions[i] = t; 15233965Sjdp if (ucissymmetric(src[t]) || src[t] == '<' || src[t] == '>') 15333965Sjdp run->chars[i] = _ucsymmetric_pair(src[t]); 15433965Sjdp else 15533965Sjdp run->chars[i] = src[t]; 15633965Sjdp } 15733965Sjdp } else { 15833965Sjdp /* 15933965Sjdp * Copy the source text into the run directly. 16033965Sjdp */ 16133965Sjdp for (i = start; i < end; i++) { 16233965Sjdp run->positions[i - start] = i; 16333965Sjdp run->chars[i - start] = src[i]; 16433965Sjdp } 16533965Sjdp } 16633965Sjdp 16733965Sjdp /* 16833965Sjdp * Add the run to the logical list for cursor traversal. 16933965Sjdp */ 17033965Sjdp if (str->logical_first == 0) 17133965Sjdp str->logical_first = str->logical_last = run; 17233965Sjdp else { 17333965Sjdp run->logical_prev = str->logical_last; 17433965Sjdp str->logical_last->logical_next = run; 17533965Sjdp str->logical_last = run; 17633965Sjdp } 17733965Sjdp 17833965Sjdp return run; 17933965Sjdp} 18033965Sjdp 18133965Sjdpstatic void 18233965Sjdp_ucadd_rtl_segment(ucstring_t *str, unsigned long *source, unsigned long start, 18333965Sjdp unsigned long end) 18433965Sjdp{ 18533965Sjdp unsigned long s, e; 18633965Sjdp ucrun_t *run, *lrun; 18733965Sjdp 18833965Sjdp /* 18933965Sjdp * This is used to splice runs into strings with overall LTR direction. 19033965Sjdp * The `lrun' variable will never be NULL because at least one LTR run was 19133965Sjdp * added before this RTL run. 19233965Sjdp */ 19333965Sjdp lrun = str->visual_last; 19433965Sjdp 19533965Sjdp for (e = s = start; s < end;) { 19633965Sjdp for (; e < end && ISRTL_NEUTRAL(source[e]); e++) ; 197130561Sobrien 19833965Sjdp if (e > s) { 19933965Sjdp run = _add_run(str, source, s, e, UCPGBA_RTL); 20033965Sjdp 20160484Sobrien /* 20233965Sjdp * Add the run to the visual list for cursor traversal. 20333965Sjdp */ 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