1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2 
3 #include "vt_bidi.h"
4 
5 #include <string.h> /* memset */
6 #include <ctype.h>  /* isalpha */
7 #include <pobl/bl_debug.h>
8 #include <pobl/bl_mem.h> /* alloca/realloc/free */
9 #include <fribidi.h>
10 
11 #if 0
12 #define __DEBUG
13 #endif
14 
15 #define DIR_LTR_MARK 0x200e
16 #define DIR_RTL_MARK 0x200f
17 
18 /* --- global functions --- */
19 
vt_bidi_new(void)20 vt_bidi_state_t vt_bidi_new(void) {
21   vt_bidi_state_t state;
22 
23   if ((state = malloc(sizeof(*state))) == NULL) {
24     return NULL;
25   }
26 
27   state->visual_order = NULL;
28   state->size = 0;
29   state->rtl_state = 0;
30   state->bidi_mode = BIDI_NORMAL_MODE;
31 
32   return state;
33 }
34 
vt_bidi_destroy(vt_bidi_state_t state)35 int vt_bidi_destroy(vt_bidi_state_t state) {
36   free(state->visual_order);
37   free(state);
38 
39   return 1;
40 }
41 
42 /*
43  * Don't call this functions with type_p == FRIBIDI_TYPE_ON and size == cur_pos.
44  */
log2vis(FriBidiChar * str,u_int size,FriBidiCharType * type_p,vt_bidi_mode_t bidi_mode,FriBidiStrIndex * order,u_int cur_pos,int append)45 static void log2vis(FriBidiChar *str, u_int size, FriBidiCharType *type_p, vt_bidi_mode_t bidi_mode,
46                     FriBidiStrIndex *order, u_int cur_pos, int append) {
47   FriBidiCharType type;
48   u_int pos;
49 
50   if (size > cur_pos) {
51     if (bidi_mode == BIDI_NORMAL_MODE) {
52       type = FRIBIDI_TYPE_ON;
53     } else if (bidi_mode == BIDI_ALWAYS_RIGHT) {
54       type = FRIBIDI_TYPE_RTL;
55     } else /* if (bidi_mode == BIDI_ALWAYS_LEFT) */ {
56       type = FRIBIDI_TYPE_LTR;
57     }
58 
59     fribidi_log2vis(str + cur_pos, size - cur_pos, &type, NULL, order + cur_pos, NULL, NULL);
60 
61     if (*type_p == FRIBIDI_TYPE_ON) {
62       *type_p = type;
63     }
64   } else {
65     /*
66      * This functions is never called if type_p == FRIBIDI_TYPE_ON and
67      * size == cur_pos.
68      */
69     type = *type_p;
70   }
71 
72   if (*type_p == FRIBIDI_TYPE_LTR) {
73     if (type == FRIBIDI_TYPE_RTL) {
74       /*
75        * (Logical) "LLL/RRRNNN " ('/' is a separator)
76        *                      ^-> endsp
77        * => (Visual) "LLL/ NNNRRR" => "LLL/NNNRRR "
78        */
79 
80       u_int endsp_num;
81 
82       for (pos = size; pos > cur_pos; pos--) {
83         if (str[pos - 1] != ' ') {
84           break;
85         }
86 
87         order[pos - 1] = pos - 1;
88       }
89 
90       endsp_num = size - pos;
91 
92       for (pos = cur_pos; pos < size - endsp_num; pos++) {
93         order[pos] = order[pos] + cur_pos - endsp_num;
94       }
95     } else if (cur_pos > 0) {
96       for (pos = cur_pos; pos < size; pos++) {
97         order[pos] += cur_pos;
98       }
99     }
100 
101     if (append) {
102       order[size] = size;
103     }
104   } else /* if (*type_p == FRIBIDI_TYPE_RTL) */ {
105     if (cur_pos > 0) {
106       for (pos = 0; pos < cur_pos; pos++) {
107         order[pos] += (size - cur_pos);
108       }
109     }
110 
111     if (type == FRIBIDI_TYPE_LTR) {
112       /*
113        * (Logical) "RRRNNN/LLL " ('/' is a separator)
114        *                      ^-> endsp
115        * => (Visual) "LLL /NNNRRR" => " LLL/NNNRRR"
116        */
117 
118       u_int endsp_num;
119 
120       for (pos = size; pos > cur_pos; pos--) {
121         if (str[pos - 1] != ' ') {
122           break;
123         }
124 
125         order[pos - 1] = size - pos;
126       }
127 
128       endsp_num = size - pos;
129       for (pos = cur_pos; pos < size - endsp_num; pos++) {
130         order[pos] += endsp_num;
131       }
132     }
133 
134     if (append) {
135       for (pos = 0; pos < size; pos++) {
136         order[pos]++;
137       }
138 
139       order[size] = 0;
140     }
141   }
142 }
143 
log2log(FriBidiStrIndex * order,u_int cur_pos,u_int size)144 static void log2log(FriBidiStrIndex *order, u_int cur_pos, u_int size) {
145   u_int pos;
146 
147   for (pos = cur_pos; pos < size; pos++) {
148     order[pos] = pos;
149   }
150 }
151 
vt_bidi(vt_bidi_state_t state,vt_char_t * src,u_int size,vt_bidi_mode_t bidi_mode,const char * separators)152 int vt_bidi(vt_bidi_state_t state, vt_char_t *src, u_int size, vt_bidi_mode_t bidi_mode,
153             const char *separators) {
154   FriBidiChar *fri_src;
155   FriBidiCharType fri_type;
156   FriBidiStrIndex *fri_order;
157   u_int cur_pos;
158   ef_charset_t cs;
159   u_int32_t code;
160   u_int count;
161   int ret;
162 
163   state->rtl_state = 0;
164 
165   if (size == 0) {
166     state->size = 0;
167 
168     return 0;
169   }
170 
171   if ((fri_src = alloca(sizeof(FriBidiChar) * size)) == NULL) {
172 #ifdef DEBUG
173     bl_warn_printf(BL_DEBUG_TAG " alloca() failed.\n");
174 #endif
175 
176     return 0;
177   }
178 
179   if ((fri_order = alloca(sizeof(FriBidiStrIndex) * size)) == NULL) {
180 #ifdef DEBUG
181     bl_warn_printf(BL_DEBUG_TAG " alloca() failed.\n");
182 #endif
183 
184     return 0;
185   }
186 
187   fri_type = FRIBIDI_TYPE_ON;
188 
189   if (bidi_mode == BIDI_ALWAYS_RIGHT) {
190     SET_HAS_RTL(state);
191   }
192 
193   for (count = 0, cur_pos = 0; count < size; count++) {
194     cs = vt_char_cs(&src[count]);
195     code = vt_char_code(&src[count]);
196 
197     if (cs == US_ASCII) {
198       if (!isalpha(code)) {
199         if (vt_get_picture_char(&src[count])) {
200           fri_src[count] = 'a';
201         } else if (separators && strchr(separators, code)) {
202           if (HAS_RTL(state)) {
203             log2vis(fri_src, count, &fri_type, bidi_mode, fri_order, cur_pos, 1);
204           } else {
205             fri_type = FRIBIDI_TYPE_LTR;
206             log2log(fri_order, cur_pos, count + 1);
207           }
208 
209           cur_pos = count + 1;
210         } else {
211           fri_src[count] = code;
212         }
213       } else {
214         fri_src[count] = code;
215       }
216     } else if (cs == ISO10646_UCS4_1) {
217       if (0x2500 <= code && code <= 0x259f) {
218         goto decsp;
219       } else {
220         fri_src[count] = code;
221 
222         if (!HAS_RTL(state) && (fribidi_get_type(fri_src[count]) & FRIBIDI_MASK_RTL)) {
223           SET_HAS_RTL(state);
224         }
225       }
226     } else if (cs == DEC_SPECIAL) {
227     decsp:
228       bidi_mode = BIDI_ALWAYS_LEFT;
229 
230       if (HAS_RTL(state)) {
231         log2vis(fri_src, count, &fri_type, bidi_mode, fri_order, cur_pos, 1);
232       } else {
233         log2log(fri_order, cur_pos, count + 1);
234       }
235 
236       cur_pos = count + 1;
237     } else if (IS_ISCII(cs)) {
238       return -2; /* iscii */
239     } else {
240 #ifdef __DEBUG
241       bl_debug_printf(BL_DEBUG_TAG " %x is not ucs.\n", cs);
242 #endif
243 
244       /*
245        * Regarded as NEUTRAL character.
246        */
247       fri_src[count] = ' ';
248     }
249   }
250 
251   if (HAS_RTL(state)) {
252     log2vis(fri_src, size, &fri_type, bidi_mode, fri_order, cur_pos, 0);
253 
254     count = 0;
255 
256     if (state->size != size) {
257       void *p;
258 
259       if (!(p = realloc(state->visual_order, sizeof(u_int16_t) * size))) {
260 #ifdef DEBUG
261         bl_warn_printf(BL_DEBUG_TAG " realloc() failed.\n");
262 #endif
263 
264         state->size = 0;
265 
266         return 0;
267       }
268 
269       state->visual_order = p;
270       state->size = size;
271 
272       ret = 2; /* order is changed */
273     } else {
274       ret = 1; /* order is not changed */
275 
276       for (; count < size; count++) {
277         if (state->visual_order[count] != fri_order[count]) {
278           ret = 2; /* order_is_changed */
279           break;
280         }
281       }
282     }
283 
284     for (; count < size; count++) {
285       state->visual_order[count] = fri_order[count];
286     }
287 
288 #ifdef __DEBUG
289     bl_msg_printf("utf8 text => \n");
290     for (count = 0; count < size; count++) {
291       bl_msg_printf("%.4x ", fri_src[count]);
292     }
293     bl_msg_printf("\n");
294 
295     bl_msg_printf("visual order => ");
296     for (count = 0; count < size; count++) {
297       bl_msg_printf("%.2d ", state->visual_order[count]);
298     }
299     bl_msg_printf("\n");
300 #endif
301 
302 #ifdef DEBUG
303     for (count = 0; count < size; count++) {
304       if (state->visual_order[count] >= size) {
305         bl_warn_printf(BL_DEBUG_TAG " visual order(%d) of %d is illegal.\n",
306                        state->visual_order[count], count);
307 
308         bl_msg_printf("returned order => ");
309         for (count = 0; count < size; count++) {
310           bl_msg_printf("%d ", state->visual_order[count]);
311         }
312         bl_msg_printf("\n");
313 
314         abort();
315       }
316     }
317 #endif
318   } else {
319     state->size = 0;
320 
321     return -1; /* ot layout */
322   }
323 
324   if (fri_type == FRIBIDI_TYPE_RTL) {
325     SET_BASE_RTL(state);
326   }
327 
328   state->bidi_mode = bidi_mode;
329 
330   return ret;
331 }
332 
vt_bidi_copy(vt_bidi_state_t dst,vt_bidi_state_t src,int optimize)333 int vt_bidi_copy(vt_bidi_state_t dst, vt_bidi_state_t src, int optimize) {
334   u_int16_t *p;
335 
336   if (optimize && !HAS_RTL(src)) {
337     vt_bidi_destroy(dst);
338 
339     return -1;
340   } else if (src->size == 0) {
341     free(dst->visual_order);
342     p = NULL;
343   } else if ((p = realloc(dst->visual_order, sizeof(u_int16_t) * src->size))) {
344     memcpy(p, src->visual_order, sizeof(u_int16_t) * src->size);
345   } else {
346     return 0;
347   }
348 
349   dst->visual_order = p;
350   dst->size = src->size;
351   dst->rtl_state = src->rtl_state;
352   dst->bidi_mode = src->bidi_mode;
353 
354   return 1;
355 }
356 
vt_bidi_reset(vt_bidi_state_t state)357 int vt_bidi_reset(vt_bidi_state_t state) {
358   state->size = 0;
359 
360   return 1;
361 }
362 
vt_bidi_get_mirror_char(u_int32_t ch)363 u_int32_t vt_bidi_get_mirror_char(u_int32_t ch) {
364   FriBidiChar mirror;
365 
366   if (fribidi_get_mirror_char(ch, &mirror)) {
367     return mirror;
368   } else {
369     return 0;
370   }
371 }
372 
vt_is_rtl_char(u_int32_t ch)373 int vt_is_rtl_char(u_int32_t ch) {
374   return (fribidi_get_bidi_type(ch) & FRIBIDI_MASK_RTL) == FRIBIDI_MASK_RTL;
375 }
376