Imported Upstream version 1.20.1
[platform/upstream/krb5.git] / src / lib / krb5 / unicode / ucdata / ucpgba.c
1 /*
2  * Copyright 1998-2008 The OpenLDAP Foundation.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted only as authorized by the OpenLDAP
7  * Public License.
8  *
9  * A copy of this license is available in file LICENSE in the
10  * top-level directory of the distribution or, alternatively, at
11  * <https://www.OpenLDAP.org/license.html>.
12  */
13 /* Copyright 2001 Computing Research Labs, New Mexico State University
14  *
15  * Permission is hereby granted, free of charge, to any person obtaining a
16  * copy of this software and associated documentation files (the "Software"),
17  * to deal in the Software without restriction, including without limitation
18  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
19  * and/or sell copies of the Software, and to permit persons to whom the
20  * Software is furnished to do so, subject to the following conditions:
21  *
22  * The above copyright notice and this permission notice shall be included in
23  * all copies or substantial portions of the Software.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
28  * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
29  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
30  * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
31  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32  */
33
34 /*
35  * This work is part of OpenLDAP Software <https://www.openldap.org/>.
36  * $OpenLDAP: pkg/ldap/libraries/liblunicode/ucdata/ucpgba.c,v 1.9 2008/01/07 23:20:05 kurt Exp $
37  * $Id: ucpgba.c,v 1.5 2001/01/02 18:46:20 mleisher Exp $
38  */
39
40 #include "k5-int.h"
41 #include "k5-utf8.h"
42 #include "k5-unicode.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
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 *
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
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
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 *
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
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
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
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 occured, 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
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
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
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 occured, 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
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
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
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 }