xref: /netbsd-src/external/bsd/openldap/dist/libraries/liblunicode/ucdata/ucpgba.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: ucpgba.c,v 1.1.1.4 2014/05/28 09:58:44 tron Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 1998-2014 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  */
86 static 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 
101 static int _symmetric_pairs_size =
102 sizeof(_symmetric_pairs)/sizeof(_symmetric_pairs[0]);
103 
104 /*
105  * This routine looks up the other form of a symmetric pair.
106  */
107 static 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  */
124 static 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 
181 static 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 
295 static 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 
320 ucstring_t *
321 ucstring_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 
440 void
441 ucstring_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 
461 int
462 ucstring_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 
474 static 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 
512 static 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 
590 int
591 ucstring_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 
600 static 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 
639 static 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 
717 int
718 ucstring_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 
727 void
728 ucstring_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