1 /*
2 * 学習の履歴などを管理するためのデータベース
3 * 文字列(xstr)をキーにして高速に行(row)を検索することができる.
4 * 複数のセクションをもつことができ,学習の違うフェーズなどに対応する
5 * (セクション * 文字列 -> 行)
6 * 各行は文字列か数を持つ配列になっている
7 *
8 * 「パトリシア・トライ」というデータ構造を使用している。
9 * 自然言語の検索などを扱っている教科書を参照のこと
10 */
11 /*
12 This library is free software; you can redistribute it and/or
13 modify it under the terms of the GNU Lesser General Public
14 License as published by the Free Software Foundation; either
15 version 2 of the License, or (at your option) any later version.
16
17 This library is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 Lesser General Public License for more details.
21
22 You should have received a copy of the GNU Lesser General Public
23 License along with this library; if not, write to the Free Software
24 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26 /*
27 * Funded by IPA未踏ソフトウェア創造事業 2002 1/18
28 * Funded by IPA未踏ソフトウェア創造事業 2005
29 * Copyright (C) 2005 YOSHIDA Yuichi
30 * Copyright (C) 2000-2006 TABATA Yusuke
31 * Copyright (C) 2000-2003 UGAWA Tomoharu
32 * Copyright (C) 2001-2002 TAKAI Kosuke
33 */
34 /*
35 * パーソナリティ""は匿名パーソナリティであり,
36 * ファイルへの読み書きは行わない.
37 */
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <errno.h>
41 #include <unistd.h>
42 #include <string.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45
46 #include "config.h"
47 #include <anthy/anthy.h>
48 #include <anthy/dic.h>
49 #include <anthy/alloc.h>
50 #include <anthy/conf.h>
51 #include <anthy/ruleparser.h>
52 #include <anthy/record.h>
53 #include <anthy/logger.h>
54 #include <anthy/prediction.h>
55
56 #include "dic_main.h"
57 #include "dic_personality.h"
58
59 /* 個人辞書をセーブするファイル名のsuffix */
60 #define ENCODING_SUFFIX ".utf8"
61
62
63 enum val_type {
64 RT_EMPTY, RT_VAL, RT_XSTR, RT_XSTRP
65 };
66
67 /* 値 */
68 struct record_val {
69 enum val_type type;
70 union {
71 xstr str;
72 int val;
73 xstr* strp;
74 } u;
75 };
76
77 /* 行 */
78 struct record_row {
79 xstr key;
80 int nr_vals;
81 struct record_val *vals;
82 };
83
84 /* trie node管理用 */
85 struct trie_node {
86 struct trie_node *l;
87 struct trie_node *r;
88 int bit;
89 struct record_row row;
90 struct trie_node *lru_prev, *lru_next; /* 両端ループ */
91 int dirty; /* LRU のための used, sused ビット */
92 };
93
94 /* trie treeのroot */
95 struct trie_root {
96 struct trie_node root;
97 allocator node_ator;
98 };
99
100 #define LRU_USED 0x01
101 #define LRU_SUSED 0x02
102 #define PROTECT 0x04 /* 差分書き出し時に使う(LRUとは関係ない)
103 * 差分書き出しでは、ファイルに書き出す前に
104 * ファイル上に他のプロセスが記録した更新を
105 * 読み込む。それによって、これから追加しよ
106 * うとするノードが消されるのを防ぐ
107 */
108 /*
109 * LRU:
110 * USED: メモリ上で使われた
111 * SUSED: 保存された used ビット
112 *
113 * LRUリスト上では、 USED は必ずリスト先頭に並んでいるが、 SUSED は
114 * フラグなしのノードと混在している可能性がある。
115 *
116 * n個を残すように指定された時の動作
117 * 1. used > n
118 * LRU リストの先頭から n 番目以降を消す
119 * 2. used + sused > n
120 * used -> 残す
121 * sused -> sused フラグを落す
122 * それ以外 -> 消す
123 * 3. それ以外
124 * 全て残す
125 * ファイルに書き出す時に、 used || sused -> sused として書き出す
126 */
127
128 /** セクション */
129 struct record_section {
130 const char *name;
131 struct trie_root cols;
132 struct record_section *next;
133 int lru_nr_used, lru_nr_sused; /* LRU 用 */
134 };
135
136 /** データベース */
137 struct record_stat {
138 struct record_section section_list; /* sectionのリスト*/
139 struct record_section *cur_section;
140 struct trie_root xstrs; /* xstr を intern するための trie */
141 struct trie_node *cur_row;
142 int row_dirty; /* cur_row が保存の必要があるか */
143 int encoding;
144 /**/
145 int is_anon;
146 const char *id; /* パーソナリティのid */
147 char *base_fn; /* 基本ファイル 絶対パス */
148 char *journal_fn; /* 差分ファイル 絶対パス */
149 /**/
150 time_t base_timestamp; /* 基本ファイルのタイムスタンプ */
151 int last_update; /* 差分ファイルの最後に読んだ位置 */
152 time_t journal_timestamp; /* 差分ファイルのタイムスタンプ */
153 };
154
155 /* 差分が100KB越えたら基本ファイルへマージ */
156 #define FILE2_LIMIT 102400
157
158
159 /*
160 * xstr の intern:
161 * 個人ごと( record_stat ごと)に文字列を intern する。これは、
162 * メモリの節約の他に、データベースの flush 時にデータベースに
163 * 由来する xstr が無効になるのを防ぐ目的がある。
164 * したがって、データベースの flush 時でも xstr の intern 用
165 * のデータベース xstrs はそのまま保存する。
166 *
167 * xstrs: xstr の intern 用のデータベース
168 * row の key を intern された xstr として使う。
169 * row に value は持たない。
170 * (将来的には参照カウンタをつけてもいいかも)
171 * 参照: intern_xstr()
172 */
173
174 /*
175 * 差分書き出し:
176 * データベースの保存、複数の anthy ライブラリをリンクした
177 * プロセスの学習履歴の同期のために、学習履歴の更新情報を
178 * 逐一ファイルに書き出す。
179 *
180 * ・基本ファイル 古い anthy の学習履歴と同じ形式。
181 * 差分情報を適用する元となるファイル。
182 * 基本的には起動時だけに読み込む。
183 * このプログラム中でファイル1,baseと呼ぶことがある。
184 * ・差分ファイル 基本ファイルに対する更新情報。
185 * データベースに対する更新がコミットされるたびに
186 * 読み書きされる。
187 * このプログラム中でファイル2,journalと呼ぶことがある。
188 * 基本方針:
189 * データベースに対する更新がコミットされると、まず差分ファイル
190 * に他のプロセスが追加した更新情報を読み込み、その後に自分の
191 * コミットした更新を差分ファイルに書き出す。
192 * これらはロックファイルを用いてアトミックに行われる。また、
193 * 基本ファイル、差分ファイルとも、ロックを取っている間しか
194 * オープンしていてはいけない。
195 * 追加と削除:
196 * 追加はすでにメモリ上で更新された row をコミットによって
197 * メモリに書き出すため、
198 * 1. コミット対象 row 以外を差分ファイルの情報で
199 * 2. コミット対象 row を差分ファイルに書き出し
200 * とする。削除はまだメモリ上に row が残っている状態でコミット
201 * が行われる(削除要求をコミットとして扱う)ため、
202 * 1. 削除の情報を差分ファイルに書き出し
203 * 2. 差分ファイルの読み込みにより削除要求も実行する
204 * とする。
205 * 基本ファイルの更新:
206 * 差分ファイルがある程度肥大化すると、差分ファイルの情報を
207 * 基本ファイルに反映して差分ファイルを空にする。
208 * 更新するプロセス:
209 * 差分ファイルに書き出しを行った後、差分ファイルの大きさを調べ、
210 * 肥大化していれば、そのときのメモリ上のデータベース(これには
211 * 全ての差分ファイルの更新が適用されている)を基本ファイルに
212 * 書き出す。
213 * それ以外のプロセス:
214 * 差分ファイルを読む前に、基本ファイルが更新されているかを
215 * ファイルのタイムスタンプで調べ、更新されていれば、コミット
216 * された更新情報を直ちに更新ファイルに追加し、メモリ上の
217 * データベースを flush した後基本ファイル、差分ファイルを
218 * 読み込み直す。
219 * データベースの flush により、
220 * ・cur_row が無効になる (NULL になる)
221 * ・cur_section の有効性は保存される(sectionは解放しない)
222 * ・xstr は intern していれば保存される
223 * (すべての xstr は intern されているはず)
224 * 結局、次の様になる:
225 * if (基本ファイルが更新されている) {
226 * 差分ファイルへコミットされた更新を書き出す;
227 * データベースのフラッシュ;
228 * 基本ファイルの読込と差分ファイルの最終読込位置クリア;
229 * 差分ファイルの読込と差分ファイルの最終読込位置更新;
230 * } else {
231 * if (追加) {
232 * 差分ファイルの読込と差分ファイルの最終読込位置更新;
233 * 差分ファイルへの書き出し;
234 * } else {
235 * 差分ファイルへの書き出し;
236 * 差分ファイルの読込と差分ファイルの最終読込位置更新;
237 * }
238 * }
239 * if (差分ファイルが大きい) {
240 * 基本ファイルへの書き出し;
241 * 差分ファイルのクリア;
242 * }
243 */
244
245 static allocator record_ator;
246
247 /* trie操作用 */
248 static void init_trie_root(struct trie_root *n);
249 static int trie_key_nth_bit(xstr* key, int n);
250 static int trie_key_first_diff_bit_1byte(xchar c1, xchar c2);
251 static int trie_key_first_diff_bit(xstr *k1, xstr *k2);
252 static int trie_key_cmp(xstr *k1, xstr *k2);
253 static void trie_key_dup(xstr *dst, xstr *src);
254 static void trie_row_init(struct record_row *rc);
255 static void trie_row_free(struct record_row *rc);
256 static struct trie_node *trie_find(struct trie_root *root, xstr *key);
257 static struct trie_node *trie_insert(struct trie_root *root, xstr *key,
258 int dirty, int *nr_used, int *nr_sused);
259 static void trie_remove(struct trie_root *root, xstr *key,
260 int *nr_used, int *nr_sused);
261 static struct trie_node *trie_first(struct trie_root *root);
262 static struct trie_node *trie_next(struct trie_root *root,
263 struct trie_node *cur);
264 static void trie_remove_all(struct trie_root *root,
265 int *nr_used, int *nr_sused);
266 static void trie_remove_old(struct trie_root *root, int count,
267 int* nr_used, int* nr_sused);
268 static void trie_mark_used(struct trie_root *root, struct trie_node *n,
269 int *nr_used, int *nr_sused);
270
271
272 /*
273 * トライの実装
274 * struct trie_nodeのうちrow以外の部分とrow.keyを使用
275 * 削除の時はtrie_row_freeを使ってrowの内容を解放
276 */
277
278 #if 0
279 #define PUTNODE(x) ((x) == &root->root ? printf("root\n") : anthy_putxstrln(&(x)->row.key))
280 static int
281 debug_trie_dump(FILE* fp, struct trie_node* n, int encoding)
282 {
283 int cnt = 0;
284 char buf[1024];
285
286 if (n->l->bit > n->bit) {
287 cnt = debug_trie_dump(fp, n->l, encoding);
288 } else {
289 if (n->l->row.key.len == -1) {
290 if (fp) {
291 fprintf(fp, "root\n");
292 }
293 } else {
294 if (fp) {
295 anthy_sputxstr(buf, &n->l->row.key, encoding);
296 fprintf(fp, "%s\n", buf);
297 }
298 cnt = 1;
299 }
300 }
301
302 if (n->r->bit > n->bit) {
303 return cnt + debug_trie_dump(fp, n->r, encoding);
304 } else {
305 if (n->r->row.key.len == -1) {
306 if(fp) {
307 fprintf(fp, "root\n");
308 }
309 } else {
310 if(fp) {
311 anthy_sputxstr(buf, &n->r->row.key, encoding);
312 fprintf(fp, "%s\n", buf);
313 }
314 return cnt + 1;
315 }
316 }
317
318 return cnt;
319 }
320 #endif
321
322 static void
init_trie_root(struct trie_root * root)323 init_trie_root(struct trie_root *root)
324 {
325 struct trie_node* n;
326 root->node_ator = anthy_create_allocator(sizeof(struct trie_node), NULL);
327 n = &root->root;
328 n->l = n;
329 n->r = n;
330 n->bit = 0;
331 n->lru_next = n;
332 n->lru_prev = n;
333 n->dirty = 0;
334 trie_row_init(&n->row);
335 n->row.key.len = -1;
336 }
337
338 /*
339 * bit0: 0
340 * bit1: headのキーだけ0
341 * bit2: 文字列のビット0
342 * bit3: 文字列のビット1
343 * ...
344 * 文字列長を越えると0
345 */
346 static int
trie_key_nth_bit(xstr * key,int n)347 trie_key_nth_bit(xstr* key, int n)
348 {
349 switch (n) {
350 case 0:
351 return 0;
352 case 1:
353 return key->len + 1; /* key->len == -1 ? 0 : non-zero */
354 default:
355 {
356 int pos;
357 n -= 2;
358 pos = n / (sizeof(xchar) << 3);
359 if (pos >= key->len) {
360 return 0;
361 }
362 return key->str[pos] & (1 << (n % (sizeof(xchar) << 3)));
363 }
364 }
365 }
366
367 /* c1 == c2 では呼んではいけない */
368 static int
trie_key_first_diff_bit_1byte(xchar c1,xchar c2)369 trie_key_first_diff_bit_1byte(xchar c1, xchar c2)
370 {
371 int i;
372 int ptn;
373 for (i = 0, ptn = c1 ^ c2; !(ptn & 1); i++, ptn >>= 1 )
374 ;
375 return i;
376 }
377
378 /*
379 * k1 == k2 では呼んではいけない
380 * ki->str[0 .. (ki->len - 1)]に0はないと仮定
381 */
382 #define MIN(a,b) ((a)<(b)?(a):(b))
383 static int
trie_key_first_diff_bit(xstr * k1,xstr * k2)384 trie_key_first_diff_bit(xstr *k1, xstr *k2)
385 {
386 int len;
387 int i;
388
389 len = MIN(k1->len, k2->len);
390 if (len == -1) {
391 return 1;
392 }
393 for ( i = 0 ; i < len ; i++ ){
394 if (k1->str[i] != k2->str[i]) {
395 return (2 + (i * (sizeof(xchar) << 3)) +
396 trie_key_first_diff_bit_1byte(k1->str[i], k2->str[i]));
397 }
398 }
399 if (k1->len < k2->len) {
400 return (2 + (i * (sizeof(xchar) << 3)) +
401 trie_key_first_diff_bit_1byte(0, k2->str[i]));
402 } else {
403 return (2 + (i * (sizeof(xchar) << 3)) +
404 trie_key_first_diff_bit_1byte(k1->str[i], 0));
405 }
406 }
407 #undef MIN
408
409 static int
trie_key_cmp(xstr * k1,xstr * k2)410 trie_key_cmp(xstr *k1, xstr *k2)
411 {
412 if (k1->len == -1 || k2->len == -1) {
413 return k1->len - k2->len;
414 }
415 return anthy_xstrcmp(k1, k2);
416 }
417
418 static void
trie_key_dup(xstr * dst,xstr * src)419 trie_key_dup(xstr *dst, xstr *src)
420 {
421 dst->str = anthy_xstr_dup_str(src);
422 dst->len = src->len;
423 }
424
425 /*
426 * 見つからなければ 0
427 */
428 static struct trie_node *
trie_find(struct trie_root * root,xstr * key)429 trie_find(struct trie_root *root, xstr *key)
430 {
431 struct trie_node *p;
432 struct trie_node *q;
433
434 p = &root->root;
435 q = p->l;
436 while (p->bit < q->bit) {
437 p = q;
438 q = trie_key_nth_bit(key, p->bit) ? p->r : p->l;
439 }
440 return trie_key_cmp(&q->row.key,key) ? NULL : q;
441 }
442
443 /*
444 * 最長マッチのための補助関数
445 * key で探索して、始めて一致しなくなったノードを返す。
446 */
447 static struct trie_node *
trie_find_longest(struct trie_root * root,xstr * key)448 trie_find_longest (struct trie_root* root, xstr *key)
449 {
450 struct trie_node *p;
451 struct trie_node *q;
452
453 p = &root->root;
454 q = p->l;
455 while (p->bit < q->bit) {
456 p = q;
457 q = trie_key_nth_bit(key, p->bit) ? p->r : p->l;
458 }
459
460 return q;
461 }
462
463 /*
464 * 追加したノードを返す
465 * すでに同じキーをもつノードがあるときは、追加せずに0を返す
466 */
467 static struct trie_node *
trie_insert(struct trie_root * root,xstr * key,int dirty,int * nr_used,int * nr_sused)468 trie_insert(struct trie_root *root, xstr *key,
469 int dirty, int *nr_used, int *nr_sused)
470 {
471 struct trie_node *n;
472 struct trie_node *p;
473 struct trie_node *q;
474 int i;
475
476 p = &root->root;
477 q = p->l;
478 while (p->bit < q->bit) {
479 p = q;
480 q = trie_key_nth_bit(key, p->bit) ? p->r : p->l;
481 }
482 if (trie_key_cmp(&q->row.key,key) == 0) {
483 /* USED > SUSED > 0 で強い方を残す */
484 if (dirty == LRU_USED) {
485 trie_mark_used(root, q, nr_used, nr_sused);
486 } else if (q->dirty == 0) {
487 q->dirty = dirty;
488 }
489 return 0;
490 }
491 i = trie_key_first_diff_bit(&q->row.key, key);
492 p = &root->root;
493 q = p->l;
494 while (p->bit < q->bit && i > q->bit) {
495 p = q;
496 q = trie_key_nth_bit(key, p->bit) ? p->r : p->l;
497 }
498 n = anthy_smalloc(root->node_ator);
499 trie_row_init(&n->row);
500 trie_key_dup(&n->row.key, key);
501 n->bit = i;
502 if (trie_key_nth_bit(key, i)) {
503 n->l = q;
504 n->r = n;
505 } else {
506 n->l = n;
507 n->r = q;
508 }
509 if (p->l == q) {
510 p->l = n;
511 } else {
512 p->r = n;
513 }
514
515 /* LRU の処理 */
516 if (dirty == LRU_USED) {
517 root->root.lru_next->lru_prev = n;
518 n->lru_prev = &root->root;
519 n->lru_next = root->root.lru_next;
520 root->root.lru_next = n;
521 (*nr_used)++;
522 } else {
523 root->root.lru_prev->lru_next = n;
524 n->lru_next = &root->root;
525 n->lru_prev = root->root.lru_prev;
526 root->root.lru_prev = n;
527 if (dirty == LRU_SUSED) {
528 (*nr_sused)++;
529 }
530 }
531 n->dirty = dirty;
532 return n;
533 }
534
535 /*
536 * ノードを見つけると削除する
537 * 内部でtrie_row_freeを呼び、キーを含むデータ部分をfreeする
538 *
539 * データとノードを削除する。
540 * 削除対象のデータは削除対象のノードに格納されているとは
541 * 限らないことに注意。
542 * 1. 削除対象の葉を持つノードに削除対象の葉が含まれているとき
543 * 削除対象のノードは、子への枝のうち、生きのこる枝を親に渡して死ぬ
544 * 2. 削除対象の葉を持つノードの祖先に削除対象の葉が含まれているとき
545 * 1. に加えて、削除対象の葉をもつノードを殺して、代わりに削除
546 * 対象のノードを削除対象の葉をもつノードの位置に移動させ生かす
547 */
548 static void
trie_remove(struct trie_root * root,xstr * key,int * nr_used,int * nr_sused)549 trie_remove(struct trie_root *root, xstr *key,
550 int *nr_used, int *nr_sused)
551 {
552 struct trie_node *p;
553 struct trie_node *q;
554 struct trie_node **pp = NULL; /* gcc の warning 回避 */
555 struct trie_node **qq;
556 p = &root->root;
557 qq = &p->l;
558 q = *qq;
559 while (p->bit < q->bit) {
560 pp = qq;
561 p = q;
562 qq = trie_key_nth_bit(key,p->bit) ? &p->r : &p->l;
563 q = *qq;
564 }
565 if (trie_key_cmp(&q->row.key, key) != 0) {
566 return ;
567 }
568 if (p != q) {
569 /* case 2. */
570 struct trie_node *r;
571 struct trie_node *s;
572 r = &root->root;
573 s = r->l;
574 while (s != q) {
575 r = s;
576 s = trie_key_nth_bit(key, r->bit) ? r->r : r->l;
577 }
578 *pp = (p->r == q) ? p->l : p->r;
579 p->l = q->l;
580 p->r = q->r;
581 p->bit = q->bit;
582 if (trie_key_nth_bit(key, r->bit)) {
583 r->r = p;
584 } else {
585 r->l = p;
586 }
587 p = q;
588 } else {
589 *pp = (p->r == q) ? p->l : p->r;
590 }
591 p->lru_prev->lru_next = p->lru_next;
592 p->lru_next->lru_prev = p->lru_prev;
593 if (p->dirty == LRU_USED) {
594 (*nr_used)--;
595 } else if (p->dirty == LRU_SUSED) {
596 (*nr_sused)--;
597 }
598 trie_row_free(&p->row);
599 anthy_sfree(root->node_ator, p);
600 }
601
602 /* head以外のノードがなければ 0 を返す */
603 static struct trie_node *
trie_first(struct trie_root * root)604 trie_first (struct trie_root *root)
605 {
606 return root->root.lru_next == &root->root ?
607 NULL : root->root.lru_next;
608 }
609
610 /* 次のノードがなければ 0 を返す */
611 static struct trie_node *
trie_next(struct trie_root * root,struct trie_node * cur)612 trie_next (struct trie_root *root,
613 struct trie_node *cur)
614 {
615 return cur->lru_next == &root->root ? 0 : cur->lru_next;
616 }
617
618 /*
619 * head以外全てのノードを削除する
620 * 内部でtrie_row_freeを呼び、キーを含むデータ部分をfreeする
621 */
622 static void
trie_remove_all(struct trie_root * root,int * nr_used,int * nr_sused)623 trie_remove_all (struct trie_root *root,
624 int *nr_used, int *nr_sused)
625 {
626 struct trie_node* p;
627 for (p = root->root.lru_next; p != &root->root; p = p->lru_next) {
628 trie_row_free(&p->row);
629 }
630 anthy_free_allocator(root->node_ator);
631 init_trie_root(root);
632 *nr_used = 0;
633 *nr_sused = 0;
634 }
635
636 /*
637 * LRU リストの先頭から count 番目までを残して残りを解放する
638 */
639 static void
trie_remove_old(struct trie_root * root,int count,int * nr_used,int * nr_sused)640 trie_remove_old (struct trie_root *root, int count,
641 int *nr_used, int *nr_sused)
642 {
643 struct trie_node *p;
644 struct trie_node *q;
645
646 if (*nr_used > count) {
647 for (p = root->root.lru_next; count; count--, p = p->lru_next)
648 ;
649 /* p から head までを消す */
650 for ( ; p != &root->root; p = q) {
651 q = p->lru_next;
652 trie_remove(root, &p->row.key, nr_used, nr_sused);
653 }
654 } else if (*nr_used + *nr_sused > count) {
655 for (p = root->root.lru_next; p->dirty == LRU_USED; p = p->lru_next)
656 ;
657 /*
658 * p から root まで sused -> dirty := 0
659 * それ以外 -> 消す
660 */
661 for ( ; p != &root->root; p = q) {
662 q = p->lru_next;
663 if (p->dirty == LRU_SUSED) {
664 p->dirty = 0;
665 } else {
666 trie_remove(root, &p->row.key, nr_used, nr_sused);
667 }
668 }
669 *nr_sused = 0;
670 }
671 }
672
673 static void
trie_mark_used(struct trie_root * root,struct trie_node * n,int * nr_used,int * nr_sused)674 trie_mark_used (struct trie_root *root, struct trie_node *n,
675 int *nr_used, int *nr_sused)
676 {
677 switch(n->dirty) {
678 case LRU_USED:
679 break;
680 case LRU_SUSED:
681 (*nr_sused)--;
682 /* fall through */
683 default:
684 n->dirty = LRU_USED;
685 (*nr_used)++;
686 break;
687 }
688 n->lru_prev->lru_next = n->lru_next;
689 n->lru_next->lru_prev = n->lru_prev;
690 root->root.lru_next->lru_prev = n;
691 n->lru_next = root->root.lru_next;
692 root->root.lru_next = n;
693 n->lru_prev = &root->root;
694 }
695
696 /*
697 * トライの実装はここまで
698 */
699
700 static xstr *
do_get_index_xstr(struct record_stat * rec)701 do_get_index_xstr(struct record_stat *rec)
702 {
703 if (!rec->cur_row) {
704 return 0;
705 }
706 return &rec->cur_row->row.key;
707 }
708
709 static struct record_section*
do_select_section(struct record_stat * rst,const char * name,int flag)710 do_select_section(struct record_stat *rst, const char *name, int flag)
711 {
712 struct record_section *rsc;
713
714 for (rsc = rst->section_list.next; rsc; rsc = rsc->next) {
715 if (!strcmp(name, rsc->name)) {
716 return rsc;
717 }
718 }
719
720 if (flag) {
721 rsc = malloc(sizeof(struct record_section));
722 rsc->name = strdup(name);
723 rsc->next = rst->section_list.next;
724 rst->section_list.next = rsc;
725 rsc->lru_nr_used = 0;
726 rsc->lru_nr_sused = 0;
727 init_trie_root(&rsc->cols);
728 return rsc;
729 }
730
731 return NULL;
732 }
733
734 static struct trie_node*
do_select_longest_row(struct record_section * rsc,xstr * name)735 do_select_longest_row(struct record_section *rsc, xstr *name)
736 {
737 struct trie_node *mark, *found;
738 xstr xs;
739 int i;
740
741 if ((NULL == name) || (NULL == name->str) || (name->len < 1) || (0 == name->str[0])) {
742 /* 辞書もしくは学習データが壊れていた時の対策 */
743 return NULL;
744 }
745
746 mark = trie_find_longest(&rsc->cols, name);
747 xs.str = name->str;
748 for (i = (mark->row.key.len <= name->len) ? mark->row.key.len : name->len; i > 1; i--) { /* 不正なメモリアクセスの修正 */
749 /* ルートノードは i == 1 でマッチするので除外
750 * trie_key_nth_bit 参照
751 */
752 xs.len = i;
753 found = trie_find(&rsc->cols, &xs);
754 if (found) {
755 return found;
756 }
757 }
758 return NULL;
759 }
760
761 static struct trie_node*
do_select_row(struct record_section * rsc,xstr * name,int flag,int dirty)762 do_select_row(struct record_section* rsc, xstr *name,
763 int flag, int dirty)
764 {
765 struct trie_node *node;
766
767 if (flag) {
768 node = trie_insert(&rsc->cols, name, dirty,
769 &rsc->lru_nr_used, &rsc->lru_nr_sused);
770 if (node) {
771 node->row.nr_vals = 0;
772 node->row.vals = 0;
773 } else {
774 node = trie_find(&rsc->cols, name);
775 }
776 } else {
777 node = trie_find(&rsc->cols, name);
778 }
779 return node;
780 }
781
782 static void
do_mark_row_used(struct record_section * rsc,struct trie_node * node)783 do_mark_row_used(struct record_section* rsc, struct trie_node* node)
784 {
785 trie_mark_used(&rsc->cols, node, &rsc->lru_nr_used, &rsc->lru_nr_sused);
786 }
787
788 static void
do_truncate_section(struct record_stat * s,int count)789 do_truncate_section(struct record_stat *s, int count)
790 {
791 if (!s->cur_section) {
792 return;
793 }
794
795 trie_remove_old(&s->cur_section->cols, count,
796 &s->cur_section->lru_nr_used,
797 &s->cur_section->lru_nr_sused);
798 }
799
800
801 static struct trie_node*
do_select_first_row(struct record_section * rsc)802 do_select_first_row(struct record_section *rsc)
803 {
804 return trie_first(&rsc->cols);
805 }
806
807 static struct trie_node*
do_select_next_row(struct record_section * rsc,struct trie_node * node)808 do_select_next_row(struct record_section *rsc,
809 struct trie_node* node)
810 {
811 return trie_next(&rsc->cols, node);
812 }
813
814
815 static int
do_get_nr_values(struct trie_node * node)816 do_get_nr_values(struct trie_node *node)
817 {
818 if (!node)
819 return 0;
820 return node->row.nr_vals;
821 }
822
823 static struct record_val *
get_nth_val_ent(struct trie_node * node,int n,int f)824 get_nth_val_ent(struct trie_node *node, int n, int f)
825 {
826 struct record_row *col;
827 col = &node->row;
828 if (n < 0) {
829 return NULL;
830 }
831 if (n < do_get_nr_values(node)) {
832 return &col->vals[n];
833 }
834 if (f) {
835 int i;
836 col->vals = realloc(col->vals, sizeof(struct record_val)*(n + 1));
837 for (i = col->nr_vals; i < n+1; i++) {
838 col->vals[i].type = RT_EMPTY;
839 }
840 col->nr_vals = n + 1;
841 return &col->vals[n];
842 }
843 return NULL;
844 }
845
846 static void
free_val_contents(struct record_val * v)847 free_val_contents(struct record_val* v)
848 {
849 switch (v->type) {
850 case RT_XSTR:
851 anthy_free_xstr_str(&v->u.str);
852 break;
853 case RT_XSTRP:
854 case RT_VAL:
855 case RT_EMPTY:
856 default:
857 break;
858 }
859 }
860
861 static void
do_set_nth_value(struct trie_node * node,int nth,int val)862 do_set_nth_value(struct trie_node *node, int nth, int val)
863 {
864 struct record_val *v = get_nth_val_ent(node, nth, 1);
865 if (!v) {
866 return ;
867 }
868 free_val_contents(v);
869 v->type = RT_VAL;
870 v->u.val = val;
871 }
872
873 static int
do_get_nth_value(struct trie_node * node,int n)874 do_get_nth_value(struct trie_node *node, int n)
875 {
876 struct record_val *v = get_nth_val_ent(node, n, 0);
877 if (v && v->type == RT_VAL) {
878 return v->u.val;
879 }
880 return 0;
881 }
882
883 static xstr*
intern_xstr(struct trie_root * xstrs,xstr * xs)884 intern_xstr (struct trie_root* xstrs, xstr* xs)
885 {
886 struct trie_node* node;
887 int dummy;
888
889 if ((NULL == xs) || (NULL == xs->str) || (xs->len < 1) || (0 == xs->str[0])) {
890 /* 辞書もしくは学習データが壊れていた時の対策 */
891 return NULL;
892 }
893 node = trie_find(xstrs, xs);
894 if (!node)
895 node = trie_insert(xstrs, xs, 0, &dummy, &dummy);
896 return &node->row.key;
897 }
898
899 static void
do_set_nth_xstr(struct trie_node * node,int nth,xstr * xs,struct trie_root * xstrs)900 do_set_nth_xstr (struct trie_node *node, int nth, xstr *xs,
901 struct trie_root* xstrs)
902 {
903 struct record_val *v = get_nth_val_ent(node, nth, 1);
904 if (!v){
905 return ;
906 }
907 free_val_contents(v);
908 v->type = RT_XSTRP;
909 v->u.strp = intern_xstr(xstrs, xs);
910 }
911
912 static void
do_truncate_row(struct trie_node * node,int n)913 do_truncate_row (struct trie_node* node, int n)
914 {
915 int i;
916 if (n < node->row.nr_vals) {
917 for (i = n; i < node->row.nr_vals; i++) {
918 free_val_contents(node->row.vals + i);
919 }
920 node->row.vals = realloc(node->row.vals,
921 sizeof(struct record_val)* n);
922 node->row.nr_vals = n;
923 }
924 }
925
926 static void
do_remove_row(struct record_section * rsc,struct trie_node * node)927 do_remove_row (struct record_section* rsc,
928 struct trie_node* node)
929 {
930 xstr* xs;
931 xs = anthy_xstr_dup(&node->row.key);
932 trie_remove(&rsc->cols, &node->row.key,
933 &rsc->lru_nr_used, &rsc->lru_nr_sused);
934
935 anthy_free_xstr(xs);
936 }
937
938 static xstr *
do_get_nth_xstr(struct trie_node * node,int n)939 do_get_nth_xstr(struct trie_node *node, int n)
940 {
941 struct record_val *v = get_nth_val_ent(node, n, 0);
942 if (v) {
943 if (v->type == RT_XSTR) {
944 return &v->u.str;
945 } else if (v->type == RT_XSTRP) {
946 return v->u.strp;
947 }
948 }
949 return 0;
950 }
951
952 static void
lock_record(struct record_stat * rs)953 lock_record (struct record_stat* rs)
954 {
955 if (rs->is_anon) {
956 return ;
957 }
958 anthy_priv_dic_lock();
959 }
960
961 static void
unlock_record(struct record_stat * rs)962 unlock_record (struct record_stat* rs)
963 {
964 if (rs->is_anon) {
965 return ;
966 }
967 anthy_priv_dic_unlock();
968 }
969
970 /* 再読み込みの必要があるかをチェックする
971 * 必要があれば返り値が1になる */
972 static int
check_base_record_uptodate(struct record_stat * rst)973 check_base_record_uptodate(struct record_stat *rst)
974 {
975 struct stat st;
976 if (rst->is_anon) {
977 return 0;
978 }
979 anthy_check_user_dir();
980 if (stat(rst->base_fn, &st) < 0) {
981 return 0;
982 } else if (st.st_mtime == rst->base_timestamp) {
983 return 0;
984 }
985 return 1;
986 }
987
988
989 /*
990 * row format:
991 * ROW := OPERATION SECTION KEY VALUE*
992 * OPERATION := "ADD" (追加またはLRU更新)
993 * "DEL" (削除)
994 * SECTION := (文字列)
995 * KEY := TD
996 * VALUE := TD
997 * TD := TYPE DATA (空白をあけずに書く)
998 * TYPE := "S" (xstr)
999 * "N" (number)
1000 * DATA := (型ごとにシリアライズしたもの)
1001 */
1002
1003 static char*
read_1_token(FILE * fp,int * eol)1004 read_1_token (FILE* fp, int* eol)
1005 {
1006 int c;
1007 char* s;
1008 int in_quote;
1009 int len;
1010
1011 in_quote = 0;
1012 s = NULL;
1013 len = 0;
1014 while (1) {
1015 c = fgetc(fp);
1016 switch (c) {
1017 case EOF: case '\n':
1018 goto out;
1019 case '\\':
1020 c = fgetc(fp);
1021 if (c == EOF || c == '\n') {
1022 goto out;
1023 }
1024 break;
1025 case '\"':
1026 in_quote = !in_quote;
1027 continue;
1028 case ' ': case '\t': case '\r':
1029 if (in_quote) {
1030 break;
1031 }
1032 if (s != NULL) {
1033 goto out;
1034 }
1035 break;
1036 default:
1037 break;
1038 }
1039
1040 s = (char*) realloc(s, len + 2);
1041 s[len] = c;
1042 len ++;
1043 }
1044 out:
1045 if (s) {
1046 s[len] = '\0';
1047 }
1048 *eol = (c == '\n' || c == EOF);
1049 return s;
1050 }
1051
1052 /* journalからADDの行を読む */
1053 static void
read_add_row(FILE * fp,struct record_stat * rst,struct record_section * rsc)1054 read_add_row(FILE *fp, struct record_stat* rst,
1055 struct record_section* rsc)
1056 {
1057 int n;
1058 xstr* xs;
1059 char *token;
1060 int eol;
1061 struct trie_node* node;
1062
1063 token = read_1_token(fp, &eol);
1064 if (!token) {
1065 return ;
1066 }
1067
1068 xs = anthy_cstr_to_xstr(/* xstr 型を表す S を読み捨てる */
1069 token + 1,
1070 rst->encoding);
1071 node = do_select_row(rsc, xs, 1, LRU_USED);
1072 anthy_free_xstr(xs);
1073 free(token);
1074
1075 if (node->dirty & PROTECT) {
1076 /* 保存すべき row なので、差分ファイルを読み捨てる */
1077 while (!eol) {
1078 free(read_1_token(fp, &eol));
1079 }
1080 return ;
1081 }
1082
1083 n = 0;
1084 while (!eol) {
1085 token = read_1_token(fp, &eol);
1086 if (token) {
1087 switch(*token) {
1088 /* String 文字列 */
1089 case 'S':
1090 {
1091 xstr* xs;
1092 xs = anthy_cstr_to_xstr(token + 1, rst->encoding);
1093 do_set_nth_xstr(node, n, xs, &rst->xstrs);
1094 anthy_free_xstr(xs);
1095 }
1096 break;
1097 /* Number 数値 */
1098 case 'N':
1099 do_set_nth_value(node, n, atoi(token + 1));
1100 break;
1101 }
1102 free(token);
1103 n++;
1104 }
1105 }
1106 do_truncate_row(node, n);
1107 }
1108
1109 /* journalからDELの行を読む */
1110 static void
read_del_row(FILE * fp,struct record_stat * rst,struct record_section * rsc)1111 read_del_row(FILE *fp, struct record_stat* rst,
1112 struct record_section* rsc)
1113 {
1114 struct trie_node* node;
1115 char* token;
1116 xstr* xs;
1117 int eol;
1118
1119 token = read_1_token(fp, &eol);
1120 if (!token) {
1121 return ;
1122 }
1123
1124 xs = anthy_cstr_to_xstr(/* xstr 型を表す S を読み飛ばす */
1125 token + 1,
1126 rst->encoding);
1127 if ((node = do_select_row(rsc, xs, 0, 0)) != NULL) {
1128 do_remove_row(rsc, node);
1129 }
1130 anthy_free_xstr(xs);
1131 free(token);
1132 }
1133
1134 /** 差分ファイルから1行読み込む */
1135 static void
read_1_row(struct record_stat * rst,FILE * fp,char * op)1136 read_1_row(struct record_stat* rst, FILE* fp, char *op)
1137 {
1138 char* sec_name;
1139 struct record_section* rsc;
1140 int eol;
1141
1142 sec_name = read_1_token(fp, &eol);
1143 if (!sec_name || eol) {
1144 free(sec_name);
1145 return ;
1146 }
1147 rsc = do_select_section(rst, sec_name, 1);
1148 free(sec_name);
1149 if (!rsc) {
1150 return ;
1151 }
1152
1153 if (strcmp(op, "ADD") == 0) {
1154 read_add_row(fp, rst, rsc);
1155 } else if (strcmp(op, "DEL") == 0) {
1156 read_del_row(fp, rst, rsc);
1157 }
1158 }
1159
1160 /*
1161 * journal(差分)ファイルを読む
1162 */
1163 static void
read_journal_record(struct record_stat * rs)1164 read_journal_record(struct record_stat* rs)
1165 {
1166 FILE* fp;
1167 struct stat st;
1168
1169 if (rs->is_anon) {
1170 return ;
1171 }
1172 fp = fopen(rs->journal_fn, "r");
1173 if (fp == NULL) {
1174 return;
1175 }
1176 if (fstat(fileno(fp), &st) == -1) {
1177 fclose(fp);
1178 return ;
1179 }
1180 if (st.st_size < rs->last_update) {
1181 /* ファイルサイズが小さくなっているので、
1182 * 最初から読み込む */
1183 fseek(fp, 0, SEEK_SET);
1184 } else {
1185 fseek(fp, rs->last_update, SEEK_SET);
1186 }
1187 rs->journal_timestamp = st.st_mtime;
1188 while (!feof(fp)) {
1189 char *op;
1190 int eol;
1191 op = read_1_token(fp, &eol);
1192 if (op && !eol) {
1193 read_1_row(rs, fp, op);
1194 }
1195 free(op);
1196 }
1197 rs->last_update = ftell(fp);
1198 fclose(fp);
1199 }
1200
1201 static void
write_string(FILE * fp,const char * str)1202 write_string(FILE* fp, const char* str)
1203 {
1204 fprintf(fp, "%s", str);
1205 }
1206
1207 /* ダブルクオートもしくはバックスラッシュにバックスラッシュを付ける */
1208 static void
write_quote_string(FILE * fp,const char * str)1209 write_quote_string(FILE* fp, const char* str)
1210 {
1211 const char* p;
1212
1213 for (p = str; *p; p++) {
1214 if (*p == '\"' || *p == '\\') {
1215 fputc('\\', fp);
1216 }
1217 fputc(*p, fp);
1218 }
1219 }
1220
1221 static void
write_quote_xstr(FILE * fp,xstr * xs,int encoding)1222 write_quote_xstr(FILE* fp, xstr* xs, int encoding)
1223 {
1224 char* buf;
1225
1226 if ((NULL == xs) || (NULL == xs->str) || (xs->len < 1) || (0 == xs->str[0])) {
1227 /* 辞書もしくは学習データが壊れていた時の対策 */
1228 return;
1229 }
1230
1231 buf = (char*) alloca(xs->len * 6 + 2); /* EUC またはUTF8を仮定 */
1232 anthy_sputxstr(buf, xs, encoding);
1233 write_quote_string(fp, buf);
1234 }
1235
1236 static void
write_number(FILE * fp,int x)1237 write_number(FILE* fp, int x)
1238 {
1239 fprintf(fp, "%d", x);
1240 }
1241
1242 /* journalに1行追記する */
1243 static void
commit_add_row(struct record_stat * rst,const char * sname,struct trie_node * node)1244 commit_add_row(struct record_stat* rst,
1245 const char* sname, struct trie_node* node)
1246 {
1247 FILE* fp;
1248 int i;
1249
1250 if (rst->is_anon)
1251 return ;
1252
1253 fp = fopen(rst->journal_fn, "a");
1254 if (fp == NULL) {
1255 return;
1256 }
1257
1258 write_string(fp, "ADD \"");
1259 write_quote_string(fp, sname);
1260 write_string(fp, "\" S\"");
1261 write_quote_xstr(fp, &node->row.key, rst->encoding);
1262 write_string(fp, "\"");
1263
1264 for (i = 0; i < node->row.nr_vals; i++) {
1265 switch (node->row.vals[i].type) {
1266 case RT_EMPTY:
1267 write_string(fp, " E");
1268 break;
1269 case RT_VAL:
1270 write_string(fp, " N");
1271 write_number(fp, node->row.vals[i].u.val);
1272 break;
1273 case RT_XSTR:
1274 write_string(fp, " S\"");
1275 write_quote_xstr(fp, &node->row.vals[i].u.str, rst->encoding);
1276 write_string(fp, "\"");
1277 break;
1278 case RT_XSTRP:
1279 write_string(fp, " S\"");
1280 write_quote_xstr(fp, node->row.vals[i].u.strp, rst->encoding);
1281 write_string(fp, "\"");
1282 break;
1283 }
1284 }
1285 write_string(fp, "\n");
1286 rst->last_update = ftell(fp);
1287 fclose(fp);
1288 }
1289
1290 /* 全ての row を解放する */
1291 static void
clear_record(struct record_stat * rst)1292 clear_record(struct record_stat* rst)
1293 {
1294 struct record_section *rsc;
1295 for (rsc = rst->section_list.next; rsc; rsc = rsc->next) {
1296 trie_remove_all(&rsc->cols, &rsc->lru_nr_used, &rsc->lru_nr_sused);
1297 }
1298 rst->cur_row = NULL;
1299 }
1300
1301 /* 基本ファイルを読む */
1302 static void
read_session(struct record_stat * rst)1303 read_session(struct record_stat *rst)
1304 {
1305 char **tokens;
1306 int nr;
1307 struct record_section* rsc = NULL;
1308 while (!anthy_read_line(&tokens, &nr)) {
1309 xstr *xs;
1310 int i;
1311 int dirty = 0;
1312 struct trie_node* node;
1313
1314 if (!strcmp(tokens[0], "---") && nr > 1) {
1315 /* セクションの切れ目 */
1316 rsc = do_select_section(rst, tokens[1], 1);
1317 goto end;
1318 }
1319 if (!rsc || nr < 2) {
1320 /* セクションが始まってない or 行が不完全 */
1321 goto end;
1322 }
1323 /* 行頭のLRUのマークを読む */
1324 if (tokens[0][0] == '-') {
1325 dirty = 0;
1326 } else if (tokens[0][0] == '+') {
1327 dirty = LRU_SUSED;
1328 }
1329 /* 次にindex */
1330 xs = anthy_cstr_to_xstr(&tokens[0][1], rst->encoding);
1331 node = do_select_row(rsc, xs, 1, dirty);
1332 anthy_free_xstr(xs);
1333 if (!node) {
1334 goto end;
1335 }
1336 /**/
1337 for (i = 1; i < nr; i++) {
1338 if (tokens[i][0] == '"') {
1339 char *str;
1340 str = strdup(&tokens[i][1]);
1341 str[strlen(str) - 1] = 0;
1342 xs = anthy_cstr_to_xstr(str, rst->encoding);
1343 free(str);
1344 do_set_nth_xstr(node, i-1, xs, &rst->xstrs);
1345 anthy_free_xstr(xs);
1346 }else if (tokens[i][0] == '*') {
1347 /* EMPTY entry */
1348 get_nth_val_ent(node, i-1, 1);
1349 } else {
1350 do_set_nth_value(node, i-1, atoi(tokens[i]));
1351 }
1352 }
1353 end:
1354 anthy_free_line();
1355 }
1356 }
1357
1358 /* いまのデータベースを解放した後にファイルから読み込む */
1359 static void
read_base_record(struct record_stat * rst)1360 read_base_record(struct record_stat *rst)
1361 {
1362 struct stat st;
1363 if (rst->is_anon) {
1364 clear_record(rst);
1365 return ;
1366 }
1367 anthy_check_user_dir();
1368
1369 if (anthy_open_file(rst->base_fn) == -1) {
1370 return ;
1371 }
1372
1373 clear_record(rst);
1374 read_session(rst);
1375 anthy_close_file();
1376 if (stat(rst->base_fn, &st) == 0) {
1377 rst->base_timestamp = st.st_mtime;
1378 }
1379 rst->last_update = 0;
1380 }
1381
1382 static FILE *
open_tmp_in_recorddir(void)1383 open_tmp_in_recorddir(void)
1384 {
1385 char *pn;
1386 const char *hd;
1387 const char *sid;
1388 sid = anthy_conf_get_str("SESSION-ID");
1389 hd = anthy_conf_get_str("HOME");
1390 pn = alloca(strlen(hd)+strlen(sid) + 10);
1391 sprintf(pn, "%s/.anthy/%s", hd, sid);
1392 return fopen(pn, "w");
1393 }
1394
1395 /*
1396 * 一時ファイルからbaseファイルへrenameする
1397 */
1398 static void
update_file(const char * fn)1399 update_file(const char *fn)
1400 {
1401 const char *hd;
1402 char *tmp_fn;
1403 const char *sid;
1404 hd = anthy_conf_get_str("HOME");
1405 sid = anthy_conf_get_str("SESSION-ID");
1406 tmp_fn = alloca(strlen(hd)+strlen(sid) + 10);
1407
1408 sprintf(tmp_fn, "%s/.anthy/%s", hd, sid);
1409 if (rename(tmp_fn, fn)){
1410 anthy_log(0, "Failed to update record file %s -> %s.\n", tmp_fn, fn);
1411 }
1412 }
1413
1414 /* カラムを保存する */
1415 static void
save_a_row(FILE * fp,struct record_stat * rst,struct record_row * c,int dirty)1416 save_a_row(FILE *fp, struct record_stat* rst,
1417 struct record_row *c, int dirty)
1418 {
1419 int i;
1420 char *buf = alloca(c->key.len * 6 + 2);
1421 /* LRUのマークを出力 */
1422 if (dirty == 0) {
1423 fputc('-', fp);
1424 } else {
1425 fputc('+', fp);
1426 }
1427 anthy_sputxstr(buf, &c->key, rst->encoding);
1428 /* index を出力 */
1429 fprintf(fp, "%s ", buf);
1430 /**/
1431 for (i = 0; i < c->nr_vals; i++) {
1432 struct record_val *val = &c->vals[i];
1433 switch (val->type) {
1434 case RT_EMPTY:
1435 fprintf(fp, "* ");
1436 break;
1437 case RT_XSTR:
1438 /* should not happen */
1439 fprintf(fp, "\"");
1440 write_quote_xstr(fp, &val->u.str, rst->encoding);
1441 fprintf(fp, "\" ");
1442 abort();
1443 break;
1444 case RT_XSTRP:
1445 fprintf(fp, "\"");
1446 write_quote_xstr(fp, val->u.strp, rst->encoding);
1447 fprintf(fp, "\" ");
1448 break;
1449 case RT_VAL:
1450 fprintf(fp, "%d ", val->u.val);
1451 break;
1452 default:
1453 anthy_log(0, "Faild to save an unknown record. (in record.c)\n");
1454 break;
1455 }
1456 }
1457 fprintf(fp, "\n");
1458 }
1459
1460 static void
update_base_record(struct record_stat * rst)1461 update_base_record(struct record_stat* rst)
1462 {
1463 struct record_section *sec;
1464 struct trie_node *col;
1465 FILE *fp;
1466 struct stat st;
1467
1468 /* 一時ファイルを作ってrecordを書き出す */
1469 anthy_check_user_dir();
1470 fp = open_tmp_in_recorddir();
1471 if (!fp) {
1472 anthy_log(0, "Failed to open temporaly session file.\n");
1473 return ;
1474 }
1475 /* 各セクションに対して */
1476 for (sec = rst->section_list.next;
1477 sec; sec = sec->next) {
1478 if (!trie_first(&sec->cols)) {
1479 /*このセクションは空*/
1480 continue;
1481 }
1482 /* セクション境界の文字列 */
1483 fprintf(fp, "--- %s\n", sec->name);
1484 /* 各カラムを保存する */
1485 for (col = trie_first(&sec->cols); col;
1486 col = trie_next(&sec->cols, col)) {
1487 save_a_row(fp, rst, &col->row, col->dirty);
1488 }
1489 }
1490 fclose(fp);
1491
1492 /* 本来の名前にrenameする */
1493 update_file(rst->base_fn);
1494
1495 if (stat(rst->base_fn, &st) == 0) {
1496 rst->base_timestamp = st.st_mtime;
1497 }
1498 /* journalファイルを消す */
1499 unlink(rst->journal_fn);
1500 rst->last_update = 0;
1501 }
1502
1503 static void
commit_del_row(struct record_stat * rst,const char * sname,struct trie_node * node)1504 commit_del_row(struct record_stat* rst,
1505 const char* sname, struct trie_node* node)
1506 {
1507 FILE* fp;
1508
1509 fp = fopen(rst->journal_fn, "a");
1510 if (fp == NULL) {
1511 return;
1512 }
1513 write_string(fp, "DEL \"");
1514 write_quote_string(fp, sname);
1515 write_string(fp, "\" S\"");
1516 write_quote_xstr(fp, &node->row.key, rst->encoding);
1517 write_string(fp, "\"");
1518 write_string(fp, "\n");
1519 fclose(fp);
1520 }
1521
1522 /*
1523 * sync_add: ADD の書き込み
1524 * sync_del_and_del: DEL の書き込みと削除
1525 * どちらも書き込みの前に、他のプロセスによってディスク上に保存された
1526 * 更新をメモリ上に読み込む。
1527 * このとき、データベースをフラッシュする可能性もある。データベースの
1528 * フラッシュがあると、 cur_row と全ての xstr は無効になる。
1529 * ただし、 cur_section の有効性は保存される。
1530 */
1531 static void
sync_add(struct record_stat * rst,struct record_section * rsc,struct trie_node * node)1532 sync_add(struct record_stat* rst, struct record_section* rsc,
1533 struct trie_node* node)
1534 {
1535 lock_record(rst);
1536 if (!check_base_record_uptodate(rst)) {
1537 node->dirty |= PROTECT;
1538 /* 差分ファイルだけ読む */
1539 read_journal_record(rst);
1540 node->dirty &= ~PROTECT;
1541 commit_add_row(rst, rsc->name, node);
1542 } else {
1543 /* 再読み込み */
1544 commit_add_row(rst, rsc->name, node);
1545 read_base_record(rst);
1546 read_journal_record(rst);
1547 }
1548 if (rst->last_update > FILE2_LIMIT) {
1549 update_base_record(rst);
1550 }
1551 unlock_record(rst);
1552 }
1553
1554 static void
sync_del_and_del(struct record_stat * rst,struct record_section * rsc,struct trie_node * node)1555 sync_del_and_del(struct record_stat* rst, struct record_section* rsc,
1556 struct trie_node* node)
1557 {
1558 lock_record(rst);
1559 commit_del_row(rst, rsc->name, node);
1560 if (!check_base_record_uptodate(rst)) {
1561 read_base_record(rst);
1562 }
1563 read_journal_record(rst);
1564 if (rst->last_update > FILE2_LIMIT) {
1565 update_base_record(rst);
1566 }
1567 unlock_record(rst);
1568 }
1569
1570
1571 /*
1572 * prediction関係
1573 */
1574
1575 static int
read_prediction_node(struct trie_node * n,struct prediction_t * predictions,int index)1576 read_prediction_node(struct trie_node *n, struct prediction_t* predictions, int index)
1577 {
1578 int i;
1579 int nr_predictions = do_get_nr_values(n);
1580 for (i = 0; i < nr_predictions; i += 2) {
1581 time_t t = do_get_nth_value(n, i);
1582 xstr* xs = do_get_nth_xstr(n, i + 1);
1583 if (t && xs) {
1584 if (predictions) {
1585 predictions[index].timestamp = t;
1586 predictions[index].src_str = anthy_xstr_dup(&n->row.key);
1587 predictions[index].str = anthy_xstr_dup(xs);
1588 }
1589 ++index;
1590 }
1591 }
1592 return index;
1593 }
1594
1595
1596 /*
1597 * trie中をたどり、prefixがマッチしたらread_prediction_nodeを
1598 * 呼んでpredictionsの配列に結果を追加する。
1599 */
1600 static int
traverse_record_for_prediction(xstr * key,struct trie_node * n,struct prediction_t * predictions,int index)1601 traverse_record_for_prediction(xstr* key, struct trie_node *n,
1602 struct prediction_t* predictions, int index)
1603 {
1604 if (n->l->bit > n->bit) {
1605 index = traverse_record_for_prediction(key, n->l, predictions, index);
1606 } else {
1607 if (n->l->row.key.len != -1) {
1608 if (anthy_xstrncmp(&n->l->row.key, key, key->len) == 0) {
1609 index = read_prediction_node(n->l, predictions, index);
1610 }
1611 }
1612 }
1613 if (n->r->bit > n->bit) {
1614 index = traverse_record_for_prediction(key, n->r, predictions, index);
1615 } else {
1616 if (n->r->row.key.len != -1) {
1617 if (anthy_xstrncmp(&n->r->row.key, key, key->len) == 0) {
1618 index = read_prediction_node(n->r, predictions, index);
1619 }
1620 }
1621 }
1622 return index;
1623 }
1624
1625 /*
1626 * key で探索
1627 * key の文字列長を越えるか、ノードが無くなったら探索打ち切り
1628 * trieのkeyが格納されているところでななくて葉を返す
1629 */
1630 static struct trie_node *
trie_find_for_prediction(struct trie_root * root,xstr * key)1631 trie_find_for_prediction (struct trie_root* root, xstr *key)
1632 {
1633 struct trie_node *p;
1634 struct trie_node *q;
1635
1636 p = &root->root;
1637 q = p->l;
1638
1639 while (p->bit < q->bit) {
1640 if (q->bit >= 2) {
1641 if ((q->bit - 2) / (int)(sizeof(xchar) << 3) >= key->len) {
1642 break;
1643 }
1644 }
1645 p = q;
1646 q = trie_key_nth_bit(key, p->bit) ? p->r : p->l;
1647 }
1648 return p;
1649 }
1650
1651 static int
prediction_cmp(const void * lhs,const void * rhs)1652 prediction_cmp(const void* lhs, const void* rhs)
1653 {
1654 struct prediction_t *lpre = (struct prediction_t*)lhs;
1655 struct prediction_t *rpre = (struct prediction_t*)rhs;
1656 return rpre->timestamp - lpre->timestamp;
1657 }
1658
1659 int
anthy_traverse_record_for_prediction(xstr * key,struct prediction_t * predictions)1660 anthy_traverse_record_for_prediction(xstr* key, struct prediction_t* predictions)
1661 {
1662 struct trie_node* mark;
1663 int nr_predictions;
1664 if (anthy_select_section("PREDICTION", 0)) {
1665 return 0;
1666 }
1667
1668 /* 指定された文字列をprefixに持つnodeを探す */
1669 mark = trie_find_for_prediction(&anthy_current_record->cur_section->cols, key);
1670 if (!mark) {
1671 return 0;
1672 }
1673 nr_predictions = traverse_record_for_prediction(key, mark, predictions, 0);
1674 if (predictions) {
1675 /* タイムスタンプで予測候補をソートする */
1676 qsort(predictions, nr_predictions, sizeof(struct prediction_t), prediction_cmp);
1677 }
1678 return nr_predictions;
1679 }
1680
1681 /* Wrappers begin.. */
1682 int
anthy_select_section(const char * name,int flag)1683 anthy_select_section(const char *name, int flag)
1684 {
1685 struct record_stat* rst;
1686 struct record_section* rsc;
1687
1688 rst = anthy_current_record;
1689 if (rst->row_dirty && rst->cur_section && rst->cur_row) {
1690 sync_add(rst, rst->cur_section, rst->cur_row);
1691 }
1692 rst->cur_row = NULL;
1693 rst->row_dirty = 0;
1694 rsc = do_select_section(rst, name, flag);
1695 if (!rsc) {
1696 return -1;
1697 }
1698 rst->cur_section = rsc;
1699 return 0;
1700 }
1701
1702 int
anthy_select_row(xstr * name,int flag)1703 anthy_select_row(xstr *name, int flag)
1704 {
1705 struct record_stat* rst;
1706 struct trie_node* node;
1707
1708 rst = anthy_current_record;
1709 if (!rst->cur_section) {
1710 return -1;
1711 }
1712 if (rst->row_dirty && rst->cur_row) {
1713 sync_add(rst, rst->cur_section, rst->cur_row);
1714 rst->row_dirty = 0;
1715 }
1716 node = do_select_row(rst->cur_section, name, flag, LRU_USED);
1717 if (!node) {
1718 return -1;
1719 }
1720 rst->cur_row = node;
1721 rst->row_dirty = flag;
1722 return 0;
1723 }
1724
1725 int
anthy_select_longest_row(xstr * name)1726 anthy_select_longest_row(xstr *name)
1727 {
1728 struct record_stat* rst;
1729 struct trie_node* node;
1730
1731 rst = anthy_current_record;
1732 if (!rst->cur_section)
1733 return -1;
1734
1735 if (rst->row_dirty && rst->cur_row) {
1736 sync_add(rst, rst->cur_section, rst->cur_row);
1737 rst->row_dirty = 0;
1738 }
1739 node = do_select_longest_row(rst->cur_section, name);
1740 if (!node) {
1741 return -1;
1742 }
1743
1744 rst->cur_row = node;
1745 rst->row_dirty = 0;
1746 return 0;
1747 }
1748
1749 void
anthy_truncate_section(int count)1750 anthy_truncate_section(int count)
1751 {
1752 do_truncate_section(anthy_current_record, count);
1753 }
1754
1755 void
anthy_truncate_row(int nth)1756 anthy_truncate_row(int nth)
1757 {
1758 struct trie_node *cur_row = anthy_current_record->cur_row;
1759 if (!cur_row) {
1760 return ;
1761 }
1762 do_truncate_row(cur_row, nth);
1763
1764 }
1765
1766 int
anthy_mark_row_used(void)1767 anthy_mark_row_used(void)
1768 {
1769 struct record_stat* rst = anthy_current_record;
1770 if (!rst->cur_row) {
1771 return -1;
1772 }
1773
1774 do_mark_row_used(rst->cur_section, rst->cur_row);
1775 sync_add(rst, rst->cur_section, rst->cur_row);
1776 rst->row_dirty = 0;
1777 return 0;
1778 }
1779
1780 void
anthy_set_nth_value(int nth,int val)1781 anthy_set_nth_value(int nth, int val)
1782 {
1783 struct record_stat* rst;
1784
1785 rst = anthy_current_record;
1786 if (!rst->cur_row) {
1787 return;
1788 }
1789 do_set_nth_value(rst->cur_row, nth, val);
1790 rst->row_dirty = 1;
1791 }
1792
1793 void
anthy_set_nth_xstr(int nth,xstr * xs)1794 anthy_set_nth_xstr(int nth, xstr *xs)
1795 {
1796 struct record_stat* rst = anthy_current_record;
1797 if (!rst->cur_row) {
1798 return;
1799 }
1800 do_set_nth_xstr(rst->cur_row, nth, xs, &rst->xstrs);
1801 rst->row_dirty = 1;
1802 }
1803
1804 int
anthy_get_nr_values(void)1805 anthy_get_nr_values(void)
1806 {
1807 return do_get_nr_values(anthy_current_record->cur_row);
1808 }
1809
1810 int
anthy_get_nth_value(int n)1811 anthy_get_nth_value(int n)
1812 {
1813 return do_get_nth_value(anthy_current_record->cur_row, n);
1814 }
1815
1816 xstr *
anthy_get_nth_xstr(int n)1817 anthy_get_nth_xstr(int n)
1818 {
1819 return do_get_nth_xstr(anthy_current_record->cur_row, n);
1820 }
1821
1822 int
anthy_select_first_row(void)1823 anthy_select_first_row(void)
1824 {
1825 struct record_stat* rst;
1826 struct trie_node* node;
1827
1828 rst = anthy_current_record;
1829 if (!rst->cur_section)
1830 return -1;
1831
1832 if (rst->row_dirty && rst->cur_row) {
1833 sync_add(rst, rst->cur_section, rst->cur_row);
1834 rst->row_dirty = 0;
1835 }
1836 node = do_select_first_row(rst->cur_section);
1837 if (!node) {
1838 return -1;
1839 }
1840 rst->cur_row = node;
1841 rst->row_dirty = 0;
1842 return 0;
1843 }
1844
1845 int
anthy_select_next_row(void)1846 anthy_select_next_row(void)
1847 {
1848 struct record_stat* rst;
1849 struct trie_node* node;
1850
1851 rst = anthy_current_record;
1852 if (!rst->cur_section || !rst->cur_row)
1853 return -1;
1854
1855 /* sync_add() で cur_row が無効になることがあるので、
1856 * たとえ row_dirty でも sync_add() しない
1857 */
1858 rst->row_dirty = 0;
1859 node = do_select_next_row(rst->cur_section, rst->cur_row);
1860 if (!node)
1861 return -1;
1862 rst->cur_row = node;
1863 rst->row_dirty = 0;
1864 return 0;
1865 }
1866
1867 xstr *
anthy_get_index_xstr(void)1868 anthy_get_index_xstr(void)
1869 {
1870 return do_get_index_xstr(anthy_current_record);
1871 }
1872 /*..Wrappers end*/
1873
1874 /*
1875 * trie_row_init は何回よんでもいい
1876 */
1877 static void
trie_row_init(struct record_row * rc)1878 trie_row_init(struct record_row* rc)
1879 {
1880 rc->nr_vals = 0;
1881 rc->vals = NULL;
1882 }
1883
1884 static void
trie_row_free(struct record_row * rc)1885 trie_row_free(struct record_row *rc)
1886 {
1887 int i;
1888 for (i = 0; i < rc->nr_vals; i++)
1889 free_val_contents(rc->vals + i);
1890 free(rc->vals);
1891 free(rc->key.str);
1892 }
1893
1894 /* あるセクションのデータを全て解放する */
1895 static void
free_section(struct record_stat * r,struct record_section * rs)1896 free_section(struct record_stat *r, struct record_section *rs)
1897 {
1898 struct record_section *s;
1899 trie_remove_all(&rs->cols, &rs->lru_nr_used, &rs->lru_nr_sused);
1900 if (r->cur_section == rs) {
1901 r->cur_row = 0;
1902 r->cur_section = 0;
1903 }
1904 for (s = &r->section_list; s && s->next; s = s->next) {
1905 if (s->next == rs) {
1906 s->next = s->next->next;
1907 }
1908 }
1909 if (rs->name){
1910 free((void *)rs->name);
1911 }
1912 free(rs);
1913 }
1914
1915 /* すべてのデータを解放する */
1916 static void
free_record(struct record_stat * rst)1917 free_record(struct record_stat *rst)
1918 {
1919 struct record_section *rsc;
1920 for (rsc = rst->section_list.next; rsc; ){
1921 struct record_section *tmp;
1922 tmp = rsc;
1923 rsc = rsc->next;
1924 free_section(rst, tmp);
1925 }
1926 rst->section_list.next = NULL;
1927 }
1928
1929 void
anthy_release_section(void)1930 anthy_release_section(void)
1931 {
1932 struct record_stat* rst;
1933
1934 rst = anthy_current_record;
1935 if (!rst->cur_section) {
1936 return ;
1937 }
1938 free_section(rst, rst->cur_section);
1939 rst->cur_section = 0;
1940 }
1941
1942 void
anthy_release_row(void)1943 anthy_release_row(void)
1944 {
1945 struct record_stat* rst;
1946
1947 rst = anthy_current_record;
1948 if (!rst->cur_section || !rst->cur_row) {
1949 return;
1950 }
1951
1952 rst->row_dirty = 0;
1953 /* sync_del_and_del で削除もする */
1954 sync_del_and_del(rst, rst->cur_section, rst->cur_row);
1955 rst->cur_row = NULL;
1956 }
1957
1958 static void
check_record_encoding(struct record_stat * rst)1959 check_record_encoding(struct record_stat *rst)
1960 {
1961 FILE *fp;
1962 if (anthy_open_file(rst->base_fn) == 0) {
1963 /* EUCの履歴ファイルがあった */
1964 anthy_close_file();
1965 return ;
1966 }
1967 fp = fopen(rst->journal_fn, "r");
1968 if (fp) {
1969 /* EUCの差分ファイルがあった */
1970 fclose(fp);
1971 return ;
1972 }
1973 rst->encoding = ANTHY_UTF8_ENCODING;
1974 strcat(rst->base_fn, ENCODING_SUFFIX);
1975 strcat(rst->journal_fn, ENCODING_SUFFIX);
1976 }
1977
1978 static void
record_dtor(void * p)1979 record_dtor(void *p)
1980 {
1981 int dummy;
1982 struct record_stat *rst = (struct record_stat*) p;
1983 free_record(rst);
1984 if (rst->id) {
1985 free(rst->base_fn);
1986 free(rst->journal_fn);
1987 }
1988 trie_remove_all(&rst->xstrs, &dummy, &dummy);
1989 }
1990
1991 void
anthy_reload_record(void)1992 anthy_reload_record(void)
1993 {
1994 struct stat st;
1995 struct record_stat *rst = anthy_current_record;
1996
1997 if (stat(rst->journal_fn, &st) == 0 &&
1998 rst->journal_timestamp == st.st_mtime) {
1999 return ;
2000 }
2001
2002 lock_record(rst);
2003 read_base_record(rst);
2004 read_journal_record(rst);
2005 unlock_record(rst);
2006 }
2007
2008 void
anthy_init_record(void)2009 anthy_init_record(void)
2010 {
2011 record_ator = anthy_create_allocator(sizeof(struct record_stat),
2012 record_dtor);
2013 }
2014
2015 static void
setup_filenames(const char * id,struct record_stat * rst)2016 setup_filenames(const char *id, struct record_stat *rst)
2017 {
2018 const char *home = anthy_conf_get_str("HOME");
2019 int base_len = strlen(home) + strlen(id) + 10;
2020
2021 /* 基本ファイル */
2022 rst->base_fn = (char*) malloc(base_len +
2023 strlen("/.anthy/last-record1_"));
2024 sprintf(rst->base_fn, "%s/.anthy/last-record1_%s",
2025 home, id);
2026 /* 差分ファイル */
2027 rst->journal_fn = (char*) malloc(base_len +
2028 strlen("/.anthy/last-record2_"));
2029 sprintf(rst->journal_fn, "%s/.anthy/last-record2_%s",
2030 home, id);
2031 }
2032
2033 struct record_stat *
anthy_create_record(const char * id)2034 anthy_create_record(const char *id)
2035 {
2036 struct record_stat *rst;
2037
2038 if (!id) {
2039 return NULL;
2040 }
2041
2042 rst = anthy_smalloc(record_ator);
2043 rst->id = id;
2044 rst->section_list.next = 0;
2045 init_trie_root(&rst->xstrs);
2046 rst->cur_section = 0;
2047 rst->cur_row = 0;
2048 rst->row_dirty = 0;
2049 rst->encoding = ANTHY_EUC_JP_ENCODING;
2050
2051 /* ファイル名の文字列を作る */
2052 setup_filenames(id, rst);
2053
2054 rst->last_update = 0;
2055
2056 if (!strcmp(id, ANON_ID)) {
2057 rst->is_anon = 1;
2058 } else {
2059 rst->is_anon = 0;
2060 anthy_check_user_dir();
2061 }
2062
2063 /* ファイルから読み込む */
2064 lock_record(rst);
2065 check_record_encoding(rst);
2066 read_base_record(rst);
2067 read_journal_record(rst);
2068 unlock_record(rst);
2069
2070 return rst;
2071 }
2072
2073 void
anthy_release_record(struct record_stat * rs)2074 anthy_release_record(struct record_stat *rs)
2075 {
2076 anthy_sfree(record_ator, rs);
2077 }
2078