Back to index

openldap  2.4.31
ucpgba.c
Go to the documentation of this file.
00001 /* $OpenLDAP$ */
00002 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00003  *
00004  * Copyright 1998-2012 The OpenLDAP Foundation.
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted only as authorized by the OpenLDAP
00009  * Public License.
00010  *
00011  * A copy of this license is available in file LICENSE in the
00012  * top-level directory of the distribution or, alternatively, at
00013  * <http://www.OpenLDAP.org/license.html>.
00014  */
00015 /* Copyright 2001 Computing Research Labs, New Mexico State University
00016  *
00017  * Permission is hereby granted, free of charge, to any person obtaining a
00018  * copy of this software and associated documentation files (the "Software"),
00019  * to deal in the Software without restriction, including without limitation
00020  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
00021  * and/or sell copies of the Software, and to permit persons to whom the
00022  * Software is furnished to do so, subject to the following conditions:
00023  *
00024  * The above copyright notice and this permission notice shall be included in
00025  * all copies or substantial portions of the Software.
00026  *
00027  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00028  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00029  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
00030  * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
00031  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
00032  * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
00033  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00034  */
00035 /* $Id: ucpgba.c,v 1.5 2001/01/02 18:46:20 mleisher Exp $ */
00036 
00037 #include "portable.h"
00038 
00039 #include <stdio.h>
00040 #include <stdlib.h>
00041 
00042 #include "ucdata.h"
00043 #include "ucpgba.h"
00044 
00045 /*
00046  * These macros are used while reordering of RTL runs of text for the
00047  * special case of non-spacing characters being in runs of weakly
00048  * directional text.  They check for weak and non-spacing, and digits and
00049  * non-spacing.
00050  */
00051 #define ISWEAKSPECIAL(cc)  ucisprop(cc, UC_EN|UC_ES|UC_MN, UC_ET|UC_AN|UC_CS)
00052 #define ISDIGITSPECIAL(cc) ucisprop(cc, UC_ND|UC_MN, 0)
00053 
00054 /*
00055  * These macros are used while breaking a string into runs of text in
00056  * different directions.  Descriptions:
00057  *
00058  * ISLTR_LTR - Test for members of an LTR run in an LTR context.  This looks
00059  *             for characters with ltr, non-spacing, weak, and neutral
00060  *             properties.
00061  *
00062  * ISRTL_RTL - Test for members of an RTL run in an RTL context.  This looks
00063  *             for characters with rtl, non-spacing, weak, and neutral
00064  *             properties.
00065  *
00066  * ISRTL_NEUTRAL  - Test for RTL or neutral characters.
00067  *
00068  * ISWEAK_NEUTRAL - Test for weak or neutral characters.
00069  */
00070 #define ISLTR_LTR(cc) ucisprop(cc, UC_L|UC_MN|UC_EN|UC_ES,\
00071                                UC_ET|UC_CS|UC_B|UC_S|UC_WS|UC_ON)
00072 
00073 #define ISRTL_RTL(cc) ucisprop(cc, UC_R|UC_MN|UC_EN|UC_ES,\
00074                                UC_ET|UC_AN|UC_CS|UC_B|UC_S|UC_WS|UC_ON)
00075 
00076 #define ISRTL_NEUTRAL(cc) ucisprop(cc, UC_R, UC_B|UC_S|UC_WS|UC_ON)
00077 #define ISWEAK_NEUTRAL(cc) ucisprop(cc, UC_EN|UC_ES, \
00078                                     UC_B|UC_S|UC_WS|UC_ON|UC_ET|UC_AN|UC_CS)
00079 
00080 /*
00081  * This table is temporarily hard-coded here until it can be constructed
00082  * automatically somehow.
00083  */
00084 static unsigned long _symmetric_pairs[] = {
00085     0x0028, 0x0029, 0x0029, 0x0028, 0x003C, 0x003E, 0x003E, 0x003C,
00086     0x005B, 0x005D, 0x005D, 0x005B, 0x007B, 0x007D, 0x007D, 0x007B,
00087     0x2045, 0x2046, 0x2046, 0x2045, 0x207D, 0x207E, 0x207E, 0x207D,
00088     0x208D, 0x208E, 0x208E, 0x208D, 0x3008, 0x3009, 0x3009, 0x3008,
00089     0x300A, 0x300B, 0x300B, 0x300A, 0x300C, 0x300D, 0x300D, 0x300C,
00090     0x300E, 0x300F, 0x300F, 0x300E, 0x3010, 0x3011, 0x3011, 0x3010,
00091     0x3014, 0x3015, 0x3015, 0x3014, 0x3016, 0x3017, 0x3017, 0x3016,
00092     0x3018, 0x3019, 0x3019, 0x3018, 0x301A, 0x301B, 0x301B, 0x301A,
00093     0xFD3E, 0xFD3F, 0xFD3F, 0xFD3E, 0xFE59, 0xFE5A, 0xFE5A, 0xFE59,
00094     0xFE5B, 0xFE5C, 0xFE5C, 0xFE5B, 0xFE5D, 0xFE5E, 0xFE5E, 0xFE5D,
00095     0xFF08, 0xFF09, 0xFF09, 0xFF08, 0xFF3B, 0xFF3D, 0xFF3D, 0xFF3B,
00096     0xFF5B, 0xFF5D, 0xFF5D, 0xFF5B, 0xFF62, 0xFF63, 0xFF63, 0xFF62,
00097 };
00098 
00099 static int _symmetric_pairs_size =
00100 sizeof(_symmetric_pairs)/sizeof(_symmetric_pairs[0]);
00101 
00102 /*
00103  * This routine looks up the other form of a symmetric pair.
00104  */
00105 static unsigned long
00106 _ucsymmetric_pair(unsigned long c)
00107 {
00108     int i;
00109 
00110     for (i = 0; i < _symmetric_pairs_size; i += 2) {
00111         if (_symmetric_pairs[i] == c)
00112           return _symmetric_pairs[i+1];
00113     }
00114     return c;
00115 }
00116 
00117 /*
00118  * This routine creates a new run, copies the text into it, links it into the
00119  * logical text order chain and returns it to the caller to be linked into
00120  * the visual text order chain.
00121  */
00122 static ucrun_t *
00123 _add_run(ucstring_t *str, unsigned long *src,
00124          unsigned long start, unsigned long end, int direction)
00125 {
00126     long i, t;
00127     ucrun_t *run;
00128 
00129     run = (ucrun_t *) malloc(sizeof(ucrun_t));
00130     run->visual_next = run->visual_prev = 0;
00131     run->direction = direction;
00132 
00133     run->cursor = ~0;
00134 
00135     run->chars = (unsigned long *)
00136         malloc(sizeof(unsigned long) * ((end - start) << 1));
00137     run->positions = run->chars + (end - start);
00138 
00139     run->source = src;
00140     run->start = start;
00141     run->end = end;
00142 
00143     if (direction == UCPGBA_RTL) {
00144         /*
00145          * Copy the source text into the run in reverse order and select
00146          * replacements for the pairwise punctuation and the <> characters.
00147          */
00148         for (i = 0, t = end - 1; start < end; start++, t--, i++) {
00149             run->positions[i] = t;
00150             if (ucissymmetric(src[t]) || src[t] == '<' || src[t] == '>')
00151               run->chars[i] = _ucsymmetric_pair(src[t]);
00152             else
00153               run->chars[i] = src[t];
00154         }
00155     } else {
00156         /*
00157          * Copy the source text into the run directly.
00158          */
00159         for (i = start; i < end; i++) {
00160             run->positions[i - start] = i;
00161             run->chars[i - start] = src[i];
00162         }
00163     }
00164 
00165     /*
00166      * Add the run to the logical list for cursor traversal.
00167      */
00168     if (str->logical_first == 0)
00169       str->logical_first = str->logical_last = run;
00170     else {
00171         run->logical_prev = str->logical_last;
00172         str->logical_last->logical_next = run;
00173         str->logical_last = run;
00174     }
00175 
00176     return run;
00177 }
00178 
00179 static void
00180 _ucadd_rtl_segment(ucstring_t *str, unsigned long *source, unsigned long start,
00181                    unsigned long end)
00182 {
00183     unsigned long s, e;
00184     ucrun_t *run, *lrun;
00185 
00186     /*
00187      * This is used to splice runs into strings with overall LTR direction.
00188      * The `lrun' variable will never be NULL because at least one LTR run was
00189      * added before this RTL run.
00190      */
00191     lrun = str->visual_last;
00192 
00193     for (e = s = start; s < end;) {
00194         for (; e < end && ISRTL_NEUTRAL(source[e]); e++) ;
00195 
00196         if (e > s) {
00197             run = _add_run(str, source, s, e, UCPGBA_RTL);
00198 
00199             /*
00200              * Add the run to the visual list for cursor traversal.
00201              */
00202             if (str->visual_first != 0) {
00203                 if (str->direction == UCPGBA_LTR) {
00204                     run->visual_prev = lrun;
00205                     run->visual_next = lrun->visual_next;
00206                     if (lrun->visual_next != 0)
00207                       lrun->visual_next->visual_prev = run;
00208                     lrun->visual_next = run;
00209                     if (lrun == str->visual_last)
00210                       str->visual_last = run;
00211                 } else {
00212                     run->visual_next = str->visual_first;
00213                     str->visual_first->visual_prev = run;
00214                     str->visual_first = run;
00215                 }
00216             } else
00217               str->visual_first = str->visual_last = run;
00218         }
00219 
00220         /*
00221          * Handle digits in a special way.  This makes sure the weakly
00222          * directional characters appear on the expected sides of a number
00223          * depending on whether that number is Arabic or not.
00224          */
00225         for (s = e; e < end && ISWEAKSPECIAL(source[e]); e++) {
00226             if (!ISDIGITSPECIAL(source[e]) &&
00227                 (e + 1 == end || !ISDIGITSPECIAL(source[e + 1])))
00228               break;
00229         }
00230 
00231         if (e > s) {
00232             run = _add_run(str, source, s, e, UCPGBA_LTR);
00233 
00234             /*
00235              * Add the run to the visual list for cursor traversal.
00236              */
00237             if (str->visual_first != 0) {
00238                 if (str->direction == UCPGBA_LTR) {
00239                     run->visual_prev = lrun;
00240                     run->visual_next = lrun->visual_next;
00241                     if (lrun->visual_next != 0)
00242                       lrun->visual_next->visual_prev = run;
00243                     lrun->visual_next = run;
00244                     if (lrun == str->visual_last)
00245                       str->visual_last = run;
00246                 } else {
00247                     run->visual_next = str->visual_first;
00248                     str->visual_first->visual_prev = run;
00249                     str->visual_first = run;
00250                 }
00251             } else
00252               str->visual_first = str->visual_last = run;
00253         }
00254 
00255         /*
00256          * Collect all weak non-digit sequences for an RTL segment.  These
00257          * will appear as part of the next RTL segment or will be added as
00258          * an RTL segment by themselves.
00259          */
00260         for (s = e; e < end && ucisweak(source[e]) && !ucisdigit(source[e]);
00261              e++) ;
00262     }
00263 
00264     /*
00265      * Capture any weak non-digit sequences that occur at the end of the RTL
00266      * run.
00267      */
00268     if (e > s) {
00269         run = _add_run(str, source, s, e, UCPGBA_RTL);
00270 
00271         /*
00272          * Add the run to the visual list for cursor traversal.
00273          */
00274         if (str->visual_first != 0) {
00275             if (str->direction == UCPGBA_LTR) {
00276                 run->visual_prev = lrun;
00277                 run->visual_next = lrun->visual_next;
00278                 if (lrun->visual_next != 0)
00279                   lrun->visual_next->visual_prev = run;
00280                 lrun->visual_next = run;
00281                 if (lrun == str->visual_last)
00282                   str->visual_last = run;
00283             } else {
00284                 run->visual_next = str->visual_first;
00285                 str->visual_first->visual_prev = run;
00286                 str->visual_first = run;
00287             }
00288         } else
00289           str->visual_first = str->visual_last = run;
00290     }
00291 }
00292 
00293 static void
00294 _ucadd_ltr_segment(ucstring_t *str, unsigned long *source, unsigned long start,
00295                    unsigned long end)
00296 {
00297     ucrun_t *run;
00298 
00299     run = _add_run(str, source, start, end, UCPGBA_LTR);
00300 
00301     /*
00302      * Add the run to the visual list for cursor traversal.
00303      */
00304     if (str->visual_first != 0) {
00305         if (str->direction == UCPGBA_LTR) {
00306             run->visual_prev = str->visual_last;
00307             str->visual_last->visual_next = run;
00308             str->visual_last = run;
00309         } else {
00310             run->visual_next = str->visual_first;
00311             str->visual_first->visual_prev = run;
00312             str->visual_first = run;
00313         }
00314     } else
00315       str->visual_first = str->visual_last = run;
00316 }
00317 
00318 ucstring_t *
00319 ucstring_create(unsigned long *source, unsigned long start, unsigned long end,
00320                 int default_direction, int cursor_motion)
00321 {
00322     int rtl_first;
00323     unsigned long s, e, ld;
00324     ucstring_t *str;
00325 
00326     str = (ucstring_t *) malloc(sizeof(ucstring_t));
00327 
00328     /*
00329      * Set the initial values.
00330      */
00331     str->cursor_motion = cursor_motion;
00332     str->logical_first = str->logical_last = 0;
00333     str->visual_first = str->visual_last = str->cursor = 0;
00334     str->source = source;
00335     str->start = start;
00336     str->end = end;
00337 
00338     /*
00339      * If the length of the string is 0, then just return it at this point.
00340      */
00341     if (start == end)
00342       return str;
00343 
00344     /*
00345      * This flag indicates whether the collection loop for RTL is called
00346      * before the LTR loop the first time.
00347      */
00348     rtl_first = 0;
00349 
00350     /*
00351      * Look for the first character in the string that has strong
00352      * directionality.
00353      */
00354     for (s = start; s < end && !ucisstrong(source[s]); s++) ;
00355 
00356     if (s == end)
00357       /*
00358        * If the string contains no characters with strong directionality, use
00359        * the default direction.
00360        */
00361       str->direction = default_direction;
00362     else
00363       str->direction = ucisrtl(source[s]) ? UCPGBA_RTL : UCPGBA_LTR;
00364 
00365     if (str->direction == UCPGBA_RTL)
00366       /*
00367        * Set the flag that causes the RTL collection loop to run first.
00368        */
00369       rtl_first = 1;
00370 
00371     /*
00372      * This loop now separates the string into runs based on directionality.
00373      */
00374     for (s = e = 0; s < end; s = e) {
00375         if (!rtl_first) {
00376             /*
00377              * Determine the next run of LTR text.
00378              */
00379 
00380             ld = s;
00381             while (e < end && ISLTR_LTR(source[e])) {
00382                 if (ucisdigit(source[e]) &&
00383                     !(0x660 <= source[e] && source[e] <= 0x669))
00384                   ld = e;
00385                 e++;
00386             }
00387             if (str->direction != UCPGBA_LTR) {
00388                 while (e > ld && ISWEAK_NEUTRAL(source[e - 1]))
00389                   e--;
00390             }
00391 
00392             /*
00393              * Add the LTR segment to the string.
00394              */
00395             if (e > s)
00396               _ucadd_ltr_segment(str, source, s, e);
00397         }
00398 
00399         /*
00400          * Determine the next run of RTL text.
00401          */
00402         ld = s = e;
00403         while (e < end && ISRTL_RTL(source[e])) {
00404             if (ucisdigit(source[e]) &&
00405                 !(0x660 <= source[e] && source[e] <= 0x669))
00406               ld = e;
00407             e++;
00408         }
00409         if (str->direction != UCPGBA_RTL) {
00410             while (e > ld && ISWEAK_NEUTRAL(source[e - 1]))
00411               e--;
00412         }
00413 
00414         /*
00415          * Add the RTL segment to the string.
00416          */
00417         if (e > s)
00418           _ucadd_rtl_segment(str, source, s, e);
00419 
00420         /*
00421          * Clear the flag that allowed the RTL collection loop to run first
00422          * for strings with overall RTL directionality.
00423          */
00424         rtl_first = 0;
00425     }
00426 
00427     /*
00428      * Set up the initial cursor run.
00429      */
00430     str->cursor = str->logical_first;
00431     if (str != 0)
00432       str->cursor->cursor = (str->cursor->direction == UCPGBA_RTL) ?
00433           str->cursor->end - str->cursor->start : 0;
00434 
00435     return str;
00436 }
00437 
00438 void
00439 ucstring_free(ucstring_t *s)
00440 {
00441     ucrun_t *l, *r;
00442 
00443     if (s == 0)
00444       return;
00445 
00446     for (l = 0, r = s->visual_first; r != 0; r = r->visual_next) {
00447         if (r->end > r->start)
00448           free((char *) r->chars);
00449         if (l)
00450           free((char *) l);
00451         l = r;
00452     }
00453     if (l)
00454       free((char *) l);
00455 
00456     free((char *) s);
00457 }
00458 
00459 int
00460 ucstring_set_cursor_motion(ucstring_t *str, int cursor_motion)
00461 {
00462     int n;
00463 
00464     if (str == 0)
00465       return -1;
00466 
00467     n = str->cursor_motion;
00468     str->cursor_motion = cursor_motion;
00469     return n;
00470 }
00471 
00472 static int
00473 _ucstring_visual_cursor_right(ucstring_t *str, int count)
00474 {
00475     int cnt = count;
00476     unsigned long size;
00477     ucrun_t *cursor;
00478 
00479     if (str == 0)
00480       return 0;
00481 
00482     cursor = str->cursor;
00483     while (cnt > 0) {
00484         size = cursor->end - cursor->start;
00485         if ((cursor->direction == UCPGBA_RTL && cursor->cursor + 1 == size) ||
00486             cursor->cursor + 1 > size) {
00487             /*
00488              * If the next run is NULL, then the cursor is already on the
00489              * far right end already.
00490              */
00491             if (cursor->visual_next == 0)
00492               /*
00493                * If movement occured, then report it.
00494                */
00495               return (cnt != count);
00496 
00497             /*
00498              * Move to the next run.
00499              */
00500             str->cursor = cursor = cursor->visual_next;
00501             cursor->cursor = (cursor->direction == UCPGBA_RTL) ? -1 : 0;
00502             size = cursor->end - cursor->start;
00503         } else
00504           cursor->cursor++;
00505         cnt--;
00506     }
00507     return 1;
00508 }
00509 
00510 static int
00511 _ucstring_logical_cursor_right(ucstring_t *str, int count)
00512 {
00513     int cnt = count;
00514     unsigned long size;
00515     ucrun_t *cursor;
00516 
00517     if (str == 0)
00518       return 0;
00519 
00520     cursor = str->cursor;
00521     while (cnt > 0) {
00522         size = cursor->end - cursor->start;
00523         if (str->direction == UCPGBA_RTL) {
00524             if (cursor->direction == UCPGBA_RTL) {
00525                 if (cursor->cursor + 1 == size) {
00526                     if (cursor == str->logical_first)
00527                       /*
00528                        * Already at the beginning of the string.
00529                        */
00530                       return (cnt != count);
00531 
00532                     str->cursor = cursor = cursor->logical_prev;
00533                     size = cursor->end - cursor->start;
00534                     cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
00535                         size : 0;
00536                 } else
00537                   cursor->cursor++;
00538             } else {
00539                 if (cursor->cursor == 0) {
00540                     if (cursor == str->logical_first)
00541                       /*
00542                        * At the beginning of the string already.
00543                        */
00544                       return (cnt != count);
00545 
00546                     str->cursor = cursor = cursor->logical_prev;
00547                     size = cursor->end - cursor->start;
00548                     cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
00549                         size : 0;
00550                 } else
00551                   cursor->cursor--;
00552             }
00553         } else {
00554             if (cursor->direction == UCPGBA_RTL) {
00555                 if (cursor->cursor == 0) {
00556                     if (cursor == str->logical_last)
00557                       /*
00558                        * Already at the end of the string.
00559                        */
00560                       return (cnt != count);
00561 
00562                     str->cursor = cursor = cursor->logical_next;
00563                     size = cursor->end - cursor->start;
00564                     cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
00565                         0 : size - 1;
00566                 } else
00567                   cursor->cursor--;
00568             } else {
00569                 if (cursor->cursor + 1 > size) {
00570                     if (cursor == str->logical_last)
00571                       /*
00572                        * Already at the end of the string.
00573                        */
00574                       return (cnt != count);
00575 
00576                     str->cursor = cursor = cursor->logical_next;
00577                     cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
00578                         0 : size - 1;
00579                 } else
00580                   cursor->cursor++;
00581             }
00582         }
00583         cnt--;
00584     }
00585     return 1;
00586 }
00587 
00588 int
00589 ucstring_cursor_right(ucstring_t *str, int count)
00590 {
00591     if (str == 0)
00592       return 0;
00593     return (str->cursor_motion == UCPGBA_CURSOR_VISUAL) ?
00594         _ucstring_visual_cursor_right(str, count) :
00595         _ucstring_logical_cursor_right(str, count);
00596 }
00597 
00598 static int
00599 _ucstring_visual_cursor_left(ucstring_t *str, int count)
00600 {
00601     int cnt = count;
00602     unsigned long size;
00603     ucrun_t *cursor;
00604 
00605     if (str == 0)
00606       return 0;
00607 
00608     cursor = str->cursor;
00609     while (cnt > 0) {
00610         size = cursor->end - cursor->start;
00611         if ((cursor->direction == UCPGBA_LTR && cursor->cursor == 0) ||
00612             cursor->cursor - 1 < -1) {
00613             /*
00614              * If the preceding run is NULL, then the cursor is already on the
00615              * far left end already.
00616              */
00617             if (cursor->visual_prev == 0)
00618               /*
00619                * If movement occured, then report it.
00620                */
00621               return (cnt != count);
00622 
00623             /*
00624              * Move to the previous run.
00625              */
00626             str->cursor = cursor = cursor->visual_prev;
00627             size = cursor->end - cursor->start;
00628             cursor->cursor = (cursor->direction == UCPGBA_RTL) ?
00629                 size : size - 1;
00630         } else
00631           cursor->cursor--;
00632         cnt--;
00633     }
00634     return 1;
00635 }
00636 
00637 static int
00638 _ucstring_logical_cursor_left(ucstring_t *str, int count)
00639 {
00640     int cnt = count;
00641     unsigned long size;
00642     ucrun_t *cursor;
00643 
00644     if (str == 0)
00645       return 0;
00646 
00647     cursor = str->cursor;
00648     while (cnt > 0) {
00649         size = cursor->end - cursor->start;
00650         if (str->direction == UCPGBA_RTL) {
00651             if (cursor->direction == UCPGBA_RTL) {
00652                 if (cursor->cursor == -1) {
00653                     if (cursor == str->logical_last)
00654                       /*
00655                        * Already at the end of the string.
00656                        */
00657                       return (cnt != count);
00658 
00659                     str->cursor = cursor = cursor->logical_next;
00660                     size = cursor->end - cursor->start;
00661                     cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
00662                         0 : size - 1;
00663                 } else
00664                   cursor->cursor--;
00665             } else {
00666                 if (cursor->cursor + 1 > size) {
00667                     if (cursor == str->logical_last)
00668                       /*
00669                        * At the end of the string already.
00670                        */
00671                       return (cnt != count);
00672 
00673                     str->cursor = cursor = cursor->logical_next;
00674                     size = cursor->end - cursor->start;
00675                     cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
00676                         0 : size - 1;
00677                 } else
00678                   cursor->cursor++;
00679             }
00680         } else {
00681             if (cursor->direction == UCPGBA_RTL) {
00682                 if (cursor->cursor + 1 == size) {
00683                     if (cursor == str->logical_first)
00684                       /*
00685                        * Already at the beginning of the string.
00686                        */
00687                       return (cnt != count);
00688 
00689                     str->cursor = cursor = cursor->logical_prev;
00690                     size = cursor->end - cursor->start;
00691                     cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
00692                         size : 0;
00693                 } else
00694                   cursor->cursor++;
00695             } else {
00696                 if (cursor->cursor == 0) {
00697                     if (cursor == str->logical_first)
00698                       /*
00699                        * Already at the beginning of the string.
00700                        */
00701                       return (cnt != count);
00702 
00703                     str->cursor = cursor = cursor->logical_prev;
00704                     cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
00705                         size : 0;
00706                 } else
00707                   cursor->cursor--;
00708             }
00709         }
00710         cnt--;
00711     }
00712     return 1;
00713 }
00714 
00715 int
00716 ucstring_cursor_left(ucstring_t *str, int count)
00717 {
00718     if (str == 0)
00719       return 0;
00720     return (str->cursor_motion == UCPGBA_CURSOR_VISUAL) ?
00721         _ucstring_visual_cursor_left(str, count) :
00722         _ucstring_logical_cursor_left(str, count);
00723 }
00724 
00725 void
00726 ucstring_cursor_info(ucstring_t *str, int *direction, unsigned long *position)
00727 {
00728     long c;
00729     unsigned long size;
00730     ucrun_t *cursor;
00731 
00732     if (str == 0 || direction == 0 || position == 0)
00733       return;
00734 
00735     cursor = str->cursor;
00736 
00737     *direction = cursor->direction;
00738 
00739     c = cursor->cursor;
00740     size = cursor->end - cursor->start;
00741 
00742     if (c == size)
00743       *position = (cursor->direction == UCPGBA_RTL) ?
00744           cursor->start : cursor->positions[c - 1];
00745     else if (c == -1)
00746       *position = (cursor->direction == UCPGBA_RTL) ?
00747           cursor->end : cursor->start;
00748     else
00749       *position = cursor->positions[c];
00750 }