133965Sjdp/*	$NetBSD$	*/
233965Sjdp
333965Sjdp/* OpenLDAP: pkg/ldap/libraries/liblunicode/ucdata/ucpgba.c,v 1.7.2.5 2010/04/13 20:23:04 kurt Exp */
4130561Sobrien/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5130561Sobrien *
633965Sjdp * Copyright 1998-2010 The OpenLDAP Foundation.
733965Sjdp * All rights reserved.
833965Sjdp *
933965Sjdp * Redistribution and use in source and binary forms, with or without
1033965Sjdp * modification, are permitted only as authorized by the OpenLDAP
1133965Sjdp * Public License.
1233965Sjdp *
1333965Sjdp * A copy of this license is available in file LICENSE in the
1433965Sjdp * top-level directory of the distribution or, alternatively, at
1533965Sjdp * <http://www.OpenLDAP.org/license.html>.
1633965Sjdp */
1733965Sjdp/* Copyright 2001 Computing Research Labs, New Mexico State University
1833965Sjdp *
1933965Sjdp * Permission is hereby granted, free of charge, to any person obtaining a
2033965Sjdp * copy of this software and associated documentation files (the "Software"),
2133965Sjdp * to deal in the Software without restriction, including without limitation
2233965Sjdp * the rights to use, copy, modify, merge, publish, distribute, sublicense,
2333965Sjdp * and/or sell copies of the Software, and to permit persons to whom the
2433965Sjdp * Software is furnished to do so, subject to the following conditions:
2533965Sjdp *
2633965Sjdp * The above copyright notice and this permission notice shall be included in
2733965Sjdp * all copies or substantial portions of the Software.
2833965Sjdp *
2933965Sjdp * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3033965Sjdp * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
3133965Sjdp * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
3233965Sjdp * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
33130561Sobrien * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
34130561Sobrien * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
3533965Sjdp * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3633965Sjdp */
3733965Sjdp/* Id: ucpgba.c,v 1.5 2001/01/02 18:46:20 mleisher Exp */
3833965Sjdp
3933965Sjdp#include "portable.h"
4033965Sjdp
4133965Sjdp#include <stdio.h>
4233965Sjdp#include <stdlib.h>
4333965Sjdp
4433965Sjdp#include "ucdata.h"
4533965Sjdp#include "ucpgba.h"
4633965Sjdp
4733965Sjdp/*
4833965Sjdp * These macros are used while reordering of RTL runs of text for the
4933965Sjdp * special case of non-spacing characters being in runs of weakly
5033965Sjdp * directional text.  They check for weak and non-spacing, and digits and
5133965Sjdp * non-spacing.
5233965Sjdp */
5333965Sjdp#define ISWEAKSPECIAL(cc)  ucisprop(cc, UC_EN|UC_ES|UC_MN, UC_ET|UC_AN|UC_CS)
5433965Sjdp#define ISDIGITSPECIAL(cc) ucisprop(cc, UC_ND|UC_MN, 0)
5533965Sjdp
5633965Sjdp/*
5733965Sjdp * These macros are used while breaking a string into runs of text in
5833965Sjdp * different directions.  Descriptions:
5933965Sjdp *
6033965Sjdp * ISLTR_LTR - Test for members of an LTR run in an LTR context.  This looks
6133965Sjdp *             for characters with ltr, non-spacing, weak, and neutral
6233965Sjdp *             properties.
6333965Sjdp *
6433965Sjdp * ISRTL_RTL - Test for members of an RTL run in an RTL context.  This looks
6533965Sjdp *             for characters with rtl, non-spacing, weak, and neutral
6633965Sjdp *             properties.
6733965Sjdp *
6833965Sjdp * ISRTL_NEUTRAL  - Test for RTL or neutral characters.
6933965Sjdp *
7033965Sjdp * ISWEAK_NEUTRAL - Test for weak or neutral characters.
7133965Sjdp */
7233965Sjdp#define ISLTR_LTR(cc) ucisprop(cc, UC_L|UC_MN|UC_EN|UC_ES,\
7333965Sjdp                               UC_ET|UC_CS|UC_B|UC_S|UC_WS|UC_ON)
7433965Sjdp
7533965Sjdp#define ISRTL_RTL(cc) ucisprop(cc, UC_R|UC_MN|UC_EN|UC_ES,\
7633965Sjdp                               UC_ET|UC_AN|UC_CS|UC_B|UC_S|UC_WS|UC_ON)
7733965Sjdp
7899461Sobrien#define ISRTL_NEUTRAL(cc) ucisprop(cc, UC_R, UC_B|UC_S|UC_WS|UC_ON)
7933965Sjdp#define ISWEAK_NEUTRAL(cc) ucisprop(cc, UC_EN|UC_ES, \
8033965Sjdp                                    UC_B|UC_S|UC_WS|UC_ON|UC_ET|UC_AN|UC_CS)
8133965Sjdp
8233965Sjdp/*
8333965Sjdp * This table is temporarily hard-coded here until it can be constructed
8433965Sjdp * automatically somehow.
8533965Sjdp */
8633965Sjdpstatic unsigned long _symmetric_pairs[] = {
8733965Sjdp    0x0028, 0x0029, 0x0029, 0x0028, 0x003C, 0x003E, 0x003E, 0x003C,
8833965Sjdp    0x005B, 0x005D, 0x005D, 0x005B, 0x007B, 0x007D, 0x007D, 0x007B,
8933965Sjdp    0x2045, 0x2046, 0x2046, 0x2045, 0x207D, 0x207E, 0x207E, 0x207D,
9033965Sjdp    0x208D, 0x208E, 0x208E, 0x208D, 0x3008, 0x3009, 0x3009, 0x3008,
9133965Sjdp    0x300A, 0x300B, 0x300B, 0x300A, 0x300C, 0x300D, 0x300D, 0x300C,
9233965Sjdp    0x300E, 0x300F, 0x300F, 0x300E, 0x3010, 0x3011, 0x3011, 0x3010,
9333965Sjdp    0x3014, 0x3015, 0x3015, 0x3014, 0x3016, 0x3017, 0x3017, 0x3016,
9433965Sjdp    0x3018, 0x3019, 0x3019, 0x3018, 0x301A, 0x301B, 0x301B, 0x301A,
9533965Sjdp    0xFD3E, 0xFD3F, 0xFD3F, 0xFD3E, 0xFE59, 0xFE5A, 0xFE5A, 0xFE59,
9633965Sjdp    0xFE5B, 0xFE5C, 0xFE5C, 0xFE5B, 0xFE5D, 0xFE5E, 0xFE5E, 0xFE5D,
9733965Sjdp    0xFF08, 0xFF09, 0xFF09, 0xFF08, 0xFF3B, 0xFF3D, 0xFF3D, 0xFF3B,
9833965Sjdp    0xFF5B, 0xFF5D, 0xFF5D, 0xFF5B, 0xFF62, 0xFF63, 0xFF63, 0xFF62,
9933965Sjdp};
10033965Sjdp
10133965Sjdpstatic int _symmetric_pairs_size =
10233965Sjdpsizeof(_symmetric_pairs)/sizeof(_symmetric_pairs[0]);
10333965Sjdp
10433965Sjdp/*
10533965Sjdp * This routine looks up the other form of a symmetric pair.
10633965Sjdp */
10733965Sjdpstatic unsigned long
10833965Sjdp_ucsymmetric_pair(unsigned long c)
10933965Sjdp{
11033965Sjdp    int i;
11133965Sjdp
11233965Sjdp    for (i = 0; i < _symmetric_pairs_size; i += 2) {
11333965Sjdp        if (_symmetric_pairs[i] == c)
11433965Sjdp          return _symmetric_pairs[i+1];
11533965Sjdp    }
11633965Sjdp    return c;
11733965Sjdp}
11833965Sjdp
11933965Sjdp/*
12033965Sjdp * This routine creates a new run, copies the text into it, links it into the
12133965Sjdp * logical text order chain and returns it to the caller to be linked into
12233965Sjdp * the visual text order chain.
12333965Sjdp */
12433965Sjdpstatic ucrun_t *
12533965Sjdp_add_run(ucstring_t *str, unsigned long *src,
12633965Sjdp         unsigned long start, unsigned long end, int direction)
12733965Sjdp{
12833965Sjdp    long i, t;
12933965Sjdp    ucrun_t *run;
13033965Sjdp
13133965Sjdp    run = (ucrun_t *) malloc(sizeof(ucrun_t));
13233965Sjdp    run->visual_next = run->visual_prev = 0;
13333965Sjdp    run->direction = direction;
13433965Sjdp
13533965Sjdp    run->cursor = ~0;
13633965Sjdp
13733965Sjdp    run->chars = (unsigned long *)
13833965Sjdp        malloc(sizeof(unsigned long) * ((end - start) << 1));
13933965Sjdp    run->positions = run->chars + (end - start);
14033965Sjdp
14133965Sjdp    run->source = src;
14233965Sjdp    run->start = start;
14333965Sjdp    run->end = end;
14433965Sjdp
14533965Sjdp    if (direction == UCPGBA_RTL) {
14633965Sjdp        /*
14733965Sjdp         * Copy the source text into the run in reverse order and select
14833965Sjdp         * replacements for the pairwise punctuation and the <> characters.
14933965Sjdp         */
15033965Sjdp        for (i = 0, t = end - 1; start < end; start++, t--, i++) {
15133965Sjdp            run->positions[i] = t;
15233965Sjdp            if (ucissymmetric(src[t]) || src[t] == '<' || src[t] == '>')
15333965Sjdp              run->chars[i] = _ucsymmetric_pair(src[t]);
15433965Sjdp            else
15533965Sjdp              run->chars[i] = src[t];
15633965Sjdp        }
15733965Sjdp    } else {
15833965Sjdp        /*
15933965Sjdp         * Copy the source text into the run directly.
16033965Sjdp         */
16133965Sjdp        for (i = start; i < end; i++) {
16233965Sjdp            run->positions[i - start] = i;
16333965Sjdp            run->chars[i - start] = src[i];
16433965Sjdp        }
16533965Sjdp    }
16633965Sjdp
16733965Sjdp    /*
16833965Sjdp     * Add the run to the logical list for cursor traversal.
16933965Sjdp     */
17033965Sjdp    if (str->logical_first == 0)
17133965Sjdp      str->logical_first = str->logical_last = run;
17233965Sjdp    else {
17333965Sjdp        run->logical_prev = str->logical_last;
17433965Sjdp        str->logical_last->logical_next = run;
17533965Sjdp        str->logical_last = run;
17633965Sjdp    }
17733965Sjdp
17833965Sjdp    return run;
17933965Sjdp}
18033965Sjdp
18133965Sjdpstatic void
18233965Sjdp_ucadd_rtl_segment(ucstring_t *str, unsigned long *source, unsigned long start,
18333965Sjdp                   unsigned long end)
18433965Sjdp{
18533965Sjdp    unsigned long s, e;
18633965Sjdp    ucrun_t *run, *lrun;
18733965Sjdp
18833965Sjdp    /*
18933965Sjdp     * This is used to splice runs into strings with overall LTR direction.
19033965Sjdp     * The `lrun' variable will never be NULL because at least one LTR run was
19133965Sjdp     * added before this RTL run.
19233965Sjdp     */
19333965Sjdp    lrun = str->visual_last;
19433965Sjdp
19533965Sjdp    for (e = s = start; s < end;) {
19633965Sjdp        for (; e < end && ISRTL_NEUTRAL(source[e]); e++) ;
197130561Sobrien
19833965Sjdp        if (e > s) {
19933965Sjdp            run = _add_run(str, source, s, e, UCPGBA_RTL);
20033965Sjdp
20160484Sobrien            /*
20233965Sjdp             * Add the run to the visual list for cursor traversal.
20333965Sjdp             */
204            if (str->visual_first != 0) {
205                if (str->direction == UCPGBA_LTR) {
206                    run->visual_prev = lrun;
207                    run->visual_next = lrun->visual_next;
208                    if (lrun->visual_next != 0)
209                      lrun->visual_next->visual_prev = run;
210                    lrun->visual_next = run;
211                    if (lrun == str->visual_last)
212                      str->visual_last = run;
213                } else {
214                    run->visual_next = str->visual_first;
215                    str->visual_first->visual_prev = run;
216                    str->visual_first = run;
217                }
218            } else
219              str->visual_first = str->visual_last = run;
220        }
221
222        /*
223         * Handle digits in a special way.  This makes sure the weakly
224         * directional characters appear on the expected sides of a number
225         * depending on whether that number is Arabic or not.
226         */
227        for (s = e; e < end && ISWEAKSPECIAL(source[e]); e++) {
228            if (!ISDIGITSPECIAL(source[e]) &&
229                (e + 1 == end || !ISDIGITSPECIAL(source[e + 1])))
230              break;
231        }
232
233        if (e > s) {
234            run = _add_run(str, source, s, e, UCPGBA_LTR);
235
236            /*
237             * Add the run to the visual list for cursor traversal.
238             */
239            if (str->visual_first != 0) {
240                if (str->direction == UCPGBA_LTR) {
241                    run->visual_prev = lrun;
242                    run->visual_next = lrun->visual_next;
243                    if (lrun->visual_next != 0)
244                      lrun->visual_next->visual_prev = run;
245                    lrun->visual_next = run;
246                    if (lrun == str->visual_last)
247                      str->visual_last = run;
248                } else {
249                    run->visual_next = str->visual_first;
250                    str->visual_first->visual_prev = run;
251                    str->visual_first = run;
252                }
253            } else
254              str->visual_first = str->visual_last = run;
255        }
256
257        /*
258         * Collect all weak non-digit sequences for an RTL segment.  These
259         * will appear as part of the next RTL segment or will be added as
260         * an RTL segment by themselves.
261         */
262        for (s = e; e < end && ucisweak(source[e]) && !ucisdigit(source[e]);
263             e++) ;
264    }
265
266    /*
267     * Capture any weak non-digit sequences that occur at the end of the RTL
268     * run.
269     */
270    if (e > s) {
271        run = _add_run(str, source, s, e, UCPGBA_RTL);
272
273        /*
274         * Add the run to the visual list for cursor traversal.
275         */
276        if (str->visual_first != 0) {
277            if (str->direction == UCPGBA_LTR) {
278                run->visual_prev = lrun;
279                run->visual_next = lrun->visual_next;
280                if (lrun->visual_next != 0)
281                  lrun->visual_next->visual_prev = run;
282                lrun->visual_next = run;
283                if (lrun == str->visual_last)
284                  str->visual_last = run;
285            } else {
286                run->visual_next = str->visual_first;
287                str->visual_first->visual_prev = run;
288                str->visual_first = run;
289            }
290        } else
291          str->visual_first = str->visual_last = run;
292    }
293}
294
295static void
296_ucadd_ltr_segment(ucstring_t *str, unsigned long *source, unsigned long start,
297                   unsigned long end)
298{
299    ucrun_t *run;
300
301    run = _add_run(str, source, start, end, UCPGBA_LTR);
302
303    /*
304     * Add the run to the visual list for cursor traversal.
305     */
306    if (str->visual_first != 0) {
307        if (str->direction == UCPGBA_LTR) {
308            run->visual_prev = str->visual_last;
309            str->visual_last->visual_next = run;
310            str->visual_last = run;
311        } else {
312            run->visual_next = str->visual_first;
313            str->visual_first->visual_prev = run;
314            str->visual_first = run;
315        }
316    } else
317      str->visual_first = str->visual_last = run;
318}
319
320ucstring_t *
321ucstring_create(unsigned long *source, unsigned long start, unsigned long end,
322                int default_direction, int cursor_motion)
323{
324    int rtl_first;
325    unsigned long s, e, ld;
326    ucstring_t *str;
327
328    str = (ucstring_t *) malloc(sizeof(ucstring_t));
329
330    /*
331     * Set the initial values.
332     */
333    str->cursor_motion = cursor_motion;
334    str->logical_first = str->logical_last = 0;
335    str->visual_first = str->visual_last = str->cursor = 0;
336    str->source = source;
337    str->start = start;
338    str->end = end;
339
340    /*
341     * If the length of the string is 0, then just return it at this point.
342     */
343    if (start == end)
344      return str;
345
346    /*
347     * This flag indicates whether the collection loop for RTL is called
348     * before the LTR loop the first time.
349     */
350    rtl_first = 0;
351
352    /*
353     * Look for the first character in the string that has strong
354     * directionality.
355     */
356    for (s = start; s < end && !ucisstrong(source[s]); s++) ;
357
358    if (s == end)
359      /*
360       * If the string contains no characters with strong directionality, use
361       * the default direction.
362       */
363      str->direction = default_direction;
364    else
365      str->direction = ucisrtl(source[s]) ? UCPGBA_RTL : UCPGBA_LTR;
366
367    if (str->direction == UCPGBA_RTL)
368      /*
369       * Set the flag that causes the RTL collection loop to run first.
370       */
371      rtl_first = 1;
372
373    /*
374     * This loop now separates the string into runs based on directionality.
375     */
376    for (s = e = 0; s < end; s = e) {
377        if (!rtl_first) {
378            /*
379             * Determine the next run of LTR text.
380             */
381
382            ld = s;
383            while (e < end && ISLTR_LTR(source[e])) {
384                if (ucisdigit(source[e]) &&
385                    !(0x660 <= source[e] && source[e] <= 0x669))
386                  ld = e;
387                e++;
388            }
389            if (str->direction != UCPGBA_LTR) {
390                while (e > ld && ISWEAK_NEUTRAL(source[e - 1]))
391                  e--;
392            }
393
394            /*
395             * Add the LTR segment to the string.
396             */
397            if (e > s)
398              _ucadd_ltr_segment(str, source, s, e);
399        }
400
401        /*
402         * Determine the next run of RTL text.
403         */
404        ld = s = e;
405        while (e < end && ISRTL_RTL(source[e])) {
406            if (ucisdigit(source[e]) &&
407                !(0x660 <= source[e] && source[e] <= 0x669))
408              ld = e;
409            e++;
410        }
411        if (str->direction != UCPGBA_RTL) {
412            while (e > ld && ISWEAK_NEUTRAL(source[e - 1]))
413              e--;
414        }
415
416        /*
417         * Add the RTL segment to the string.
418         */
419        if (e > s)
420          _ucadd_rtl_segment(str, source, s, e);
421
422        /*
423         * Clear the flag that allowed the RTL collection loop to run first
424         * for strings with overall RTL directionality.
425         */
426        rtl_first = 0;
427    }
428
429    /*
430     * Set up the initial cursor run.
431     */
432    str->cursor = str->logical_first;
433    if (str != 0)
434      str->cursor->cursor = (str->cursor->direction == UCPGBA_RTL) ?
435          str->cursor->end - str->cursor->start : 0;
436
437    return str;
438}
439
440void
441ucstring_free(ucstring_t *s)
442{
443    ucrun_t *l, *r;
444
445    if (s == 0)
446      return;
447
448    for (l = 0, r = s->visual_first; r != 0; r = r->visual_next) {
449        if (r->end > r->start)
450          free((char *) r->chars);
451        if (l)
452          free((char *) l);
453        l = r;
454    }
455    if (l)
456      free((char *) l);
457
458    free((char *) s);
459}
460
461int
462ucstring_set_cursor_motion(ucstring_t *str, int cursor_motion)
463{
464    int n;
465
466    if (str == 0)
467      return -1;
468
469    n = str->cursor_motion;
470    str->cursor_motion = cursor_motion;
471    return n;
472}
473
474static int
475_ucstring_visual_cursor_right(ucstring_t *str, int count)
476{
477    int cnt = count;
478    unsigned long size;
479    ucrun_t *cursor;
480
481    if (str == 0)
482      return 0;
483
484    cursor = str->cursor;
485    while (cnt > 0) {
486        size = cursor->end - cursor->start;
487        if ((cursor->direction == UCPGBA_RTL && cursor->cursor + 1 == size) ||
488            cursor->cursor + 1 > size) {
489            /*
490             * If the next run is NULL, then the cursor is already on the
491             * far right end already.
492             */
493            if (cursor->visual_next == 0)
494              /*
495               * If movement occured, then report it.
496               */
497              return (cnt != count);
498
499            /*
500             * Move to the next run.
501             */
502            str->cursor = cursor = cursor->visual_next;
503            cursor->cursor = (cursor->direction == UCPGBA_RTL) ? -1 : 0;
504            size = cursor->end - cursor->start;
505        } else
506          cursor->cursor++;
507        cnt--;
508    }
509    return 1;
510}
511
512static int
513_ucstring_logical_cursor_right(ucstring_t *str, int count)
514{
515    int cnt = count;
516    unsigned long size;
517    ucrun_t *cursor;
518
519    if (str == 0)
520      return 0;
521
522    cursor = str->cursor;
523    while (cnt > 0) {
524        size = cursor->end - cursor->start;
525        if (str->direction == UCPGBA_RTL) {
526            if (cursor->direction == UCPGBA_RTL) {
527                if (cursor->cursor + 1 == size) {
528                    if (cursor == str->logical_first)
529                      /*
530                       * Already at the beginning of the string.
531                       */
532                      return (cnt != count);
533
534                    str->cursor = cursor = cursor->logical_prev;
535                    size = cursor->end - cursor->start;
536                    cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
537                        size : 0;
538                } else
539                  cursor->cursor++;
540            } else {
541                if (cursor->cursor == 0) {
542                    if (cursor == str->logical_first)
543                      /*
544                       * At the beginning of the string already.
545                       */
546                      return (cnt != count);
547
548                    str->cursor = cursor = cursor->logical_prev;
549                    size = cursor->end - cursor->start;
550                    cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
551                        size : 0;
552                } else
553                  cursor->cursor--;
554            }
555        } else {
556            if (cursor->direction == UCPGBA_RTL) {
557                if (cursor->cursor == 0) {
558                    if (cursor == str->logical_last)
559                      /*
560                       * Already at the end of the string.
561                       */
562                      return (cnt != count);
563
564                    str->cursor = cursor = cursor->logical_next;
565                    size = cursor->end - cursor->start;
566                    cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
567                        0 : size - 1;
568                } else
569                  cursor->cursor--;
570            } else {
571                if (cursor->cursor + 1 > size) {
572                    if (cursor == str->logical_last)
573                      /*
574                       * Already at the end of the string.
575                       */
576                      return (cnt != count);
577
578                    str->cursor = cursor = cursor->logical_next;
579                    cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
580                        0 : size - 1;
581                } else
582                  cursor->cursor++;
583            }
584        }
585        cnt--;
586    }
587    return 1;
588}
589
590int
591ucstring_cursor_right(ucstring_t *str, int count)
592{
593    if (str == 0)
594      return 0;
595    return (str->cursor_motion == UCPGBA_CURSOR_VISUAL) ?
596        _ucstring_visual_cursor_right(str, count) :
597        _ucstring_logical_cursor_right(str, count);
598}
599
600static int
601_ucstring_visual_cursor_left(ucstring_t *str, int count)
602{
603    int cnt = count;
604    unsigned long size;
605    ucrun_t *cursor;
606
607    if (str == 0)
608      return 0;
609
610    cursor = str->cursor;
611    while (cnt > 0) {
612        size = cursor->end - cursor->start;
613        if ((cursor->direction == UCPGBA_LTR && cursor->cursor == 0) ||
614            cursor->cursor - 1 < -1) {
615            /*
616             * If the preceding run is NULL, then the cursor is already on the
617             * far left end already.
618             */
619            if (cursor->visual_prev == 0)
620              /*
621               * If movement occured, then report it.
622               */
623              return (cnt != count);
624
625            /*
626             * Move to the previous run.
627             */
628            str->cursor = cursor = cursor->visual_prev;
629            size = cursor->end - cursor->start;
630            cursor->cursor = (cursor->direction == UCPGBA_RTL) ?
631                size : size - 1;
632        } else
633          cursor->cursor--;
634        cnt--;
635    }
636    return 1;
637}
638
639static int
640_ucstring_logical_cursor_left(ucstring_t *str, int count)
641{
642    int cnt = count;
643    unsigned long size;
644    ucrun_t *cursor;
645
646    if (str == 0)
647      return 0;
648
649    cursor = str->cursor;
650    while (cnt > 0) {
651        size = cursor->end - cursor->start;
652        if (str->direction == UCPGBA_RTL) {
653            if (cursor->direction == UCPGBA_RTL) {
654                if (cursor->cursor == -1) {
655                    if (cursor == str->logical_last)
656                      /*
657                       * Already at the end of the string.
658                       */
659                      return (cnt != count);
660
661                    str->cursor = cursor = cursor->logical_next;
662                    size = cursor->end - cursor->start;
663                    cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
664                        0 : size - 1;
665                } else
666                  cursor->cursor--;
667            } else {
668                if (cursor->cursor + 1 > size) {
669                    if (cursor == str->logical_last)
670                      /*
671                       * At the end of the string already.
672                       */
673                      return (cnt != count);
674
675                    str->cursor = cursor = cursor->logical_next;
676                    size = cursor->end - cursor->start;
677                    cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
678                        0 : size - 1;
679                } else
680                  cursor->cursor++;
681            }
682        } else {
683            if (cursor->direction == UCPGBA_RTL) {
684                if (cursor->cursor + 1 == size) {
685                    if (cursor == str->logical_first)
686                      /*
687                       * Already at the beginning of the string.
688                       */
689                      return (cnt != count);
690
691                    str->cursor = cursor = cursor->logical_prev;
692                    size = cursor->end - cursor->start;
693                    cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
694                        size : 0;
695                } else
696                  cursor->cursor++;
697            } else {
698                if (cursor->cursor == 0) {
699                    if (cursor == str->logical_first)
700                      /*
701                       * Already at the beginning of the string.
702                       */
703                      return (cnt != count);
704
705                    str->cursor = cursor = cursor->logical_prev;
706                    cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
707                        size : 0;
708                } else
709                  cursor->cursor--;
710            }
711        }
712        cnt--;
713    }
714    return 1;
715}
716
717int
718ucstring_cursor_left(ucstring_t *str, int count)
719{
720    if (str == 0)
721      return 0;
722    return (str->cursor_motion == UCPGBA_CURSOR_VISUAL) ?
723        _ucstring_visual_cursor_left(str, count) :
724        _ucstring_logical_cursor_left(str, count);
725}
726
727void
728ucstring_cursor_info(ucstring_t *str, int *direction, unsigned long *position)
729{
730    long c;
731    unsigned long size;
732    ucrun_t *cursor;
733
734    if (str == 0 || direction == 0 || position == 0)
735      return;
736
737    cursor = str->cursor;
738
739    *direction = cursor->direction;
740
741    c = cursor->cursor;
742    size = cursor->end - cursor->start;
743
744    if (c == size)
745      *position = (cursor->direction == UCPGBA_RTL) ?
746          cursor->start : cursor->positions[c - 1];
747    else if (c == -1)
748      *position = (cursor->direction == UCPGBA_RTL) ?
749          cursor->end : cursor->start;
750    else
751      *position = cursor->positions[c];
752}
753