1 /**
2  * chewingutil.c
3  *
4  * Copyright (c) 1999, 2000, 2001
5  *      Lu-chuan Kung and Kang-pen Chen.
6  *      All rights reserved.
7  *
8  * Copyright (c) 2004-2006, 2008, 2010-2014
9  *      libchewing Core Team. See ChangeLog for details.
10  *
11  * See the file "COPYING" for information on usage and redistribution
12  * of this file.
13  */
14 
15 /* This file is encoded in UTF-8 */
16 #ifdef HAVE_CONFIG_H
17 #    include <config.h>
18 #endif
19 
20 #include <ctype.h>
21 #include <string.h>
22 #include <assert.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 
26 #include "chewing-utf8-util.h"
27 #include "global.h"
28 #include "global-private.h"
29 #include "chewingutil.h"
30 #include "bopomofo-private.h"
31 #include "choice-private.h"
32 #include "tree-private.h"
33 #include "userphrase-private.h"
34 #include "private.h"
35 
36 #ifdef HAVE_ASPRINTF
37 /* asprintf is provided by GNU extensions and *BSD */
38 #    ifndef _GNU_SOURCE
39 #        define _GNU_SOURCE
40 #    endif
41 #    include <stdio.h>
42 #else
43 #    include "plat_path.h"
44 #endif
45 
46 extern const char *const zhuin_tab[];
47 static void MakePreferInterval(ChewingData *pgdata);
48 static void ShiftInterval(ChewingOutput *pgo, ChewingData *pgdata);
49 static int ChewingKillSelectIntervalAcross(int cursor, ChewingData *pgdata);
50 
51 static int FindSymbolKey(const char *symbol);
52 
53 /* Note: Keep synchronize with `FindEasySymbolIndex`! */
54 static const char G_EASY_SYMBOL_KEY[EASY_SYMBOL_KEY_TAB_LEN] = {
55     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
56     'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
57     'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
58     'U', 'V', 'W', 'X', 'Y', 'Z'
59 };
60 static const char NO_SYM_KEY = '\t';
61 
62 /*
63  * FindEasySymbolIndex(ch) = char ch's index in G_EASY_SYMBOL_KEY
64  * Just return -1 if not found.
65  */
FindEasySymbolIndex(char ch)66 static int FindEasySymbolIndex(char ch)
67 {
68         /**
69          * '0' => 0, ..., '9' => 9
70          * 'A' => 10, 'B' => 11, ... 'Z' => 35
71          */
72     if (isdigit(ch)) {
73         return ch - '0';
74     } else if (isupper(ch)) {
75         return ch - 'A' + 10;
76     } else {
77         return -1;
78     }
79 }
80 
SetUpdatePhraseMsg(ChewingData * pgdata,const char * addWordSeq,int len,int state)81 void SetUpdatePhraseMsg(ChewingData *pgdata, const char *addWordSeq, int len, int state)
82 {
83     if (state == USER_UPDATE_INSERT) {
84         /* 加入: */
85         snprintf(pgdata->showMsg, sizeof(pgdata->showMsg), "\xE5\x8A\xA0\xE5\x85\xA5\xEF\xBC\x9A%s", addWordSeq);
86     } else {
87         /* 已有: */
88         snprintf(pgdata->showMsg, sizeof(pgdata->showMsg), "\xE5\xB7\xB2\xE6\x9C\x89\xEF\xBC\x9A%s", addWordSeq);
89     }
90     pgdata->showMsgLen = AUX_PREFIX_LEN + len;
91 }
92 
NoSymbolBetween(ChewingData * pgdata,int begin,int end)93 int NoSymbolBetween(ChewingData *pgdata, int begin, int end)
94 {
95     int i;
96 
97     for (i = begin; i < end; ++i) {
98         if (pgdata->preeditBuf[i].category == CHEWING_SYMBOL) {
99             return 0;
100         }
101     }
102 
103     return 1;
104 }
105 
ChewingIsEntering(ChewingData * pgdata)106 int ChewingIsEntering(ChewingData *pgdata)
107 {
108     if (pgdata->choiceInfo.isSymbol != WORD_CHOICE)
109         return 1;
110     return (pgdata->chiSymbolBufLen != 0 || BopomofoIsEntering(&(pgdata->bopomofoData)));
111 }
112 
HaninSymbolInput(ChewingData * pgdata)113 int HaninSymbolInput(ChewingData *pgdata)
114 {
115     unsigned int i;
116 
117     ChoiceInfo *pci = &(pgdata->choiceInfo);
118     AvailInfo *pai = &(pgdata->availInfo);
119 
120     /* No available symbol table */
121     if (!pgdata->static_data.symbol_table)
122         return BOPOMOFO_ABSORB;
123 
124     pci->nTotalChoice = 0;
125     for (i = 0; i < pgdata->static_data.n_symbol_entry; i++) {
126         strcpy(pci->totalChoiceStr[pci->nTotalChoice], pgdata->static_data.symbol_table[i]->category);
127         pci->nTotalChoice++;
128     }
129     pai->avail[0].len = 1;
130     pai->avail[0].id = NULL;
131     pai->nAvail = 1;
132     pai->currentAvail = 0;
133     pci->nChoicePerPage = pgdata->config.candPerPage;
134     assert(pci->nTotalChoice > 0);
135     pci->nPage = CEIL_DIV(pci->nTotalChoice, pci->nChoicePerPage);
136     pci->pageNo = 0;
137     pci->isSymbol = SYMBOL_CATEGORY_CHOICE;
138     return BOPOMOFO_ABSORB;
139 }
140 
_Inner_InternalSpecialSymbol(int key,ChewingData * pgdata,char symkey,const char * const chibuf)141 static int _Inner_InternalSpecialSymbol(int key, ChewingData *pgdata, char symkey, const char *const chibuf)
142 {
143     int kbtype;
144     PreeditBuf *buf;
145 
146     if (key == symkey && NULL != chibuf) {
147         assert(pgdata->chiSymbolBufLen >= pgdata->chiSymbolCursor);
148 
149         buf = &pgdata->preeditBuf[pgdata->chiSymbolCursor];
150 
151         memmove(&pgdata->preeditBuf[pgdata->chiSymbolCursor + 1],
152                 &pgdata->preeditBuf[pgdata->chiSymbolCursor],
153                 sizeof(pgdata->preeditBuf[0]) * (pgdata->chiSymbolBufLen - pgdata->chiSymbolCursor));
154 
155         strncpy(buf->char_, chibuf, sizeof(buf->char_) - 1);
156         buf->category = CHEWING_SYMBOL;
157 
158         /* Save Symbol Key */
159         memmove(&(pgdata->symbolKeyBuf[pgdata->chiSymbolCursor + 1]),
160                 &(pgdata->symbolKeyBuf[pgdata->chiSymbolCursor]),
161                 sizeof(pgdata->symbolKeyBuf[0]) * (pgdata->chiSymbolBufLen - pgdata->chiSymbolCursor));
162         pgdata->symbolKeyBuf[pgdata->chiSymbolCursor] = key;
163         pgdata->bUserArrCnnct[PhoneSeqCursor(pgdata)] = 0;
164         pgdata->chiSymbolCursor++;
165         pgdata->chiSymbolBufLen++;
166         /* reset Bopomofo data */
167         /* Don't forget the kbtype */
168         kbtype = pgdata->bopomofoData.kbtype;
169         memset(&(pgdata->bopomofoData), 0, sizeof(BopomofoData));
170         pgdata->bopomofoData.kbtype = kbtype;
171         return 1;
172     }
173     return 0;
174 }
175 
InternalSpecialSymbol(int key,ChewingData * pgdata,int nSpecial,const char keybuf[],const char * const chibuf[])176 static int InternalSpecialSymbol(int key, ChewingData *pgdata,
177                                  int nSpecial, const char keybuf[], const char *const chibuf[])
178 {
179     int i, rtn = BOPOMOFO_IGNORE;   /* very strange and difficult to understand */
180 
181     for (i = 0; i < nSpecial; i++) {
182         if (1 == _Inner_InternalSpecialSymbol(key, pgdata, keybuf[i], chibuf[i])) {
183             rtn = BOPOMOFO_ABSORB;
184             break;
185         }
186     }
187     return rtn;
188 }
189 
SpecialSymbolInput(int key,ChewingData * pgdata)190 int SpecialSymbolInput(int key, ChewingData *pgdata)
191 {
192     static const char keybuf[] = {
193         '[', ']', '{', '}', '\'', '<', ':', '\"', '>',
194         '~', '!', '@', '#', '$', '%', '^', '&', '*',
195         '(', ')', '_', '+', '=', '\\', '|', '?',
196         ',', '.', ';'
197     };
198 
199     static const char *const chibuf[] = {
200         "\xE3\x80\x8C", "\xE3\x80\x8D", "\xE3\x80\x8E", "\xE3\x80\x8F",
201         /* "「", "」", "『", "』" */
202         "\xE3\x80\x81", "\xEF\xBC\x8C", "\xEF\xBC\x9A", "\xEF\xBC\x9B",
203         /* "、", ",", ":", ";" */
204         "\xE3\x80\x82", "\xEF\xBD\x9E", "\xEF\xBC\x81", "\xEF\xBC\xA0",
205         /* "。", "~", "!", "@" */
206         "\xEF\xBC\x83", "\xEF\xBC\x84", "\xEF\xBC\x85", "\xEF\xB8\xBF",
207         /* "#", "$", "%", "︿" */
208         "\xEF\xBC\x86", "\xEF\xBC\x8A", "\xEF\xBC\x88", "\xEF\xBC\x89",
209         /* "&", "*", "(", ")" */
210         "\xE2\x80\x94", "\xEF\xBC\x8B", "\xEF\xBC\x9D", "\xEF\xBC\xBC",
211         /* "—", "+", "=", "\" */
212         "\xEF\xBD\x9C", "\xEF\xBC\x9F", "\xEF\xBC\x8C", "\xE3\x80\x82",
213         /* "|", "?", ",", "。" */
214         "\xEF\xBC\x9B"
215             /* ";" */
216     };
217     STATIC_ASSERT(ARRAY_SIZE(keybuf) == ARRAY_SIZE(chibuf));
218 
219     return InternalSpecialSymbol(key, pgdata, ARRAY_SIZE(keybuf), keybuf, chibuf);
220 }
221 
FullShapeSymbolInput(int key,ChewingData * pgdata)222 int FullShapeSymbolInput(int key, ChewingData *pgdata)
223 {
224     int rtn;
225 
226     static char keybuf[] = {
227         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
228         'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
229         'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
230         'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D',
231         'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
232         'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
233         'Y', 'Z', ' ', '\"', '\'', '/', '<', '>', '`', '[',
234         ']', '{', '}', '+', '-'
235     };
236     static const char *chibuf[] = {
237         "\xEF\xBC\x90", "\xEF\xBC\x91", "\xEF\xBC\x92", "\xEF\xBC\x93",
238         /* "0","1","2","3" */
239         "\xEF\xBC\x94", "\xEF\xBC\x95", "\xEF\xBC\x96", "\xEF\xBC\x97",
240         /* "4","5","6","7" */
241         "\xEF\xBC\x98", "\xEF\xBC\x99", "\xEF\xBD\x81", "\xEF\xBD\x82",
242         /* "8","9","a","b" */
243         "\xEF\xBD\x83", "\xEF\xBD\x84", "\xEF\xBD\x85", "\xEF\xBD\x86",
244         /* "c","d","e","f" */
245         "\xEF\xBD\x87", "\xEF\xBD\x88", "\xEF\xBD\x89", "\xEF\xBD\x8A",
246         /* "g","h","i","j" */
247         "\xEF\xBD\x8B", "\xEF\xBD\x8C", "\xEF\xBD\x8D", "\xEF\xBD\x8E",
248         /* "k","l","m","n" */
249         "\xEF\xBD\x8F", "\xEF\xBD\x90", "\xEF\xBD\x91", "\xEF\xBD\x92",
250         /* "o","p","q","r" */
251         "\xEF\xBD\x93", "\xEF\xBD\x94", "\xEF\xBD\x95", "\xEF\xBD\x96",
252         /* "s","t","u","v" */
253         "\xEF\xBD\x97", "\xEF\xBD\x98", "\xEF\xBD\x99", "\xEF\xBD\x9A",
254         /* "w","x","y","z" */
255         "\xEF\xBC\xA1", "\xEF\xBC\xA2", "\xEF\xBC\xA3", "\xEF\xBC\xA4",
256         /* "A","B","C","D" */
257         "\xEF\xBC\xA5", "\xEF\xBC\xA6", "\xEF\xBC\xA7", "\xEF\xBC\xA8",
258         /* "E","F","G","H" */
259         "\xEF\xBC\xA9", "\xEF\xBC\xAA", "\xEF\xBC\xAB", "\xEF\xBC\xAC",
260         /* "I","J","K","L" */
261         "\xEF\xBC\xAD", "\xEF\xBC\xAE", "\xEF\xBC\xAF", "\xEF\xBC\xB0",
262         /* "M","N","O","P" */
263         "\xEF\xBC\xB1", "\xEF\xBC\xB2", "\xEF\xBC\xB3", "\xEF\xBC\xB4",
264         /* "Q","R","S","T" */
265         "\xEF\xBC\xB5", "\xEF\xBC\xB6", "\xEF\xBC\xB7", "\xEF\xBC\xB8",
266         /* "U","V","W","X" */
267         "\xEF\xBC\xB9", "\xEF\xBC\xBA", "\xE3\x80\x80", "\xE2\x80\x9D",
268         /* "Y","Z"," ","”" */
269         "\xE2\x80\x99", "\xEF\xBC\x8F", "\xEF\xBC\x9C", "\xEF\xBC\x9E",
270         /* "’","/","<",">" */
271         "\xE2\x80\xB5", "\xE3\x80\x94", "\xE3\x80\x95", "\xEF\xBD\x9B",
272         /* "‵","〔""〕","{" */
273         "\xEF\xBD\x9D", "\xEF\xBC\x8B", "\xEF\xBC\x8D"
274             /* "}","+","-" */
275     };
276     STATIC_ASSERT(ARRAY_SIZE(keybuf) == ARRAY_SIZE(chibuf));
277 
278     rtn = InternalSpecialSymbol(key, pgdata, ARRAY_SIZE(keybuf), keybuf, chibuf);
279     if (rtn == BOPOMOFO_IGNORE)
280         rtn = SpecialSymbolInput(key, pgdata);
281     return (rtn == BOPOMOFO_IGNORE ? SYMBOL_KEY_ERROR : SYMBOL_KEY_OK);
282 }
283 
EasySymbolInput(int key,ChewingData * pgdata)284 int EasySymbolInput(int key, ChewingData *pgdata)
285 {
286     int rtn, loop, _index;
287     char wordbuf[8];
288 
289     int nSpecial = EASY_SYMBOL_KEY_TAB_LEN;
290 
291     _index = FindEasySymbolIndex(key);
292     if (-1 != _index) {
293         for (loop = 0; loop < pgdata->static_data.g_easy_symbol_num[_index]; ++loop) {
294             ueStrNCpy(wordbuf, ueStrSeek(pgdata->static_data.g_easy_symbol_value[_index], loop), 1, 1);
295             rtn = _Inner_InternalSpecialSymbol(key, pgdata, key, wordbuf);
296         }
297         return SYMBOL_KEY_OK;
298     }
299 
300     rtn = InternalSpecialSymbol(key, pgdata, nSpecial,
301                                 G_EASY_SYMBOL_KEY, (const char **) pgdata->static_data.g_easy_symbol_value);
302     if (rtn == BOPOMOFO_IGNORE)
303         rtn = SpecialSymbolInput(key, pgdata);
304     return (rtn == BOPOMOFO_IGNORE ? SYMBOL_KEY_ERROR : SYMBOL_KEY_OK);
305 }
306 
SymbolChoice(ChewingData * pgdata,int sel_i)307 int SymbolChoice(ChewingData *pgdata, int sel_i)
308 {
309     int kbtype;
310     int i;
311     int symbol_type;
312     int key;
313 
314     if (!pgdata->static_data.symbol_table && pgdata->choiceInfo.isSymbol != SYMBOL_CHOICE_UPDATE)
315         return BOPOMOFO_ABSORB;
316 
317     if (pgdata->choiceInfo.isSymbol == SYMBOL_CATEGORY_CHOICE && 0 == pgdata->static_data.symbol_table[sel_i]->nSymbols)
318         symbol_type = SYMBOL_CHOICE_INSERT;
319     else
320         symbol_type = pgdata->choiceInfo.isSymbol;
321 
322     /* level one, symbol category */
323     if (symbol_type == SYMBOL_CATEGORY_CHOICE) {
324         ChoiceInfo *pci = &pgdata->choiceInfo;
325         AvailInfo *pai = &pgdata->availInfo;
326 
327         /* Display all symbols in this category */
328         pci->nTotalChoice = 0;
329         for (i = 0; i < pgdata->static_data.symbol_table[sel_i]->nSymbols; i++) {
330             ueStrNCpy(pci->totalChoiceStr[pci->nTotalChoice],
331                       pgdata->static_data.symbol_table[sel_i]->symbols[i], 1, 1);
332             pci->nTotalChoice++;
333         }
334         pai->avail[0].len = 1;
335         pai->avail[0].id = NULL;
336         pai->nAvail = 1;
337         pai->currentAvail = 0;
338         pci->nChoicePerPage = pgdata->config.candPerPage;
339         assert(pci->nTotalChoice > 0);
340         pci->nPage = CEIL_DIV(pci->nTotalChoice, pci->nChoicePerPage);
341         pci->pageNo = 0;
342         pci->isSymbol = SYMBOL_CHOICE_INSERT;
343     } else {                    /* level 2 symbol or OpenSymbolChoice */
344         /* TODO: FIXME, this part is buggy! */
345         PreeditBuf *buf = &pgdata->preeditBuf[pgdata->chiSymbolCursor];
346 
347         if (symbol_type == SYMBOL_CHOICE_INSERT) {
348             assert(pgdata->chiSymbolCursor <= pgdata->chiSymbolBufLen);
349 
350             if (pgdata->chiSymbolCursor == pgdata->chiSymbolBufLen ||
351                     pgdata->symbolKeyBuf[pgdata->chiSymbolCursor] != NO_SYM_KEY) {
352                 memmove(&pgdata->preeditBuf[pgdata->chiSymbolCursor + 1],
353                         &pgdata->preeditBuf[pgdata->chiSymbolCursor],
354                         sizeof(pgdata->preeditBuf[0]) * (pgdata->chiSymbolBufLen - pgdata->chiSymbolCursor));
355             } else {
356                 symbol_type = SYMBOL_CHOICE_UPDATE;
357             }
358         }
359         strncpy(buf->char_, pgdata->choiceInfo.totalChoiceStr[sel_i], sizeof(buf->char_) - 1);
360         buf->category = CHEWING_SYMBOL;
361 
362         /* This is very strange */
363         key = FindSymbolKey(pgdata->choiceInfo.totalChoiceStr[sel_i]);
364         pgdata->symbolKeyBuf[pgdata->chiSymbolCursor] = key ? key : NO_SYM_KEY;
365 
366         pgdata->bUserArrCnnct[PhoneSeqCursor(pgdata)] = 0;
367         ChoiceEndChoice(pgdata);
368         /* Don't forget the kbtype */
369         kbtype = pgdata->bopomofoData.kbtype;
370         memset(&(pgdata->bopomofoData), 0, sizeof(BopomofoData));
371         pgdata->bopomofoData.kbtype = kbtype;
372 
373         if (symbol_type == SYMBOL_CHOICE_INSERT) {
374             pgdata->chiSymbolBufLen++;
375             pgdata->chiSymbolCursor++;
376         }
377 
378         pgdata->choiceInfo.isSymbol = WORD_CHOICE;
379     }
380     return BOPOMOFO_ABSORB;
381 }
382 
SymbolInput(int key,ChewingData * pgdata)383 int SymbolInput(int key, ChewingData *pgdata)
384 {
385     if (isprint((char) key) &&  /* other character was ignored */
386         (pgdata->chiSymbolBufLen < MAX_PHONE_SEQ_LEN)) {        /* protect the buffer */
387         PreeditBuf *buf = &pgdata->preeditBuf[pgdata->chiSymbolCursor];
388 
389         assert(pgdata->chiSymbolCursor <= pgdata->chiSymbolBufLen);
390 
391         memmove(&pgdata->preeditBuf[pgdata->chiSymbolCursor + 1],
392                 &pgdata->preeditBuf[pgdata->chiSymbolCursor],
393                 sizeof(pgdata->preeditBuf[0]) * (pgdata->chiSymbolBufLen - pgdata->chiSymbolCursor));
394 
395         buf->char_[0] = (char) key;
396         buf->char_[1] = 0;
397         buf->category = CHEWING_SYMBOL;
398 
399         /* Save Symbol Key */
400         memmove(&(pgdata->symbolKeyBuf[pgdata->chiSymbolCursor + 1]),
401                 &(pgdata->symbolKeyBuf[pgdata->chiSymbolCursor]),
402                 sizeof(pgdata->symbolKeyBuf[0]) * (pgdata->chiSymbolBufLen - pgdata->chiSymbolCursor));
403         pgdata->symbolKeyBuf[pgdata->chiSymbolCursor] = toupper(key);
404 
405         pgdata->bUserArrCnnct[PhoneSeqCursor(pgdata)] = 0;
406         pgdata->chiSymbolCursor++;
407         pgdata->chiSymbolBufLen++;
408         return SYMBOL_KEY_OK;
409     }
410     return SYMBOL_KEY_ERROR;
411 }
412 
CompInterval(const IntervalType * a,const IntervalType * b)413 static int CompInterval(const IntervalType * a, const IntervalType * b)
414 {
415     int cmp = a->from - b->from;
416 
417     if (cmp)
418         return cmp;
419     return (a->to - b->to);
420 }
421 
FindIntervalFrom(int from,IntervalType inte[],int nInte)422 static int FindIntervalFrom(int from, IntervalType inte[], int nInte)
423 {
424     int i;
425 
426     for (i = 0; i < nInte; i++)
427         if (inte[i].from == from)
428             return i;
429     return -1;
430 }
431 
WriteChiSymbolToCommitBuf(ChewingData * pgdata,ChewingOutput * pgo,int len)432 void WriteChiSymbolToCommitBuf(ChewingData *pgdata, ChewingOutput *pgo, int len)
433 {
434     int i;
435     char *pos;
436 
437     assert(pgdata);
438     assert(pgo);
439 
440     pgo->commitBufLen = len;
441 
442     pos = pgo->commitBuf;
443     for (i = 0; i < pgo->commitBufLen; ++i) {
444         assert(pos + MAX_UTF8_SIZE + 1 < pgo->commitBuf + sizeof(pgo->commitBuf));
445         strcpy(pos, pgdata->preeditBuf[i].char_);
446         pos += strlen(pgdata->preeditBuf[i].char_);
447     }
448     *pos = 0;
449 }
450 
CountReleaseNum(ChewingData * pgdata)451 static int CountReleaseNum(ChewingData *pgdata)
452 {
453     int remain, i;
454 
455     remain = pgdata->config.maxChiSymbolLen - pgdata->chiSymbolBufLen;
456     if (remain >= 0)
457         return 0;
458 
459     qsort(pgdata->preferInterval, pgdata->nPrefer, sizeof(IntervalType), (CompFuncType) CompInterval);
460 
461     if (!ChewingIsChiAt(0, pgdata)) {
462         for (i = 0; i < pgdata->chiSymbolCursor; ++i) {
463             if (ChewingIsChiAt(i, pgdata)) {
464                 break;
465             }
466         }
467         return i;
468     }
469 
470     i = FindIntervalFrom(0, pgdata->preferInterval, pgdata->nPrefer);
471     if (i >= 0) {
472         return (pgdata->preferInterval[i].to - pgdata->preferInterval[i].from);
473     }
474 
475     return 1;
476 }
477 
KillFromLeft(ChewingData * pgdata,int nKill)478 static void KillFromLeft(ChewingData *pgdata, int nKill)
479 {
480     int i;
481 
482     for (i = 0; i < nKill; i++)
483         ChewingKillChar(pgdata, 0, DECREASE_CURSOR);
484 }
485 
CleanAllBuf(ChewingData * pgdata)486 void CleanAllBuf(ChewingData *pgdata)
487 {
488     /* 1 */
489     pgdata->nPhoneSeq = 0;
490     memset(pgdata->phoneSeq, 0, sizeof(pgdata->phoneSeq));
491     /* 2 */
492     pgdata->chiSymbolBufLen = 0;
493     memset(pgdata->preeditBuf, 0, sizeof(pgdata->preeditBuf));
494     /* 3 */
495     memset(pgdata->bUserArrBrkpt, 0, sizeof(pgdata->bUserArrBrkpt));
496     /* 4 */
497     pgdata->nSelect = 0;
498     /* 5 */
499     pgdata->chiSymbolCursor = 0;
500     /* 6 */
501     memset(pgdata->bUserArrCnnct, 0, sizeof(pgdata->bUserArrCnnct));
502 
503     pgdata->phrOut.nNumCut = 0;
504 
505     memset(pgdata->symbolKeyBuf, 0, sizeof(pgdata->symbolKeyBuf));
506 
507     pgdata->nPrefer = 0;
508 }
509 
ReleaseChiSymbolBuf(ChewingData * pgdata,ChewingOutput * pgo)510 int ReleaseChiSymbolBuf(ChewingData *pgdata, ChewingOutput *pgo)
511 {
512     int throwEnd;
513 
514     throwEnd = CountReleaseNum(pgdata);
515 
516     /*
517      * When current buffer size exceeds maxChiSymbolLen,
518      * we need to throw some of the characters at the head of the buffer and
519      * commit them.
520      */
521     if (throwEnd) {
522         /*
523          * count how many chinese words in "chiSymbolBuf[ 0 .. (throwEnd - 1)]"
524          * And release from "chiSymbolBuf" && "phoneSeq"
525          */
526         WriteChiSymbolToCommitBuf(pgdata, pgo, throwEnd);
527         KillFromLeft(pgdata, throwEnd);
528     }
529     return throwEnd;
530 }
531 
ChewingIsBreakPoint(int cursor,ChewingData * pgdata)532 static int ChewingIsBreakPoint(int cursor, ChewingData *pgdata)
533 {
534     static const char *const BREAK_WORD[] = {
535         "\xE6\x98\xAF", "\xE7\x9A\x84", "\xE4\xBA\x86", "\xE4\xB8\x8D",
536         /* 是              的              了              不 */
537         "\xE4\xB9\x9F", "\xE8\x80\x8C", "\xE4\xBD\xA0", "\xE6\x88\x91",
538         /* 也              而              你              我 */
539         "\xE4\xBB\x96", "\xE8\x88\x87", "\xE5\xAE\x83", "\xE5\xA5\xB9",
540         /* 他              與              它              她 */
541         "\xE5\x85\xB6", "\xE5\xB0\xB1", "\xE5\x92\x8C", "\xE6\x88\x96",
542         /* 其              就              和              或 */
543         "\xE5\x80\x91", "\xE6\x80\xA7", "\xE5\x93\xA1", "\xE5\xAD\x90",
544         /* 們              性              員              子 */
545         "\xE4\xB8\x8A", "\xE4\xB8\x8B", "\xE4\xB8\xAD", "\xE5\x85\xA7",
546         /* 上              下              中              內 */
547         "\xE5\xA4\x96", "\xE5\x8C\x96", "\xE8\x80\x85", "\xE5\xAE\xB6",
548         /* 外              化              者              家 */
549         "\xE5\x85\x92", "\xE5\xB9\xB4", "\xE6\x9C\x88", "\xE6\x97\xA5",
550         /* 兒              年              月              日 */
551         "\xE6\x99\x82", "\xE5\x88\x86", "\xE7\xA7\x92", "\xE8\xA1\x97",
552         /* 時              分              秒              街 */
553         "\xE8\xB7\xAF", "\xE6\x9D\x91",
554         /* 路              村 */
555         "\xE5\x9C\xA8",
556         /* 在 */
557     };
558     size_t i;
559 
560     if (!ChewingIsChiAt(cursor, pgdata))
561         return 1;
562 
563     for (i = 0; i < ARRAY_SIZE(BREAK_WORD); ++i)
564         if (!strcmp(pgdata->preeditBuf[cursor].char_, BREAK_WORD[i]))
565             return 1;
566 
567     return 0;
568 }
569 
AutoLearnPhrase(ChewingData * pgdata)570 void AutoLearnPhrase(ChewingData *pgdata)
571 {
572     uint16_t bufPhoneSeq[MAX_PHONE_SEQ_LEN + 1];
573     char bufWordSeq[MAX_PHONE_SEQ_LEN * MAX_UTF8_SIZE + 1] = { 0 };
574     char *pos;
575     int i;
576     int from;
577     int fromPreeditBuf;
578     int len;
579     int prev_pos = 0;
580     int pending_pos = 0;
581 
582     /*
583      * FIXME: pgdata->preferInterval does not consider symbol, so we need to
584      * do translate when using APIs that considering symbol.
585      */
586 
587     UserUpdatePhraseBegin(pgdata);
588 
589     for (i = 0; i < pgdata->nPrefer; i++) {
590         from = pgdata->preferInterval[i].from;
591         len = pgdata->preferInterval[i].to - from;
592         fromPreeditBuf = toPreeditBufIndex(pgdata, from);
593 
594         LOG_VERBOSE("interval from = %d, fromPreeditBuf = %d, len = %d, pending_pos = %d", from, fromPreeditBuf, len,
595                     pending_pos);
596 
597         if (pending_pos != 0 && pending_pos < fromPreeditBuf) {
598             /*
599              * There is a pending phrase in buffer and it is not
600              * connected to current phrase. We store it as
601              * userphrase here.
602              */
603             UserUpdatePhrase(pgdata, bufPhoneSeq, bufWordSeq);
604             prev_pos = 0;
605             pending_pos = 0;
606         }
607 
608         if (len == 1 && !ChewingIsBreakPoint(fromPreeditBuf, pgdata)) {
609             /*
610              * There is a length one phrase and it is not a break
611              * point. We store it and try to connect to other length
612              * one phrase if possible.
613              */
614             memcpy(bufPhoneSeq + prev_pos, &pgdata->phoneSeq[from], sizeof(uint16_t) * len);
615             bufPhoneSeq[prev_pos + len] = (uint16_t) 0;
616 
617             pos = ueStrSeek(bufWordSeq, prev_pos);
618             copyStringFromPreeditBuf(pgdata, fromPreeditBuf, len, pos, bufWordSeq + sizeof(bufWordSeq) - pos);
619             prev_pos += len;
620             pending_pos = fromPreeditBuf + len;
621 
622         } else {
623             if (pending_pos) {
624                 /*
625                  * Clean pending phrase because we cannot join
626                  * it with current phrase.
627                  */
628                 UserUpdatePhrase(pgdata, bufPhoneSeq, bufWordSeq);
629                 prev_pos = 0;
630                 pending_pos = 0;
631             }
632             memcpy(bufPhoneSeq, &pgdata->phoneSeq[from], sizeof(uint16_t) * len);
633             bufPhoneSeq[len] = (uint16_t) 0;
634             copyStringFromPreeditBuf(pgdata, fromPreeditBuf, len, bufWordSeq, sizeof(bufWordSeq));
635             UserUpdatePhrase(pgdata, bufPhoneSeq, bufWordSeq);
636         }
637     }
638 
639     if (pending_pos) {
640         UserUpdatePhrase(pgdata, bufPhoneSeq, bufWordSeq);
641     }
642 
643     UserUpdatePhraseEnd(pgdata);
644 }
645 
AddChi(uint16_t phone,uint16_t phoneAlt,ChewingData * pgdata)646 int AddChi(uint16_t phone, uint16_t phoneAlt, ChewingData *pgdata)
647 {
648     int i;
649     int cursor = PhoneSeqCursor(pgdata);
650 
651     /* shift the selectInterval */
652     for (i = 0; i < pgdata->nSelect; i++) {
653         if (pgdata->selectInterval[i].from >= cursor) {
654             pgdata->selectInterval[i].from++;
655             pgdata->selectInterval[i].to++;
656         }
657     }
658 
659     /* shift the Brkpt */
660     assert(pgdata->nPhoneSeq >= cursor);
661     memmove(&(pgdata->bUserArrBrkpt[cursor + 2]),
662             &(pgdata->bUserArrBrkpt[cursor + 1]), sizeof(int) * (pgdata->nPhoneSeq - cursor));
663     memmove(&(pgdata->bUserArrCnnct[cursor + 2]),
664             &(pgdata->bUserArrCnnct[cursor + 1]), sizeof(int) * (pgdata->nPhoneSeq - cursor));
665 
666     /* add to phoneSeq */
667     memmove(&(pgdata->phoneSeq[cursor + 1]),
668             &(pgdata->phoneSeq[cursor]), sizeof(uint16_t) * (pgdata->nPhoneSeq - cursor));
669     pgdata->phoneSeq[cursor] = phone;
670     memmove(&(pgdata->phoneSeqAlt[cursor + 1]),
671             &(pgdata->phoneSeqAlt[cursor]), sizeof(uint16_t) * (pgdata->nPhoneSeq - cursor));
672     pgdata->phoneSeqAlt[cursor] = phoneAlt;
673     pgdata->nPhoneSeq++;
674 
675     /* add to chiSymbolBuf */
676     assert(pgdata->chiSymbolBufLen >= pgdata->chiSymbolCursor);
677     memmove(&(pgdata->preeditBuf[pgdata->chiSymbolCursor + 1]),
678             &(pgdata->preeditBuf[pgdata->chiSymbolCursor]),
679             sizeof(pgdata->preeditBuf[0]) * (pgdata->chiSymbolBufLen - pgdata->chiSymbolCursor));
680     /* "0" means Chinese word */
681     pgdata->preeditBuf[pgdata->chiSymbolCursor].category = CHEWING_CHINESE;
682     pgdata->chiSymbolBufLen++;
683     pgdata->chiSymbolCursor++;
684 
685     return 0;
686 }
687 
ShowChewingData(ChewingData * pgdata)688 static void ShowChewingData(ChewingData *pgdata)
689 {
690     int i;
691 
692     DEBUG_OUT("nPhoneSeq : %d\n" "phoneSeq  : ", pgdata->nPhoneSeq);
693     for (i = 0; i < pgdata->nPhoneSeq; i++)
694         DEBUG_OUT("%hu ", pgdata->phoneSeq[i]);
695     DEBUG_OUT("[cursor : %d]\n"
696               "nSelect : %d\n" "selectStr       selectInterval\n", PhoneSeqCursor(pgdata), pgdata->nSelect);
697     for (i = 0; i < pgdata->nSelect; i++) {
698         DEBUG_OUT("  %14s%4d%4d\n", pgdata->selectStr[i], pgdata->selectInterval[i].from, pgdata->selectInterval[i].to);
699     }
700 
701     DEBUG_OUT("bUserArrCnnct : ");
702     for (i = 0; i <= pgdata->nPhoneSeq; i++)
703         DEBUG_OUT("%d ", pgdata->bUserArrCnnct[i]);
704     DEBUG_OUT("\n");
705 
706     DEBUG_OUT("bUserArrBrkpt : ");
707     for (i = 0; i <= pgdata->nPhoneSeq; i++)
708         DEBUG_OUT("%d ", pgdata->bUserArrBrkpt[i]);
709     DEBUG_OUT("\n");
710 
711     DEBUG_OUT("bArrBrkpt     : ");
712     for (i = 0; i <= pgdata->nPhoneSeq; i++)
713         DEBUG_OUT("%d ", pgdata->bArrBrkpt[i]);
714     DEBUG_OUT("\n");
715 
716     DEBUG_OUT("bChiSym : %d , bSelect : %d\n", pgdata->bChiSym, pgdata->bSelect);
717 }
718 
CallPhrasing(ChewingData * pgdata,int all_phrasing)719 int CallPhrasing(ChewingData *pgdata, int all_phrasing)
720 {
721     /* set "bSymbolArrBrkpt" && "bArrBrkpt" */
722     int i, ch_count = 0;
723 
724     memcpy(pgdata->bArrBrkpt, pgdata->bUserArrBrkpt, (MAX_PHONE_SEQ_LEN + 1) * sizeof(int));
725     memset(pgdata->bSymbolArrBrkpt, 0, (MAX_PHONE_SEQ_LEN + 1) * sizeof(int));
726 
727     for (i = 0; i < pgdata->chiSymbolBufLen; i++) {
728         if (ChewingIsChiAt(i, pgdata))
729             ch_count++;
730         else {
731             pgdata->bArrBrkpt[ch_count] = 1;
732             pgdata->bSymbolArrBrkpt[i] = 1;
733         }
734     }
735 
736     /* kill select interval */
737     for (i = 0; i < pgdata->nPhoneSeq; i++) {
738         if (pgdata->bArrBrkpt[i]) {
739             ChewingKillSelectIntervalAcross(i, pgdata);
740         }
741     }
742 
743     ShowChewingData(pgdata);
744 
745     /* then phrasing */
746     Phrasing(pgdata, all_phrasing);
747 
748     /* and then make prefer interval */
749     MakePreferInterval(pgdata);
750 
751     return 0;
752 }
753 
754 
Union(int set1,int set2,int parent[])755 static void Union(int set1, int set2, int parent[])
756 {
757     if (set1 != set2)
758         parent[max(set1, set2)] = min(set1, set2);
759 }
760 
SameSet(int set1,int set2,int parent[])761 static int SameSet(int set1, int set2, int parent[])
762 {
763     while (parent[set1] != 0) {
764         set1 = parent[set1];
765     }
766     while (parent[set2] != 0) {
767         set2 = parent[set2];
768     }
769     return (set1 == set2);
770 }
771 
772 /* make prefer interval from phrOut->dispInterval */
MakePreferInterval(ChewingData * pgdata)773 static void MakePreferInterval(ChewingData *pgdata)
774 {
775     int i, j, set_no;
776     int belong_set[MAX_PHONE_SEQ_LEN + 1];
777     int parent[MAX_PHONE_SEQ_LEN + 1];
778 
779     memset(belong_set, 0, sizeof(int) * (MAX_PHONE_SEQ_LEN + 1));
780     memset(parent, 0, sizeof(int) * (MAX_PHONE_SEQ_LEN + 1));
781 
782     /* for each interval */
783     for (i = 0; i < pgdata->phrOut.nDispInterval; i++) {
784         for (j = pgdata->phrOut.dispInterval[i].from; j < pgdata->phrOut.dispInterval[i].to; j++) {
785             belong_set[j] = i + 1;
786         }
787     }
788     set_no = i + 1;
789     for (i = 0; i < pgdata->nPhoneSeq; i++)
790         if (belong_set[i] == 0)
791             belong_set[i] = set_no++;
792 
793     /* for each connect point */
794     for (i = 1; i < pgdata->nPhoneSeq; i++) {
795         if (pgdata->bUserArrCnnct[i]) {
796             Union(belong_set[i - 1], belong_set[i], parent);
797         }
798     }
799 
800     /* generate new intervals */
801     pgdata->nPrefer = 0;
802     i = 0;
803     while (i < pgdata->nPhoneSeq) {
804         for (j = i + 1; j < pgdata->nPhoneSeq; j++)
805             if (!SameSet(belong_set[i], belong_set[j], parent))
806                 break;
807 
808         pgdata->preferInterval[pgdata->nPrefer].from = i;
809         pgdata->preferInterval[pgdata->nPrefer].to = j;
810         pgdata->nPrefer++;
811         i = j;
812     }
813 }
814 
815 /* for MakeOutput */
ShiftInterval(ChewingOutput * pgo,ChewingData * pgdata)816 static void ShiftInterval(ChewingOutput *pgo, ChewingData *pgdata)
817 {
818     int i, arrPos[MAX_PHONE_SEQ_LEN], k = 0, from, len;
819 
820     for (i = 0; i < pgdata->chiSymbolBufLen; i++) {
821         if (ChewingIsChiAt(i, pgdata)) {
822             arrPos[k++] = i;
823         }
824     }
825     arrPos[k] = i;
826 
827     pgo->nDispInterval = pgdata->nPrefer;
828     for (i = 0; i < pgdata->nPrefer; i++) {
829         from = pgdata->preferInterval[i].from;
830         len = pgdata->preferInterval[i].to - from;
831         pgo->dispInterval[i].from = arrPos[from];
832         pgo->dispInterval[i].to = arrPos[from] + len;
833     }
834 }
835 
MakeOutput(ChewingOutput * pgo,ChewingData * pgdata)836 int MakeOutput(ChewingOutput *pgo, ChewingData *pgdata)
837 {
838     int i;
839     int inx;
840     char *pos;
841 
842     /* fill zero to chiSymbolBuf first */
843     pgo->preeditBuf[0] = 0;
844     pgo->bopomofoBuf[0] = 0;
845 
846     pos = pgo->preeditBuf;
847     for (i = 0; i < pgdata->chiSymbolBufLen && pos < pgo->preeditBuf + sizeof(pgo->preeditBuf) + MAX_UTF8_SIZE + 1; ++i) {
848         strncpy(pos, pgdata->preeditBuf[i].char_, MAX_UTF8_SIZE + 1);
849         pos += strlen(pgdata->preeditBuf[i].char_);
850     }
851 
852     /* fill point */
853     pgo->PointStart = pgdata->PointStart;
854     pgo->PointEnd = pgdata->PointEnd;
855 
856     /* fill other fields */
857     pgo->chiSymbolBufLen = pgdata->chiSymbolBufLen;
858     pgo->chiSymbolCursor = pgdata->chiSymbolCursor;
859 
860     /* fill bopomofoBuf */
861     if (pgdata->bopomofoData.kbtype >= KB_HANYU_PINYIN) {
862         strcpy(pgo->bopomofoBuf, pgdata->bopomofoData.pinYinData.keySeq);
863     } else {
864         for (i = 0; i < BOPOMOFO_SIZE; i++) {
865             inx = pgdata->bopomofoData.pho_inx[i];
866             if (inx != 0) {
867                 ueStrNCpy(pgo->bopomofoBuf + strlen(pgo->bopomofoBuf),
868                           ueConstStrSeek(zhuin_tab[i], inx - 1),
869                           1, STRNCPY_CLOSE);
870             }
871         }
872     }
873 
874     ShiftInterval(pgo, pgdata);
875     memcpy(pgo->dispBrkpt, pgdata->bUserArrBrkpt, sizeof(pgo->dispBrkpt[0]) * (MAX_PHONE_SEQ_LEN + 1));
876     pgo->pci = &(pgdata->choiceInfo);
877     pgo->bChiSym = pgdata->bChiSym;
878     memcpy(pgo->selKey, pgdata->config.selKey, sizeof(pgdata->config.selKey));
879     pgdata->bShowMsg = 0;
880     return 0;
881 }
882 
MakeOutputWithRtn(ChewingOutput * pgo,ChewingData * pgdata,int keystrokeRtn)883 int MakeOutputWithRtn(ChewingOutput *pgo, ChewingData *pgdata, int keystrokeRtn)
884 {
885     pgo->keystrokeRtn = keystrokeRtn;
886     return MakeOutput(pgo, pgdata);
887 }
888 
MakeOutputAddMsgAndCleanInterval(ChewingOutput * pgo,ChewingData * pgdata)889 void MakeOutputAddMsgAndCleanInterval(ChewingOutput *pgo, ChewingData *pgdata)
890 {
891     pgdata->bShowMsg = 1;
892     pgo->nDispInterval = 0;
893 }
894 
AddSelect(ChewingData * pgdata,int sel_i)895 int AddSelect(ChewingData *pgdata, int sel_i)
896 {
897     int length, nSelect, cursor;
898 
899     /* save the typing time */
900     length = pgdata->availInfo.avail[pgdata->availInfo.currentAvail].len;
901     nSelect = pgdata->nSelect;
902 
903     /* change "selectStr" , "selectInterval" , and "nSelect" of ChewingData */
904     ueStrNCpy(pgdata->selectStr[nSelect], pgdata->choiceInfo.totalChoiceStr[sel_i], length, 1);
905     cursor = PhoneSeqCursor(pgdata);
906     pgdata->selectInterval[nSelect].from = cursor;
907     pgdata->selectInterval[nSelect].to = cursor + length;
908     pgdata->nSelect++;
909     return 0;
910 }
911 
CountSelKeyNum(int key,const ChewingData * pgdata)912 int CountSelKeyNum(int key, const ChewingData *pgdata)
913         /* return value starts from 0.  If less than zero : error key */
914 {
915     int i;
916 
917     for (i = 0; i < MAX_SELKEY; i++)
918         if (pgdata->config.selKey[i] == key)
919             return i;
920     return -1;
921 }
922 
CountSymbols(ChewingData * pgdata,int to)923 int CountSymbols(ChewingData *pgdata, int to)
924 {
925     int chi;
926     int i;
927 
928     for (chi = i = 0; i < to; i++) {
929         if (ChewingIsChiAt(i, pgdata))
930             chi++;
931     }
932     return to - chi;
933 }
934 
PhoneSeqCursor(ChewingData * pgdata)935 int PhoneSeqCursor(ChewingData *pgdata)
936 {
937     int cursor = pgdata->chiSymbolCursor - CountSymbols(pgdata, pgdata->chiSymbolCursor);
938 
939     return cursor > 0 ? cursor : 0;
940 }
941 
ChewingIsChiAt(int chiSymbolCursor,ChewingData * pgdata)942 int ChewingIsChiAt(int chiSymbolCursor, ChewingData *pgdata)
943 {
944     return pgdata->preeditBuf[chiSymbolCursor].category == CHEWING_CHINESE;
945 }
946 
RemoveSelectElement(int i,ChewingData * pgdata)947 void RemoveSelectElement(int i, ChewingData *pgdata)
948 {
949     if (--pgdata->nSelect == i)
950         return;
951     pgdata->selectInterval[i] = pgdata->selectInterval[pgdata->nSelect];
952     strcpy(pgdata->selectStr[i], pgdata->selectStr[pgdata->nSelect]);
953 }
954 
ChewingKillSelectIntervalAcross(int cursor,ChewingData * pgdata)955 static int ChewingKillSelectIntervalAcross(int cursor, ChewingData *pgdata)
956 {
957     int i;
958 
959     for (i = 0; i < pgdata->nSelect; i++) {
960         if (pgdata->selectInterval[i].from < cursor && pgdata->selectInterval[i].to > cursor) {
961             RemoveSelectElement(i, pgdata);
962             i--;
963         }
964     }
965     return 0;
966 }
967 
KillCharInSelectIntervalAndBrkpt(ChewingData * pgdata,int cursorToKill)968 static int KillCharInSelectIntervalAndBrkpt(ChewingData *pgdata, int cursorToKill)
969 {
970     int i;
971 
972     for (i = 0; i < pgdata->nSelect; i++) {
973         if (pgdata->selectInterval[i].from <= cursorToKill && pgdata->selectInterval[i].to > cursorToKill) {
974             RemoveSelectElement(i, pgdata);
975             i--;                /* the last one was swap to i, we need to recheck i */
976         } else if (pgdata->selectInterval[i].from > cursorToKill) {
977             pgdata->selectInterval[i].from--;
978             pgdata->selectInterval[i].to--;
979         }
980     }
981     assert(pgdata->nPhoneSeq >= cursorToKill);
982     memmove(&(pgdata->bUserArrBrkpt[cursorToKill]),
983             &(pgdata->bUserArrBrkpt[cursorToKill + 1]), sizeof(int) * (pgdata->nPhoneSeq - cursorToKill));
984     memmove(&(pgdata->bUserArrCnnct[cursorToKill]),
985             &(pgdata->bUserArrCnnct[cursorToKill + 1]), sizeof(int) * (pgdata->nPhoneSeq - cursorToKill));
986 
987     return 0;
988 }
989 
ChewingKillChar(ChewingData * pgdata,int chiSymbolCursorToKill,int minus)990 int ChewingKillChar(ChewingData *pgdata, int chiSymbolCursorToKill, int minus)
991 {
992     int tmp, cursorToKill;
993 
994     tmp = pgdata->chiSymbolCursor;
995     pgdata->chiSymbolCursor = chiSymbolCursorToKill;
996     cursorToKill = PhoneSeqCursor(pgdata);
997     pgdata->chiSymbolCursor = tmp;
998     if (ChewingIsChiAt(chiSymbolCursorToKill, pgdata)) {
999         KillCharInSelectIntervalAndBrkpt(pgdata, cursorToKill);
1000         assert(pgdata->nPhoneSeq - cursorToKill - 1 >= 0);
1001         memmove(&(pgdata->phoneSeq[cursorToKill]),
1002                 &(pgdata->phoneSeq[cursorToKill + 1]), (pgdata->nPhoneSeq - cursorToKill - 1) * sizeof(uint16_t));
1003         pgdata->nPhoneSeq--;
1004     }
1005     pgdata->symbolKeyBuf[chiSymbolCursorToKill] = 0;
1006     assert(pgdata->chiSymbolBufLen - chiSymbolCursorToKill);
1007     memmove(&pgdata->symbolKeyBuf[chiSymbolCursorToKill],
1008             &pgdata->symbolKeyBuf[chiSymbolCursorToKill + 1],
1009             sizeof(pgdata->symbolKeyBuf[0]) * (pgdata->chiSymbolBufLen - chiSymbolCursorToKill));
1010     memmove(&pgdata->preeditBuf[chiSymbolCursorToKill],
1011             &pgdata->preeditBuf[chiSymbolCursorToKill + 1],
1012             sizeof(pgdata->preeditBuf[0]) * (pgdata->chiSymbolBufLen - chiSymbolCursorToKill));
1013     pgdata->chiSymbolBufLen--;
1014     pgdata->chiSymbolCursor -= minus;
1015     if (pgdata->chiSymbolCursor < 0)
1016         pgdata->chiSymbolCursor = 0;
1017     return 0;
1018 }
1019 
IsPreferIntervalConnted(int cursor,ChewingData * pgdata)1020 int IsPreferIntervalConnted(int cursor, ChewingData *pgdata)
1021 {
1022     int i;
1023 
1024     for (i = 0; i < pgdata->nPrefer; i++) {
1025         if (pgdata->preferInterval[i].from < cursor && pgdata->preferInterval[i].to > cursor)
1026             return 1;
1027     }
1028     return 0;
1029 }
1030 
1031 static const char *const symbol_buf[][50] = {
1032     {"0", "\xC3\xB8", 0},
1033     /* "ø" */
1034     {"[", "\xE3\x80\x8C", "\xE3\x80\x8E", "\xE3\x80\x8A", "\xE3\x80\x88",
1035      "\xE3\x80\x90", "\xE3\x80\x94", 0},
1036     /* "「", "『", "《", "〈", "【", "〔" */
1037     {"]", "\xE3\x80\x8D", "\xE3\x80\x8F", "\xE3\x80\x8B", "\xE3\x80\x89",
1038      "\xE3\x80\x91", "\xE3\x80\x95", 0},
1039     /* "」", "』", "》", "〉", "】", "〕" */
1040     {"{", "\xEF\xBD\x9B", 0},
1041     /* "{" */
1042     {"}", "\xEF\xBD\x9D", 0},
1043     /* "}" */
1044     {"<", "\xEF\xBC\x8C", "\xE2\x86\x90", 0},
1045     /* ",", "←" */
1046     {">", "\xE3\x80\x82", "\xE2\x86\x92", "\xEF\xBC\x8E", 0},
1047     /* "。", "→", "." */
1048     {"?", "\xEF\xBC\x9F", "\xC2\xBF", 0},
1049     /* "?", "¿" */
1050     {"!", "\xEF\xBC\x81", "\xE2\x85\xA0", "\xC2\xA1", 0},
1051     /* "!", "Ⅰ","¡" */
1052     {"@", "\xEF\xBC\xA0", "\xE2\x85\xA1", "\xE2\x8A\x95", "\xE2\x8A\x99",
1053      "\xE3\x8A\xA3", "\xEF\xB9\xAB", 0},
1054     /* "@", "Ⅱ", "⊕", "⊙", "㊣", "﹫" */
1055     {"#", "\xEF\xBC\x83", "\xE2\x85\xA2", "\xEF\xB9\x9F", 0},
1056     /* "#", "Ⅲ", "﹟" */
1057     {"$", "\xEF\xBC\x84", "\xE2\x85\xA3", "\xE2\x82\xAC", "\xEF\xB9\xA9",
1058      "\xEF\xBF\xA0", "\xE2\x88\xAE", "\xEF\xBF\xA1", "\xEF\xBF\xA5", 0},
1059     /* "$", "Ⅳ", "€", "﹩", "¢", "∮","£", "¥" */
1060     {"%", "\xEF\xBC\x85", "\xE2\x85\xA4", 0},
1061     /* "%", "Ⅴ" */
1062     {"^", "\xEF\xB8\xBF", "\xE2\x85\xA5", "\xEF\xB9\x80", "\xEF\xB8\xBD",
1063      "\xEF\xB8\xBE", 0},
1064     /* "︿", "Ⅵ", "﹀", "︽", "︾" */
1065     {"&", "\xEF\xBC\x86", "\xE2\x85\xA6", "\xEF\xB9\xA0", 0},
1066     /* "&", "Ⅶ", "﹠" */
1067     {"*", "\xEF\xBC\x8A", "\xE2\x85\xA7", "\xC3\x97", "\xE2\x80\xBB",
1068      "\xE2\x95\xB3", "\xEF\xB9\xA1", "\xE2\x98\xAF", "\xE2\x98\x86",
1069      "\xE2\x98\x85", 0},
1070     /* "*", "Ⅷ", "×", "※", "╳", "﹡", "☯", "☆", "★" */
1071     {"(", "\xEF\xBC\x88", "\xE2\x85\xA8", 0},
1072     /* "(", "Ⅸ" */
1073     {")", "\xEF\xBC\x89", "\xE2\x85\xA9", 0},
1074     /* ")", "Ⅹ" */
1075     {"_", "\xE2\x80\x94", "\xEF\xBC\x8D", "\xE2\x80\x95", "\xE2\x80\x93",
1076      "\xE2\x86\x90", "\xE2\x86\x92", "\xEF\xBC\xBF", "\xEF\xBF\xA3",
1077      "\xEF\xB9\x8D", "\xEF\xB9\x89", "\xEF\xB9\x8E", "\xEF\xB9\x8A",
1078      "\xEF\xB9\x8F", "\xef\xb9\x8b", "\xE2\x80\xA6", "\xE2\x80\xA5",
1079      "\xC2\xAF", 0},
1080     /* "—", "-", "―", "–"
1081      * "←", "→", "_", " ̄"
1082      * "﹍", "﹉", "﹎", "﹊"
1083      * "﹏", "﹋", "…", "‥"
1084      * "¯" */
1085     {"+", "\xEF\xBC\x8B", "\xC2\xB1", "\xEF\xB9\xA2", 0},
1086     /* "+", "±", "﹢" */
1087     {"=", "\xEF\xBC\x9D", "\xE2\x89\x92", "\xE2\x89\xA0", "\xE2\x89\xA1",
1088      "\xE2\x89\xA6", "\xE2\x89\xA7", "\xEF\xB9\xA6", 0},
1089     /* "=", "≒", "≠", "≡", "≦", "≧", "﹦" */
1090     {"`", "\xE3\x80\x8F", "\xE3\x80\x8E", "\xE2\x80\xB2", "\xE2\x80\xB5", 0},
1091     /* "』", "『", "′", "‵" */
1092     {"~", "\xEF\xBD\x9E", 0},
1093     /* "~" */
1094     {":", "\xEF\xBC\x9A", "\xEF\xBC\x9B", "\xEF\xB8\xB0", "\xEF\xB9\x95", 0},
1095     /* ":", ";", "︰", "﹕" */
1096     {"\"", "\xEF\xBC\x9B", 0},
1097     /* ";" */
1098     {"\'", "\xE3\x80\x81", "\xE2\x80\xA6", "\xE2\x80\xA5", 0},
1099     /* "、", "…", "‥" */
1100     {"\\", "\xEF\xBC\xBC", "\xE2\x86\x96", "\xE2\x86\x98", "\xEF\xB9\xA8", 0},
1101     /* "\", "↖", "↘", "﹨" */
1102     {"-", "\xE2\x80\x94", "\xEF\xBC\x8D", "\xE2\x80\x95", "\xE2\x80\x93",
1103      "\xE2\x86\x90", "\xE2\x86\x92", "\xEF\xBC\xBF", "\xEF\xBF\xA3",
1104      "\xEF\xB9\x8D", "\xEF\xB9\x89", "\xEF\xB9\x8E", "\xEF\xB9\x8A",
1105      "\xEF\xB9\x8F", "\xef\xb9\x8b", "\xE2\x80\xA6", "\xE2\x80\xA5",
1106      "\xC2\xAF", 0},
1107     /* "—", "-", "―", "–"
1108      * "←", "→", "_", " ̄"
1109      * "﹍", "﹉", "﹎", "﹊"
1110      * "﹏", "﹋", "…", "‥"
1111      * "¯" */
1112     {"/", "\xEF\xBC\x8F", "\xC3\xB7", "\xE2\x86\x97", "\xE2\x86\x99",
1113      "\xE2\x88\x95", 0},
1114     /* "/","÷","↗","↙","∕" */
1115     {"|", "\xE2\x86\x91", "\xE2\x86\x93", "\xE2\x88\xA3", "\xE2\x88\xA5",
1116      "\xEF\xB8\xB1", "\xEF\xB8\xB3", "\xEF\xB8\xB4", 0},
1117     /* "↑", "↓", "∣", "∥", "︱", "︳", "︴" */
1118     {"A", "\xC3\x85", "\xCE\x91", "\xCE\xB1", "\xE2\x94\x9C", "\xE2\x95\xA0",
1119      "\xE2\x95\x9F", "\xE2\x95\x9E", 0},
1120     /* "Å","Α", "α", "├", "╠", "╟", "╞" */
1121     {"B", "\xCE\x92", "\xCE\xB2", "\xE2\x88\xB5", 0},
1122     /* "Β", "β","∵" */
1123     {"C", "\xCE\xA7", "\xCF\x87", "\xE2\x94\x98", "\xE2\x95\xAF",
1124      "\xE2\x95\x9D", "\xE2\x95\x9C", "\xE2\x95\x9B", "\xE3\x8F\x84",
1125      "\xE2\x84\x83", "\xE3\x8E\x9D", "\xE2\x99\xA3", "\xC2\xA9", 0},
1126     /* "Χ", "χ", "┘", "╯", "╝", "╜", "╛"
1127      * "㏄", "℃", "㎝", "♣", "©" */
1128     {"D", "\xCE\x94", "\xCE\xB4", "\xE2\x97\x87", "\xE2\x97\x86",
1129      "\xE2\x94\xA4", "\xE2\x95\xA3", "\xE2\x95\xA2", "\xE2\x95\xA1",
1130      "\xE2\x99\xA6", 0},
1131     /* "Δ", "δ", "◇", "◆", "┤", "╣", "╢", "╡","♦" */
1132     {"E", "\xCE\x95", "\xCE\xB5", "\xE2\x94\x90", "\xE2\x95\xAE",
1133      "\xE2\x95\x97", "\xE2\x95\x93", "\xE2\x95\x95", 0},
1134     /* "Ε", "ε", "┐", "╮", "╗", "╓", "╕" */
1135     {"F", "\xCE\xA6", "\xCF\x88", "\xE2\x94\x82", "\xE2\x95\x91",
1136      "\xE2\x99\x80", 0},
1137     /* "Φ", "ψ", "│", "║", "♀" */
1138     {"G", "\xCE\x93", "\xCE\xB3", 0},
1139     /* "Γ", "γ" */
1140     {"H", "\xCE\x97", "\xCE\xB7", "\xE2\x99\xA5", 0},
1141     /* "Η", "η","♥" */
1142     {"I", "\xCE\x99", "\xCE\xB9", 0},
1143     /* "Ι", "ι" */
1144     {"J", "\xCF\x86", 0},
1145     /* "φ" */
1146     {"K", "\xCE\x9A", "\xCE\xBA", "\xE3\x8E\x9E", "\xE3\x8F\x8E", 0},
1147     /* "Κ", "κ","㎞", "㏎" */
1148     {"L", "\xCE\x9B", "\xCE\xBB", "\xE3\x8F\x92", "\xE3\x8F\x91", 0},
1149     /* "Λ", "λ","㏒", "㏑" */
1150     {"M", "\xCE\x9C", "\xCE\xBC", "\xE2\x99\x82", "\xE2\x84\x93",
1151      "\xE3\x8E\x8E", "\xE3\x8F\x95", "\xE3\x8E\x9C", "\xE3\x8E\xA1", 0},
1152     /* "Μ", "μ", "♂", "ℓ", "㎎", "㏕", "㎜","㎡" */
1153     {"N", "\xCE\x9D", "\xCE\xBD", "\xE2\x84\x96", 0},
1154     /* "Ν", "ν","№" */
1155     {"O", "\xCE\x9F", "\xCE\xBF", 0},
1156     /* "Ο", "ο" */
1157     {"P", "\xCE\xA0", "\xCF\x80", 0},
1158     /* "Π", "π" */
1159     {"Q", "\xCE\x98", "\xCE\xB8", "\xD0\x94", "\xE2\x94\x8C", "\xE2\x95\xAD",
1160      "\xE2\x95\x94", "\xE2\x95\x93", "\xE2\x95\x92", 0},
1161     /* "Θ", "θ","Д","┌", "╭", "╔", "╓", "╒" */
1162     {"R", "\xCE\xA1", "\xCF\x81", "\xE2\x94\x80", "\xE2\x95\x90", "\xC2\xAE", 0},
1163     /* "Ρ", "ρ", "─", "═" ,"®" */
1164     {"S", "\xCE\xA3", "\xCF\x83", "\xE2\x88\xB4", "\xE2\x96\xA1",
1165      "\xE2\x96\xA0", "\xE2\x94\xBC", "\xE2\x95\xAC", "\xE2\x95\xAA",
1166      "\xE2\x95\xAB", "\xE2\x88\xAB", "\xC2\xA7", "\xE2\x99\xA0", 0},
1167     /* "Σ", "σ", "∴", "□", "■", "┼", "╬", "╪", "╫"
1168      * "∫", "§", "♠" */
1169     {"T", "\xCE\xA4", "\xCF\x84", "\xCE\xB8", "\xE2\x96\xB3", "\xE2\x96\xB2",
1170      "\xE2\x96\xBD", "\xE2\x96\xBC", "\xE2\x84\xA2", "\xE2\x8A\xBF",
1171      "\xE2\x84\xA2", 0},
1172     /* "Τ", "τ","θ","△","▲","▽","▼","™","⊿", "™" */
1173     {"U", "\xCE\xA5", "\xCF\x85", "\xCE\xBC", "\xE2\x88\xAA", "\xE2\x88\xA9", 0},
1174     /* "Υ", "υ","μ","∪", "∩" */
1175     {"V", "\xCE\xBD", 0},
1176     {"W", "\xE2\x84\xA6", "\xCF\x89", "\xE2\x94\xAC", "\xE2\x95\xA6",
1177      "\xE2\x95\xA4", "\xE2\x95\xA5", 0},
1178     /* "Ω", "ω", "┬", "╦", "╤", "╥" */
1179     {"X", "\xCE\x9E", "\xCE\xBE", "\xE2\x94\xB4", "\xE2\x95\xA9",
1180      "\xE2\x95\xA7", "\xE2\x95\xA8", 0},
1181     /* "Ξ", "ξ", "┴", "╩", "╧", "╨" */
1182     {"Y", "\xCE\xA8", 0},
1183     /* "Ψ" */
1184     {"Z", "\xCE\x96", "\xCE\xB6", "\xE2\x94\x94", "\xE2\x95\xB0",
1185      "\xE2\x95\x9A", "\xE2\x95\x99", "\xE2\x95\x98", 0},
1186     /* "Ζ", "ζ", "└", "╰", "╚", "╙", "╘" */
1187 };
1188 
FindSymbolKey(const char * symbol)1189 static int FindSymbolKey(const char *symbol)
1190 {
1191     unsigned int i;
1192     const char *const *buf;
1193 
1194     for (i = 0; i < ARRAY_SIZE(symbol_buf); ++i) {
1195         for (buf = symbol_buf[i]; *buf; ++buf) {
1196             if (0 == strcmp(*buf, symbol))
1197                 return *symbol_buf[i][0];
1198         }
1199     }
1200     return 0;
1201 }
1202 
OpenSymbolChoice(ChewingData * pgdata)1203 int OpenSymbolChoice(ChewingData *pgdata)
1204 {
1205     int i, symbol_buf_len = ARRAY_SIZE(symbol_buf);
1206     const char *const *pBuf;
1207     ChoiceInfo *pci = &(pgdata->choiceInfo);
1208 
1209     pci->oldChiSymbolCursor = pgdata->chiSymbolCursor;
1210 
1211     /* see if there is some word in the cursor position */
1212     if (pgdata->chiSymbolCursor == pgdata->chiSymbolBufLen)
1213         pgdata->chiSymbolCursor--;
1214     if (pgdata->symbolKeyBuf[pgdata->chiSymbolCursor] == NO_SYM_KEY) {
1215         pgdata->bSelect = 1;
1216         HaninSymbolInput(pgdata);
1217         return 0;
1218     }
1219     for (i = 0; i < symbol_buf_len; i++) {
1220         if (symbol_buf[i][0][0] == pgdata->symbolKeyBuf[pgdata->chiSymbolCursor]) {
1221             pBuf = symbol_buf[i];
1222             break;
1223         }
1224     }
1225     if (i == symbol_buf_len) {
1226         ChoiceEndChoice(pgdata);
1227         return 0;
1228     }
1229     pci->nTotalChoice = 0;
1230     for (i = 1; pBuf[i]; i++) {
1231         ueStrNCpy(pci->totalChoiceStr[pci->nTotalChoice], pBuf[i], ueStrLen(pBuf[i]), 1);
1232         pci->nTotalChoice++;
1233     }
1234 
1235     pci->nChoicePerPage = pgdata->config.candPerPage;
1236     assert(pci->nTotalChoice > 0);
1237     pci->nPage = CEIL_DIV(pci->nTotalChoice, pci->nChoicePerPage);
1238     pci->pageNo = 0;
1239     pci->isSymbol = SYMBOL_CHOICE_UPDATE;
1240 
1241     pgdata->bSelect = 1;
1242     pgdata->availInfo.nAvail = 1;
1243     pgdata->availInfo.currentAvail = 0;
1244     pgdata->availInfo.avail[0].id = NULL;
1245     pgdata->availInfo.avail[0].len = 1;
1246     return 0;
1247 }
1248 
InitSymbolTable(ChewingData * pgdata,const char * prefix)1249 int InitSymbolTable(ChewingData *pgdata, const char *prefix)
1250 {
1251     static const unsigned int MAX_SYMBOL_ENTRY = 100;
1252     static const size_t LINE_LEN = 512; // shall be long enough?
1253 
1254     char *filename = NULL;
1255     FILE *file = NULL;
1256     char *line = NULL;
1257     SymbolEntry **entry = NULL;
1258     char *category_end;
1259     const char *symbols;
1260     char *symbols_end;
1261     const char *symbol;
1262     size_t i;
1263     size_t len;
1264     size_t size;
1265     int ret = -1;
1266 
1267     pgdata->static_data.n_symbol_entry = 0;
1268     pgdata->static_data.symbol_table = NULL;
1269 
1270     ret = asprintf(&filename, "%s" PLAT_SEPARATOR "%s", prefix, SYMBOL_TABLE_FILE);
1271     if (ret == -1)
1272         goto error;
1273 
1274     file = fopen(filename, "r");
1275     if (!file)
1276         goto error;
1277 
1278     line = ALC(char, LINE_LEN);
1279 
1280     if (!line)
1281         goto error;
1282 
1283     entry = ALC(SymbolEntry *, MAX_SYMBOL_ENTRY);
1284 
1285     if (!entry)
1286         goto error;
1287 
1288     while (fgets(line, LINE_LEN, file) && pgdata->static_data.n_symbol_entry < MAX_SYMBOL_ENTRY) {
1289 
1290         category_end = strpbrk(line, "=\r\n");
1291         if (!category_end)
1292             goto error;
1293 
1294         symbols = category_end + 1;
1295         symbols_end = strpbrk(symbols, "\r\n");
1296         if (symbols_end) {
1297             *symbols_end = 0;
1298             len = ueStrLen(symbols);
1299 
1300             entry[pgdata->static_data.n_symbol_entry] =
1301                 (SymbolEntry *) malloc(sizeof(entry[0][0]) + sizeof(entry[0][0].symbols[0]) * len);
1302             if (!entry[pgdata->static_data.n_symbol_entry])
1303                 goto error;
1304             entry[pgdata->static_data.n_symbol_entry]
1305                 ->nSymbols = len;
1306 
1307             symbol = symbols;
1308 
1309             for (i = 0; i < len; ++i) {
1310                 ueStrNCpy(entry[pgdata->static_data.n_symbol_entry]->symbols[i], symbol, 1, 1);
1311                 // FIXME: What if symbol is combining sequences.
1312                 symbol += ueBytesFromChar(symbol[0]);
1313             }
1314 
1315 
1316         } else {
1317             entry[pgdata->static_data.n_symbol_entry] = (SymbolEntry *) malloc(sizeof(entry[0][0]));
1318             if (!entry[pgdata->static_data.n_symbol_entry])
1319                 goto error;
1320 
1321             entry[pgdata->static_data.n_symbol_entry]
1322                 ->nSymbols = 0;
1323         }
1324 
1325         *category_end = 0;
1326         ueStrNCpy(entry[pgdata->static_data.n_symbol_entry]->category, line, MAX_PHRASE_LEN, 1);
1327 
1328         ++pgdata->static_data.n_symbol_entry;
1329     }
1330 
1331     size = sizeof(*pgdata->static_data.symbol_table) * pgdata->static_data.n_symbol_entry;
1332     if (!size)
1333         goto end;
1334     pgdata->static_data.symbol_table = (SymbolEntry **) malloc(size);
1335     if (!pgdata->static_data.symbol_table)
1336         goto error;
1337     memcpy(pgdata->static_data.symbol_table, entry, size);
1338 
1339     ret = 0;
1340   end:
1341     free(entry);
1342     free(line);
1343     fclose(file);
1344     free(filename);
1345     return ret;
1346 
1347   error:
1348     for (i = 0; i < pgdata->static_data.n_symbol_entry; ++i) {
1349         free(entry[i]);
1350     }
1351     goto end;
1352 }
1353 
TerminateSymbolTable(ChewingData * pgdata)1354 void TerminateSymbolTable(ChewingData *pgdata)
1355 {
1356     unsigned int i;
1357 
1358     if (pgdata->static_data.symbol_table) {
1359         for (i = 0; i < pgdata->static_data.n_symbol_entry; ++i)
1360             free(pgdata->static_data.symbol_table[i]);
1361         free(pgdata->static_data.symbol_table);
1362         pgdata->static_data.n_symbol_entry = 0;
1363         pgdata->static_data.symbol_table = NULL;
1364     }
1365 }
1366 
InitEasySymbolInput(ChewingData * pgdata,const char * prefix)1367 int InitEasySymbolInput(ChewingData *pgdata, const char *prefix)
1368 {
1369     static const size_t LINE_LEN = 512; // shall be long enough?
1370 
1371     FILE *file = NULL;
1372     char *filename = NULL;
1373     char *line = NULL;
1374     int len;
1375     int _index;
1376     char *symbol;
1377     int ret = -1;
1378 
1379     ret = asprintf(&filename, "%s" PLAT_SEPARATOR "%s", prefix, SOFTKBD_TABLE_FILE);
1380     if (ret == -1)
1381         goto filenamefail;
1382 
1383     file = fopen(filename, "r");
1384     if (!file)
1385         goto fileopenfail;
1386 
1387     line = ALC(char, LINE_LEN);
1388     if (!line)
1389         goto linefail;
1390 
1391     while (fgets(line, LINE_LEN, file)) {
1392         if (' ' != line[1])
1393             continue;
1394 
1395         // Remove tailing \n
1396         len = strcspn(line, "\r\n");
1397 
1398         line[len] = '\0';
1399 
1400         _index = FindEasySymbolIndex(line[0]);
1401         if (-1 == _index)
1402             continue;
1403 
1404         len = ueStrLen(&line[2]);
1405         if (0 == len || len > MAX_PHRASE_LEN)
1406             continue;
1407 
1408         symbol = ALC(char, strlen(&line[2]) + 1);
1409 
1410         if (!symbol)
1411             goto end;
1412 
1413         ueStrNCpy(symbol, &line[2], len, 1);
1414 
1415         free(pgdata->static_data.g_easy_symbol_value[_index]);
1416         pgdata->static_data.g_easy_symbol_value[_index] = symbol;
1417         pgdata->static_data.g_easy_symbol_num[_index] = len;
1418     }
1419     ret = 0;
1420 
1421 end:
1422     free(line);
1423 
1424 linefail:
1425     fclose(file);
1426 
1427 fileopenfail:
1428     free(filename);
1429 
1430 filenamefail:
1431     return ret;
1432 }
1433 
TerminateEasySymbolTable(ChewingData * pgdata)1434 void TerminateEasySymbolTable(ChewingData *pgdata)
1435 {
1436     unsigned int i;
1437 
1438     for (i = 0; i < EASY_SYMBOL_KEY_TAB_LEN; ++i) {
1439         if (NULL != pgdata->static_data.g_easy_symbol_value[i]) {
1440             free(pgdata->static_data.g_easy_symbol_value[i]);
1441             pgdata->static_data.g_easy_symbol_value[i] = NULL;
1442         }
1443         pgdata->static_data.g_easy_symbol_num[i] = 0;
1444     }
1445 }
1446 
copyStringFromPreeditBuf(ChewingData * pgdata,int pos,int len,char * output,int output_len)1447 void copyStringFromPreeditBuf(ChewingData *pgdata, int pos, int len, char *output, int output_len)
1448 {
1449     int i;
1450     int x;
1451 
1452     assert(pgdata);
1453     assert(0 <= pos && (size_t) (pos + len) < ARRAY_SIZE(pgdata->preeditBuf));
1454     assert(output);
1455     assert(output_len);
1456 
1457     LOG_VERBOSE("Copy pos %d, len %d from preeditBuf", pos, len);
1458 
1459     for (i = pos; i < pos + len; ++i) {
1460         x = strlen(pgdata->preeditBuf[i].char_);
1461         if (x >= output_len)    // overflow
1462             return;
1463         memcpy(output, pgdata->preeditBuf[i].char_, x);
1464         output += x;
1465         output_len -= x;
1466     }
1467     output[0] = 0;
1468 }
1469 
1470 /*
1471  * This function converts phoneSeq index (which does not count symbol) to
1472  * preeditBuf index (which does count symbol).
1473  */
toPreeditBufIndex(ChewingData * pgdata,int pos)1474 int toPreeditBufIndex(ChewingData *pgdata, int pos)
1475 {
1476     int word_count;
1477     int i;
1478 
1479     assert(pgdata);
1480     assert(0 <= pos && pos <= MAX_CHI_SYMBOL_LEN);
1481 
1482     for (i = 0, word_count = 0; i < MAX_CHI_SYMBOL_LEN; ++i) {
1483         if (ChewingIsChiAt(i, pgdata))
1484             ++word_count;
1485 
1486         /*
1487          * pos = 0 means finding the first word, so we need to add one
1488          * here.
1489          */
1490         if (word_count == pos + 1)
1491             break;
1492     }
1493 
1494     LOG_VERBOSE("translate phoneSeq index %d to preeditBuf index %d", pos, i);
1495 
1496     return i;
1497 }
1498