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