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