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