Update license in the spec file
[platform/core/uifw/anthy.git] / mkworddic / mkdic.c
1 /*
2  * cannadic·Á¼°¤Î¥Õ¥¡¥¤¥ë¤«¤é¼­½ñ¥Õ¥¡¥¤¥ë¤òºî¤ë
3  *
4  * Funded by IPA̤Ƨ¥½¥Õ¥È¥¦¥§¥¢ÁϤ»ö¶È 2002 1/1
5  *
6  * Copyright (C) 2000-2007 TABATA Yusuke
7  * Copyright (C) 2005 YOSHIDA Yuichi
8  * Copyright (C) 2001-2002 TAKAI Kousuke
9  */
10 /*
11  * ¼­½ñ¤ÏÆɤߤòindex¤È¤·¡¢ÉÊ»ì¤äÊÑ´¹¸å¤Îñ¸ì(=entry)¤ò¸¡º÷
12  * ¤¹¤ë¹½Â¤¤Ë¤Ê¤Ã¤Æ¤¤¤ë¡£
13  *
14  * Æɤߠ-> Ã±¸ì¡¢Ã±¸ì¡¢¡¢
15  *
16  * ¼­½ñ¥Õ¥¡¥¤¥ë¤Ï¥Í¥Ã¥È¥ï¡¼¥¯¥Ð¥¤¥È¥ª¡¼¥À¡¼¤òÍѤ¤¤ë¡£
17  *
18  * ¼­½ñ¥Õ¥¡¥¤¥ë¤ÏÊ£¿ô¤Î¥»¥¯¥·¥ç¥ó¤«¤é¹½À®¤µ¤ì¤Æ¤¤¤ë
19  *  0 ¥Ø¥Ã¥À 16*4 bytes
20  *  2 ÆɤߤΥ¤¥ó¥Ç¥Ã¥¯¥¹ (Æɤß512¸Ä¤´¤È)
21  *  3 Æɤß
22  *  4 ¥Ú¡¼¥¸
23  *  5 ¥Ú¡¼¥¸¤Î¥¤¥ó¥Ç¥Ã¥¯¥¹
24  *  6 ÍÑÎã¼­½ñ(?)
25  *  7 Æɤߠhash
26  *
27  * source ¸µ¤Î¼­½ñ¥Õ¥¡¥¤¥ë
28  * file_dic À¸À®¤¹¤ë¥Õ¥¡¥¤¥ë
29  *
30  * yomi_hash ¼­½ñ¥Õ¥¡¥¤¥ë¤Ë½ÐÎϤµ¤ì¤ëhash¤Îbitmap
31  * index_hash ¤³¤Î¥½¡¼¥¹Ãæ¤Çstruct yomi_entry¤ò¸¡º÷¤¹¤ë¤¿¤á¤Îhash
32  *
33  */
34
35 #include <sys/types.h>
36 #include <unistd.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <ctype.h>
42
43 #include <config.h>
44
45 #include <anthy/anthy.h>
46 #include <anthy/xstr.h>
47 #include <anthy/wtype.h>
48 #include <anthy/ruleparser.h>
49 #include <anthy/word_dic.h>
50 #include <anthy/diclib.h>
51 #include "mkdic.h"
52
53 #define MAX_LINE_LEN 10240
54 #define NR_HEADER_SECTIONS 16
55 #define SECTION_ALIGNMENT 8
56 #define MAX_WTYPE_LEN 20
57
58 #define DEFAULT_FN "anthy.wdic"
59
60 static const char *progname;
61
62 /* writewords.c¤«¤é¥¢¥¯¥»¥¹¤¹¤ë¤¿¤á¤Ë¡¢globalÊÑ¿ô */
63 FILE *yomi_entry_index_out, *yomi_entry_out;
64 FILE *page_out, *page_index_out;
65 /**/
66 static FILE *uc_out;
67 static FILE *yomi_hash_out;
68 /* ¥Ï¥Ã¥·¥å¤Î¾×Æͤοô¡¢Åý·×¾ðÊó */
69 static int yomi_hash_collision;
70
71 /* ¥Õ¥¡¥¤¥ëÃæ¤Î½ç½ø¤Ë½¾¤Ã¤Æʤ٤ë */
72 struct file_section {
73   FILE **fpp;
74   char *fn;
75 } file_array[] = {
76   {&yomi_entry_index_out, NULL},
77   {&yomi_entry_out, NULL},
78   {&page_out, NULL},
79   {&page_index_out, NULL},
80   {&uc_out, NULL},
81   {&yomi_hash_out, NULL},
82   {NULL, NULL},
83 };
84
85 /* ¼­½ñÀ¸À®¤Î¾õÂÖ */
86 struct mkdic_stat {
87   /* Ã±¸ì¤Î¥ê¥¹¥È */
88   struct yomi_entry_list yl;
89   /**/
90   struct adjust_command ac_list;
91   /* ÍÑÎã¼­½ñ */
92   struct uc_dict *ud;
93   /**/
94   const char *output_fn;
95   /**/
96   int input_encoding;
97   /**/
98   int nr_excluded;
99   char **excluded_wtypes;
100 };
101
102 /* ¼­½ñ¤Î½ÐÎÏÀè¤Î¥Õ¥¡¥¤¥ë¤ò¥ª¡¼¥×¥ó¤¹¤ë */
103 static void
104 open_output_files(void)
105 {
106   struct file_section *fs;
107   for (fs = file_array; fs->fpp; fs ++) {
108     char *tmpdir = getenv("TMPDIR");
109     fs->fn = NULL;
110     if (tmpdir) {
111       /* tmpfile()¤¬TMPDIR¤ò¸«¤Ê¤¤¤¿¤á¡¢TMPDIR¤ò»ØÄꤵ¤ì¤¿¾ì¹çmkstemp¤ò»È¤¦¡£*/
112       char buf[256];
113       int fd = -1;
114       snprintf(buf, sizeof(buf), "%s/mkanthydic.XXXXXX", tmpdir);
115       fd = mkstemp(buf);
116       if (fd == -1) {
117         *(fs->fpp) = NULL;
118       } else {
119         *(fs->fpp) = fdopen(fd, "w+");
120         fs->fn = strdup(buf);
121       }
122     } else {
123       *(fs->fpp) = tmpfile();
124     }
125     /**/
126     if (!(*(fs->fpp))) {
127       fprintf (stderr, "%s: cannot open temporary file: %s\n",
128                progname, strerror (errno));
129       exit (2);
130     }
131   }
132 }
133
134 /* fflush¤¹¤ë */
135 static void
136 flush_output_files (void)
137 {
138   struct file_section *fs;
139   for (fs = file_array; fs->fpp; fs ++) {
140     if (ferror(*(fs->fpp))) {
141       fprintf (stderr, "%s: write error\n", progname);
142       exit (1);
143     }
144   }
145   for (fs = file_array; fs->fpp; fs ++) {
146     if (fflush(*(fs->fpp))) {
147       fprintf (stderr, "%s: write error: %s\n", progname, strerror (errno));
148       exit (1);
149     }
150   }
151 }
152
153 /* ¥Í¥Ã¥È¥ï¡¼¥¯byteorder¤Ç4bytes½ñ¤­½Ð¤¹ */
154 void
155 write_nl(FILE *fp, int i)
156 {
157   i = anthy_dic_htonl(i);
158   fwrite(&i, sizeof(int), 1, fp);
159 }
160
161 static void
162 print_usage(void)
163 {
164   printf("please do not use mkanthydic command directly.\n");
165   exit(0);
166 }
167
168 static char *
169 read_line(FILE *fp, char *buf)
170 {
171   /* Ä¹¤¹¤®¤ë¹Ô¤ò̵»ë¤¹¤ë */
172   int toolong = 0;
173
174   while (fgets(buf, MAX_LINE_LEN, fp)) {
175     int len = strlen(buf);
176     if (buf[0] == '#') {
177       continue ;
178     }
179     if (buf[len - 1] != '\n') {
180       toolong = 1;
181       continue ;
182     }
183
184     buf[len - 1] = 0;
185     if (toolong) {
186       toolong = 0;
187     } else {
188       return buf;
189     }
190   }
191   return NULL;
192 }
193
194 /** cannadic·Á¼°¤Î¼­½ñ¤Î¹Ô¤«¤éindex¤È¤Ê¤ëÉôʬ¤ò¼è¤ê½Ð¤¹ */
195 static xstr *
196 get_index_from_line(struct mkdic_stat *mds, char *buf)
197 {
198   char *sp;
199   xstr *xs;
200   sp = strchr(buf, ' ');
201   if (!sp) {
202     /* ¼­½ñ¤Î¥Õ¥©¡¼¥Þ¥Ã¥È¤¬¤ª¤«¤·¤¤ */
203     return NULL;
204   }
205   *sp = 0;
206   xs = anthy_cstr_to_xstr(buf, mds->input_encoding);
207   *sp = ' ';
208   return xs;
209 }
210
211 /** cannadic·Á¼°¤Î¼­½ñ¤Î¹Ô¤«¤éindex°Ê³°¤ÎÉôʬ¤ò¼è¤ê½Ð¤¹ */
212 static char *
213 get_entry_from_line(char *buf)
214 {
215   char *sp;
216   sp = strchr(buf, ' ');
217   while(*sp == ' ') {
218     sp ++;
219   }
220   return strdup(sp);
221 }
222
223 static int
224 index_hash(xstr *xs)
225 {
226   int i;
227   unsigned int h = 0;
228   for (i = 0; i < xs->len; i++) {
229     h += xs->str[i] * 11;
230   }
231   return (int)(h % YOMI_HASH);
232 }
233
234 const char *
235 get_wt_name(const char *name)
236 {
237   wtype_t dummy;
238   const char *res;
239   if (!strcmp(name, "#T35")) {
240     return "#T";
241   }
242   res = anthy_type_to_wtype(name, &dummy);
243   if (!res) {
244     return "unknown";
245   }
246   return res;
247 }
248
249 /** ÆɤߤËÂФ·¤Æ¡¢Ã±¸ì¤ò°ì¤Ä¤òÄɲ乤ë */
250 static void
251 push_back_word_entry(struct mkdic_stat *mds,
252                      struct yomi_entry *ye, const char *wt_name,
253                      int freq, const char *word, int order)
254 {
255   wtype_t wt;
256   char *s;
257   if (freq == 0) {
258     return ;
259   }
260   if (!anthy_type_to_wtype(wt_name, &wt)) {
261     /* anthy¤ÎÃΤé¤Ê¤¤ÉÊ»ì */
262     return ;
263   }
264   ye->entries = realloc(ye->entries,
265                         sizeof(struct word_entry) *
266                         (ye->nr_entries + 1));
267   ye->entries[ye->nr_entries].ye = ye;
268   ye->entries[ye->nr_entries].wt_name = get_wt_name(wt_name);
269   ye->entries[ye->nr_entries].raw_freq = freq;
270   ye->entries[ye->nr_entries].feature = 0;
271   ye->entries[ye->nr_entries].source_order = order;
272   if (mds->input_encoding == ANTHY_EUC_JP_ENCODING) {
273     s = anthy_conv_euc_to_utf8(word);
274   } else {
275     s = strdup(word);
276   }
277   ye->entries[ye->nr_entries].word_utf8 = s;
278   ye->nr_entries ++;
279 }
280
281 static int
282 parse_wtype(char *wtbuf, char *cur)
283 {
284   /* ÉÊ»ì */
285   char *t;
286   int freq;
287   if (strlen(cur) >= MAX_WTYPE_LEN) {
288     return 0;
289   }
290   strcpy(wtbuf, cur);
291   /* ÉÑÅÙ */
292   t = strchr(wtbuf, '*');
293   freq = 1;
294   if (t) {
295     int tmp_freq;
296     *t = 0;
297     t++;
298     tmp_freq = atoi(t);
299     if (tmp_freq) {
300       freq = tmp_freq;
301     }
302   }
303   return freq;
304 }
305
306 /* Ê£¹ç¸ì¤ÎÍ×ÁǤÎŤµ¤Ï 1,2,3, ... 9,a,b,c */
307 static int
308 get_element_len(xchar xc)
309 {
310   if (xc > '0' && xc <= '9') {
311     return xc - '0';
312   }
313   if (xc >= 'a' && xc <= 'z') {
314     return xc - 'a' + 10;
315   }
316   return 0;
317 }
318
319 /** Ê£¹ç¸õÊä¤Î·Á¼°¥Á¥§¥Ã¥¯ */
320 static int
321 check_compound_candidate(struct mkdic_stat *mds, xstr *index, const char *cur)
322 {
323   /* ÆɤߤÎʸ»ú¿ô¤Î¹ç·×¤ò¿ô¤¨¤ë */
324   xstr *xs = anthy_cstr_to_xstr(cur, mds->input_encoding);
325   int i, total = 0;
326   for (i = 0; i < xs->len - 1; i++) {
327     if (xs->str[i] == '_') {
328       total += get_element_len(xs->str[i+1]);
329     }
330   }
331   anthy_free_xstr(xs);
332   /* Èæ³Ó¤¹¤ë */
333   if (total != index->len) {
334     fprintf(stderr, "Invalid compound candidate (%s, length = %d).\n",
335             cur, total);
336     return 0;
337   }
338   return 1;
339 }
340
341 static int
342 is_excluded_wtype(struct mkdic_stat *mds, char *wt)
343 {
344   int i;
345   for (i = 0; i < mds->nr_excluded; i++) {
346     if (!strcmp(mds->excluded_wtypes[i], wt)) {
347       return 1;
348     }
349   }
350   return 0;
351 }
352
353 static char *
354 find_token_end(char *cur)
355 {
356   char *n;
357   for (n = cur; *n != ' ' && *n != '\0'; n++) {
358     if (*n == '\\') {
359       if (!n[1]) {
360         return NULL;
361       }
362       n++;
363     }
364   }
365   return n;
366 }
367
368 /** ÆɤߤËÂбþ¤¹¤ë¹Ô¤òʬ³ä¤·¤Æ¡¢ÇÛÎó¤ò¹½À®¤¹¤ë */
369 static void
370 push_back_word_entry_line(struct mkdic_stat *mds, struct yomi_entry *ye,
371                           const char *ent)
372 {
373   char *buf = alloca(strlen(ent) + 1);
374   char *cur = buf;
375   char *n;
376   char wtbuf[MAX_WTYPE_LEN];
377   int freq = 0;
378   int order = 0;
379
380   strcpy(buf, ent);
381   wtbuf[0] = 0;
382
383   while (1) {
384     /* ¥È¡¼¥¯¥ó¤ò\0¤ÇÀڤ롣cur¤Î¸å¤Î¶õÇò¤«\0¤òõ¤¹ */
385     n = find_token_end(cur);
386     if (!n) {
387       fprintf(stderr, "invalid \\ at the end of line (%s).\n",
388               ent);
389       return ;
390     }
391     if (*n) {
392       *n = 0;
393     } else {
394       n = NULL;
395     }
396     /**/
397     if (cur[0] == '#') {
398       if (isalpha((unsigned char)cur[1])) {
399         /* #XX*?? ¤ò¥Ñ¡¼¥¹ */
400         freq = parse_wtype(wtbuf, cur);
401       } else {
402         if (cur[1] == '_' &&
403             check_compound_candidate(mds, ye->index_xstr, &cur[1])) {
404           /* #_ Ê£¹ç¸õÊä */
405           push_back_word_entry(mds, ye, wtbuf, freq, cur, order);
406           order ++;
407         }
408       }
409     } else {
410       /* Éʻ줬½üµî¥ê¥¹¥È¤ËÆþ¤Ã¤Æ¤¤¤ë¤«¤ò¥Á¥§¥Ã¥¯ */
411       if (!is_excluded_wtype(mds, wtbuf)) {
412         /* Ã±¸ì¤òÄɲà*/
413         push_back_word_entry(mds, ye, wtbuf, freq, cur, order);
414         order ++;
415       }/* :to extract excluded words
416           else {
417           anthy_putxstr(ye->index_xstr);
418           printf(" %s*%d %s\n", wtbuf, freq, cur);
419           }*/
420     }
421     if (!n) {
422       /* ¹ÔËö */
423       return ;
424     }
425     cur = n;
426     cur ++;
427   }
428 }
429
430 /** Æ±¤¸Ã±¸ì¤¬Ìµ¤¤¤«¥Á¥§¥Ã¥¯ */
431 static int
432 check_same_word(struct yomi_entry *ye, int idx)
433 {
434   struct word_entry *base = &ye->entries[idx];
435   int i;
436   for (i = idx -1; i >= 0; i--) {
437     struct word_entry *cur = &ye->entries[i];
438     if (base->raw_freq != cur->raw_freq) {
439       return 0;
440     }
441     if (strcmp(base->wt_name, cur->wt_name)) {
442       return 0;
443     }
444     if (strcmp(base->word_utf8, cur->word_utf8)) {
445       return 0;
446     }
447     /* Æ±¤¸¤À¤Ã¤¿ */
448     return 1;
449   }
450   return 0;
451 }
452
453 /** qsortÍѤÎÈæ³Ó´Ø¿ô */
454 static int
455 compare_word_entry_by_freq(const void *p1, const void *p2)
456 {
457   const struct word_entry *e1 = p1;
458   const struct word_entry *e2 = p2;
459   return e2->raw_freq - e1->raw_freq;
460 }
461
462 /** qsortÍѤÎÈæ³Ó´Ø¿ô */
463 static int
464 compare_word_entry_by_wtype(const void *p1, const void *p2)
465 {
466   const struct word_entry *e1 = p1;
467   const struct word_entry *e2 = p2;
468   int ret = strcmp(e1->wt_name, e2->wt_name);
469   if (ret != 0) {
470     return ret;
471   } else {
472     return compare_word_entry_by_freq(p1, p2);
473   }
474 }
475
476 /** ÆɤߤËÂФ¹¤ëñ¸ì¤òÉÑÅÙ½ç¤Ëʤ١¢¤¤¤é¤Ê¤¤Ã±¸ì¤ò¾Ã¤¹ */
477 static int
478 normalize_word_entry(struct yomi_entry *ye)
479 {
480   int i, nr_dup = 0;
481   if (!ye) {
482     return 0;
483   }
484   /* Ã±¸ì¤òʤ٤ë */
485   qsort(ye->entries, ye->nr_entries,
486         sizeof(struct word_entry),
487         compare_word_entry_by_freq);
488   /* ¥À¥Ö¤Ã¤¿¤é¡¢0ÅÀ */
489   for (i = 0; i < ye->nr_entries; i++) {
490     if (check_same_word(ye, i)) {
491       ye->entries[i].raw_freq = 0;
492       nr_dup ++;
493     }
494   }
495   /* ºÆ¤Ó¥½¡¼¥È */
496   qsort(ye->entries, ye->nr_entries,
497         sizeof(struct word_entry),
498         compare_word_entry_by_wtype);
499   return ye->nr_entries - nr_dup;
500 }
501
502 /*¤½¤ÎÆɤߤËÂбþ¤¹¤ëyomi_entry¤òÊÖ¤¹
503 **/
504 struct yomi_entry *
505 find_yomi_entry(struct yomi_entry_list *yl, xstr *index, int create)
506 {
507   struct yomi_entry *ye;
508   int hash = index_hash(index);
509   int search = 0;
510   /* hash chain¤«¤éõ¤¹ */
511   for (ye = yl->hash[hash];ye ; ye = ye->hash_next) {
512     search ++;
513     if (!anthy_xstrcmp(ye->index_xstr, index)) {
514       return ye;
515     }
516   }
517   if (!create) {
518     return NULL;
519   }
520
521   /* Ìµ¤¤¤Î¤Ç³ÎÊÝ */
522   ye = malloc(sizeof(struct yomi_entry));
523   ye->nr_entries = 0;
524   ye->entries = 0;
525   ye->next = NULL;
526   ye->index_xstr = anthy_xstr_dup(index);
527   ye->index_str = NULL;
528
529   /* hash chain¤Ë¤Ä¤Ê¤° */
530   ye->hash_next = yl->hash[hash];
531   yl->hash[hash] = ye;
532
533   /* ¥ê¥¹¥È¤Ë¤Ä¤Ê¤° */
534
535   ye->next = yl->head;
536   yl->head = ye;
537
538   yl->nr_entries ++;
539
540   return ye;
541 }
542
543 /* ¼­½ñ¥Õ¥¡¥¤¥ëÃæ¤Îhash bitmap¤Ë¥Þ¡¼¥¯¤òÉÕ¤±¤ë */
544 static void
545 mark_hash_array(unsigned char *hash_array, xstr *xs)
546 {
547   int val, idx, bit, mask;
548   val = anthy_xstr_hash(xs);
549   val &= (YOMI_HASH_ARRAY_SIZE*YOMI_HASH_ARRAY_BITS-1);
550   idx=(val>>YOMI_HASH_ARRAY_SHIFT)&(YOMI_HASH_ARRAY_SIZE-1);
551   bit= val & ((1<<YOMI_HASH_ARRAY_SHIFT)-1);
552   mask = (1<<bit);
553   if (hash_array[idx] & mask) {
554     yomi_hash_collision ++;
555   }
556   hash_array[idx] |= mask;
557 }
558
559 /* Æɤßhash¤Î¥Ó¥Ã¥È¥Þ¥Ã¥×¤òºî¤ë */
560 static void
561 mk_yomi_hash(FILE *yomi_hash_out, struct yomi_entry_list *yl)
562 {
563   unsigned char *hash_array;
564   int i;
565   struct yomi_entry *ye;
566   hash_array = (unsigned char *)malloc(YOMI_HASH_ARRAY_SIZE);
567   for (i = 0; i < YOMI_HASH_ARRAY_SIZE; i++) {
568     hash_array[i] = 0;
569   }
570   for (i = 0; i < yl->nr_valid_entries; i++) {
571     ye = yl->ye_array[i];
572     mark_hash_array(hash_array, ye->index_xstr);
573   }
574   fwrite(hash_array, YOMI_HASH_ARRAY_SIZE, 1, yomi_hash_out);
575   printf("generated yomi hash bitmap (%d collisions/%d entries)\n",
576          yomi_hash_collision, yl->nr_valid_entries);
577          
578 }
579
580 static struct adjust_command *
581 parse_modify_freq_command(const char *buf)
582 {
583   char *line = alloca(strlen(buf) + 1);
584   char *yomi, *wt, *word, *type_str;
585   struct adjust_command *cmd;
586   int type = 0;
587   strcpy(line, buf);
588   yomi = strtok(line, " ");
589   wt = strtok(NULL, " ");
590   word = strtok(NULL, " ");
591   type_str = strtok(NULL, " ");
592   if (!yomi || !wt || !word || !type_str) {
593     return NULL;
594   }
595   if (!strcmp(type_str, "up")) {
596     type = ADJUST_FREQ_UP;
597   }
598   if (!strcmp(type_str, "down")) {
599     type = ADJUST_FREQ_DOWN;
600   }
601   if (!strcmp(type_str, "kill")) {
602     type = ADJUST_FREQ_KILL;
603   }
604   if (!type) {
605     return NULL;
606   }
607   cmd = malloc(sizeof(struct adjust_command));
608   cmd->type = type;
609   cmd->yomi = anthy_cstr_to_xstr(yomi, ANTHY_EUC_JP_ENCODING);
610   cmd->wt = get_wt_name(wt);
611   cmd->word = anthy_conv_euc_to_utf8(word);
612   return cmd;
613 }
614
615 static void
616 parse_adjust_command(const char *buf, struct adjust_command *ac_list)
617 {
618   struct adjust_command *cmd = NULL;
619   if (!strncmp("\\modify_freq ", buf, 13)) {
620     cmd = parse_modify_freq_command(&buf[13]);
621   }
622   if (cmd) {
623     cmd->next = ac_list->next;
624     ac_list->next = cmd;
625   }
626 }
627
628 /** ¼­½ñ¤ò°ì¹Ô¤º¤ÄÆɤ߹þ¤ó¤Ç¥ê¥¹¥È¤òºî¤ë
629  * ¤³¤Î¥³¥Þ¥ó¥É¤Î¥³¥¢ */
630 static void
631 parse_dict_file(FILE *fin, struct mkdic_stat *mds)
632 {
633   xstr *index_xs;
634   char buf[MAX_LINE_LEN];
635   char *ent;
636   struct yomi_entry *ye = NULL;
637
638   /* £±¹Ô¤º¤Ä½èÍý */
639   while (read_line(fin, buf)) {
640     if (buf[0] == '\\' && buf[1] != ' ') {
641       parse_adjust_command(buf, &mds->ac_list);
642       continue ;
643     }
644     index_xs = get_index_from_line(mds, buf);
645     if (!index_xs) {
646       break;
647     }
648     ent = get_entry_from_line(buf);
649
650     /* Æɤߤ¬30ʸ»ú¤ò±Û¤¨¤ë¾ì¹ç¤Ï̵»ë */
651     if (index_xs->len < 31) {
652       ye = find_yomi_entry(&mds->yl, index_xs, 1);
653       push_back_word_entry_line(mds, ye, ent);
654     }
655
656     free(ent);
657     anthy_free_xstr(index_xs);
658   }
659 }
660
661 /* Æɤߡ¢Éʻ졢ñ¸ì¤Î»°¤ÄÁȤ«¤éñ¸ì¤Î¹½Â¤ÂΤò¼èÆÀ¤¹¤ë */
662 static struct word_entry *
663 find_word_entry(struct yomi_entry_list *yl, xstr *yomi,
664                 const char *wt, char *word)
665 {
666   struct yomi_entry *ye = find_yomi_entry(yl, yomi, 0);
667   int i;
668   if (!ye) {
669     return NULL;
670   }
671   for (i = 0; i < ye->nr_entries; i++) {
672     struct word_entry *we = &ye->entries[i];
673     if (!strcmp(we->wt_name, wt) &&
674         !strcmp(we->word_utf8, word)) {
675       return we;
676     }
677   }
678   return NULL;
679 }
680                 
681 /* ÉÑÅÙÄ´À°¤Î¥³¥Þ¥ó¥É¤òŬÍѤ¹¤ë */
682 static void
683 apply_adjust_command(struct yomi_entry_list *yl,
684                      struct adjust_command *ac_list)
685 {
686   struct adjust_command *cmd;
687   for (cmd = ac_list->next; cmd; cmd = cmd->next) {
688     struct word_entry *we = find_word_entry(yl, cmd->yomi,
689                                             cmd->wt, cmd->word);
690     if (!we) {
691       char *yomi = anthy_xstr_to_cstr(cmd->yomi, ANTHY_UTF8_ENCODING);
692       printf("failed to find target of adjust command (%s, %s, %s)\n",
693              yomi, cmd->wt, cmd->word);
694       free(yomi);
695       continue;
696     }
697     if (cmd->type == ADJUST_FREQ_UP) {
698       we->raw_freq *= 4;
699     }
700     if (cmd->type == ADJUST_FREQ_DOWN) {
701       we->raw_freq /= 4;
702       if (we->raw_freq == 0) {
703         we->raw_freq = 1;
704       }
705     }
706     if (cmd->type == ADJUST_FREQ_KILL) {
707       we->raw_freq = 0;
708     }
709   }
710 }
711
712 /* qsortÍѤÎÈæ³Ó´Ø¿ô */
713 static int
714 compare_yomi_entry(const void *p1, const void *p2)
715 {
716   const struct yomi_entry *const *y1 = p1;
717   const struct yomi_entry *const *y2 = p2;
718   return strcmp((*y1)->index_str, (*y2)->index_str);
719 }
720
721 /* yomi_entry¤Çsort¤¹¤ë */
722 static void
723 sort_word_dict(struct yomi_entry_list *yl)
724 {
725   int i;
726   struct yomi_entry *ye;
727   yl->nr_valid_entries = 0;
728   /* Ã±¸ì¤ò»ý¤ÄÆɤߤÀ¤±¤ò yl->ye_array¤ËµÍ¤áľ¤¹ */
729   yl->ye_array = malloc(sizeof(struct yomi_entry *) * yl->nr_entries);
730   for (i = 0, ye = yl->head; i < yl->nr_entries; i++, ye = ye->next) {
731     if (ye->nr_entries > 0) {
732       yl->ye_array[yl->nr_valid_entries] = ye;
733       yl->nr_valid_entries ++;
734     }
735   }
736   /**/
737   for (i = 0; i < yl->nr_valid_entries; i++) {
738     struct yomi_entry *ye = yl->ye_array[i];
739     ye->index_str = anthy_xstr_to_cstr(ye->index_xstr, yl->index_encoding);
740   }
741   /* ¥½¡¼¥È¤¹¤ë */
742   qsort(yl->ye_array, yl->nr_valid_entries,
743         sizeof(struct yomi_entry *),
744         compare_yomi_entry);
745   /* ÉÔÍפÊñ¸ì¤ò¾Ã¤¹ */
746   yl->nr_words = 0;
747   for (i = 0; i < yl->nr_valid_entries; i++) {
748     struct yomi_entry *ye = yl->ye_array[i];
749     yl->nr_words += normalize_word_entry(ye);
750   }
751 }
752
753 /** ¥Õ¥¡¥¤¥ë¤Î¥µ¥¤¥º¤ò¼èÆÀ¤¹¤ë */
754 static int
755 get_file_size(FILE *fp)
756 {
757   if (!fp) {
758     return 0;
759   }
760   return (ftell (fp) + SECTION_ALIGNMENT - 1) & (-SECTION_ALIGNMENT);
761 }
762
763 static void
764 copy_file(struct mkdic_stat *mds, FILE *in, FILE *out)
765 {
766   int i;
767   size_t nread;
768   char buf[BUFSIZ];
769
770   /* Pad OUT to the next aligned offset.  */
771   for (i = ftell (out); i & (SECTION_ALIGNMENT - 1); i++) {
772     fputc (0, out);
773   }
774
775   /* Copy the contents.  */
776   rewind (in);
777   while ((nread = fread (buf, 1, sizeof buf, in)) > 0) {
778     if (fwrite (buf, 1, nread, out) < nread) {
779       /* Handle short write (maybe disk full).  */
780       fprintf (stderr, "%s: %s: write error: %s\n",
781                progname, mds->output_fn, strerror (errno));
782       exit (1);
783     }
784   }
785 }
786
787 static void
788 generate_header(FILE *fp)
789 {
790   int buf[NR_HEADER_SECTIONS];
791   int i;
792   struct file_section *fs;
793   int off;
794
795   /* ½é´ü²½ */
796   for (i = 0; i < NR_HEADER_SECTIONS; i++) {
797     buf[i] = 0;
798   }
799
800   /* ¥Ø¥Ã¥À */
801   buf[0] = NR_HEADER_SECTIONS * sizeof(int);
802   buf[1] = 0;
803
804   /* ³Æ¥»¥¯¥·¥ç¥ó¤Î¥ª¥Õ¥»¥Ã¥È */
805   off = buf[0];
806   for (i = 2, fs = file_array; fs->fpp; fs ++, i++) {
807     buf[i] = off;
808     off += get_file_size(*(fs->fpp));
809   }
810
811   /* ¥Õ¥¡¥¤¥ë¤Ø½ÐÎϤ¹¤ë */
812   for (i = 0; i < NR_HEADER_SECTIONS; i++) {
813     write_nl(fp, buf[i]);
814   }
815 }
816
817 /* ³Æ¥»¥¯¥·¥ç¥ó¤Î¥Õ¥¡¥¤¥ë¤ò¥Þ¡¼¥¸¤·¤Æ¡¢¤Ò¤È¤Ä¤Î¼­½ñ¥Õ¥¡¥¤¥ë¤òºî¤ë */
818 static void
819 link_dics(struct mkdic_stat *mds)
820 {
821   FILE *fp;
822   struct file_section *fs;
823
824   fp = fopen (mds->output_fn, "w");
825   if (!fp) {
826       fprintf (stderr, "%s: %s: cannot create: %s\n",
827                progname, mds->output_fn, strerror (errno));
828       exit (1);
829   }
830
831   /* ¥Ø¥Ã¥À¤ò½ÐÎϤ¹¤ë */
832   generate_header(fp);
833
834   for (fs = file_array; fs->fpp; fs ++) {
835     /* ³Æ¥»¥¯¥·¥ç¥ó¤Î¥Õ¥¡¥¤¥ë¤ò·ë¹ç¤¹¤ë */
836     copy_file(mds, *(fs->fpp), fp);
837     if (fs->fn) {
838       unlink(fs->fn);
839     }
840   }
841
842   if (fclose (fp)) {
843     fprintf (stderr, "%s: %s: write error: %s\n",
844              progname, mds->output_fn, strerror (errno));
845     exit (1);
846   }
847 }
848
849 static void
850 read_dict_file(struct mkdic_stat *mds, const char *fn)
851 {
852   FILE *fp;
853   /* ¥Õ¥¡¥¤¥ë̾¤¬»ØÄꤵ¤ì¤¿¤Î¤ÇÆɤ߹þ¤à */
854   fp = fopen(fn, "r");
855   if (fp) {
856     printf("file = %s\n", fn);
857     parse_dict_file(fp, mds);
858     fclose(fp);
859   } else {
860     printf("failed file = %s\n", fn);
861   }
862 }
863
864 static void
865 complete_words(struct mkdic_stat *mds)
866 {
867   /* ÉÑÅÙÊäÀµ¤òŬÍѤ¹¤ë */
868   apply_adjust_command(&mds->yl, &mds->ac_list);
869
870   /**/
871   calc_freq(&mds->yl);
872
873   /* ÆɤߤÇʤÓÂؤ¨¤ë */
874   sort_word_dict(&mds->yl);
875
876   /* ¥Õ¥¡¥¤¥ë¤ò½àÈ÷¤¹¤ë */
877   open_output_files();
878   /* Ã±¸ì¼­½ñ¤ò½ÐÎϤ¹¤ë */
879   output_word_dict(&mds->yl);
880
881   /* Æɤߥϥå·¥å¤òºî¤ë */
882   mk_yomi_hash(yomi_hash_out, &mds->yl);
883 }
884
885 static void
886 read_udict_file(struct mkdic_stat *mds, const char *fn)
887 {
888   if (!mds->ud) {
889     mds->ud = create_uc_dict();
890     complete_words(mds);
891   }
892   read_uc_file(mds->ud, fn);
893   printf("uc = %s\n", fn);
894 }
895
896 static xstr*
897 xstr_strncat(xstr* xs, xchar* src, int n)
898 {
899   int i;
900   xs->str = realloc(xs->str, sizeof(xchar) * (xs->len + n + 1));
901
902   for (i = 0; i < n; ++i) {
903     xs->str[xs->len + i] = src[i];
904   }
905   xs->len += n;
906   return xs;
907 }
908
909 static void
910 reverse_multi_segment_word(struct mkdic_stat *mds, struct word_entry *we)
911 {
912   /*
913     ¡Ö¤«¤Ê¤«¤ó¤¸¤Ø¤ó¤«¤ó¤¨¤ó¤¸¤ó #T35 #_2²¾Ì¾_3´Á»ú_4ÊÑ´¹_4¥¨¥ó¥¸¥ó¡×
914     ¤«¤é
915     ¡Ö²¾Ì¾´Á»úÊÑ´¹¥¨¥ó¥¸¥ó #T35 #_2¤«¤Ê_2¤«¤ó¤¸_2¤Ø¤ó¤«¤ó_4¤¨¤ó¤¸¤ó¡×
916     ¤òºî¤ë
917   */
918   int j;
919   /* yomi¤Ï²¾Ì¾´Á»úº®¤¸¤ê word¤ÏÊ¿²¾Ì¾¤Î¤ß¤«¤é¤Ê¤ë */
920   int yomi_seg_start = 0;
921   int word_seg_start = 0;
922   int word_seg_len = 0;
923   xstr *yomibuf = anthy_cstr_to_xstr(we->word_utf8, ANTHY_UTF8_ENCODING);
924   xstr *wordbuf = we->ye->index_xstr;
925   xstr *yomi_xs = anthy_cstr_to_xstr("", 0);
926   xstr *word_xs = anthy_cstr_to_xstr("#", 0);
927   char *word;
928   char ch[256];
929   struct yomi_entry *target_ye;
930
931   for (j = 0; j <= yomibuf->len; ++j) {
932     if (j == yomibuf->len || yomibuf->str[j] == '_') {
933       if (yomi_seg_start != 0) {
934         anthy_xstrappend(word_xs, '_');
935         snprintf(ch, 256, "%x", j - yomi_seg_start);
936         anthy_xstrappend(word_xs, (xchar)ch[0]);
937         xstr_strncat(word_xs, &wordbuf->str[word_seg_start], word_seg_len);
938         xstr_strncat(yomi_xs, &yomibuf->str[yomi_seg_start], j - yomi_seg_start);
939       }
940       if (j == yomibuf->len) {
941         break;
942       }
943       yomi_seg_start = j + 2;
944       word_seg_start += word_seg_len;
945       word_seg_len = get_element_len(yomibuf->str[j + 1]);
946     }
947   }
948
949   target_ye = find_yomi_entry(&mds->yl, yomi_xs, 1);
950   word = anthy_xstr_to_cstr(word_xs, mds->input_encoding);
951
952   /* µÕÊÑ´¹ÍѤμ­½ñ¤Ïfreq¤¬Éé */
953   push_back_word_entry(mds, target_ye, we->wt_name, -we->raw_freq,
954                        word, we->source_order);
955
956   free(word);
957   anthy_free_xstr(yomibuf);
958   anthy_free_xstr(yomi_xs);
959   anthy_free_xstr(word_xs);
960 }
961
962 /* µÕÊÑ´¹ÍѤμ­½ñ¤òºî¤ë */
963 static void
964 build_reverse_dict(struct mkdic_stat *mds)
965 {
966   struct yomi_entry *ye;
967   int i, n;
968   struct word_entry *we_array;
969   printf("building reverse index\n");
970
971   /* Ã±¸ì¤Î¿ô¤ò¿ô¤¨¤ë */
972   n = 0;
973   for (ye = mds->yl.head; ye; ye = ye->next) {
974     for (i = 0; i < ye->nr_entries; i++) {
975       n++;
976     }
977   }
978   /* ¥³¥Ô¡¼¤¹¤ë
979    * (¸µ¤Î¼­½ñÃæ¤Î¥Ý¥¤¥ó¥¿¤Ïrealloc¤ÇÆ°¤¯¤Î¤Ç¥³¥Ô¡¼¤¬É¬Í×)
980    */
981   we_array = malloc(sizeof(struct word_entry )* n);
982   n = 0;
983   for (ye = mds->yl.head; ye; ye = ye->next) {
984     for (i = 0; i < ye->nr_entries; i++) {
985       we_array[n] = ye->entries[i];
986       n++;
987     }
988   }
989
990   /* ¼­½ñ¤ËÄɲ䷤Ƥ¤¤¯ */
991   for (i = 0; i < n; i++) {
992     struct word_entry *we;
993     struct yomi_entry *target_ye;
994
995     we = &we_array[i];
996     if (we->word_utf8[0] == '#') {
997       if (we->word_utf8[1] == '_') {
998         reverse_multi_segment_word(mds, we);
999       }
1000     } else {
1001       /* yomi¤Ï²¾Ì¾´Á»úº®¤¸¤ê word¤ÏÊ¿²¾Ì¾¤Î¤ß¤«¤é¤Ê¤ë */
1002       xstr *yomi_xs;
1003       char *word;
1004
1005       yomi_xs = anthy_cstr_to_xstr(we->word_utf8, ANTHY_UTF8_ENCODING);
1006       target_ye = find_yomi_entry(&mds->yl, yomi_xs, 1);
1007       word = anthy_xstr_to_cstr(we->ye->index_xstr, mds->input_encoding);
1008
1009       /* µÕÊÑ´¹ÍѤμ­½ñ¤Ïfreq¤¬Éé */
1010       push_back_word_entry(mds, target_ye, we->wt_name, -we->raw_freq,
1011                            word, we->source_order);
1012
1013       anthy_free_xstr(yomi_xs);
1014       free(word);
1015     }
1016   }
1017   /**/
1018   free(we_array);
1019 }
1020
1021 static void
1022 clear_exclude_wtypes(struct mkdic_stat *mds)
1023 {
1024   int i;
1025   for (i = 0; i < mds->nr_excluded; i++) {
1026     free(mds->excluded_wtypes[i]);
1027   }
1028   free(mds->excluded_wtypes);
1029   /**/
1030   mds->excluded_wtypes = NULL;
1031   mds->nr_excluded = 0;
1032 }
1033
1034 static void
1035 set_exclude_wtypes(struct mkdic_stat *mds, int nr, char **tokens)
1036 {
1037   int i;
1038   mds->nr_excluded = nr - 1;
1039   mds->excluded_wtypes = malloc(sizeof(char *) * (nr - 1));
1040   /**/
1041   for (i = 1; i < nr; i++) {
1042     mds->excluded_wtypes[i - 1] = strdup(tokens[i]);
1043   }
1044 }
1045
1046 static void
1047 set_dict_encoding(struct mkdic_stat *mds, const char *enc)
1048 {
1049   if (!strcmp(enc, "utf8")) {
1050     mds->yl.body_encoding = ANTHY_UTF8_ENCODING;
1051   }
1052 }
1053
1054 static void
1055 set_input_encoding(struct mkdic_stat *mds, const char *enc)
1056 {
1057   if (!strcmp(enc, "utf8")) {
1058     mds->input_encoding = ANTHY_UTF8_ENCODING;
1059   }
1060   if (!strcmp(enc, "eucjp")) {
1061     mds->input_encoding = ANTHY_EUC_JP_ENCODING;
1062   }
1063 }
1064
1065 static void
1066 write_dict_file(struct mkdic_stat *mds)
1067 {
1068   if (!mds->ud) {
1069     printf("can not build without use case dict\n");
1070     exit(1);
1071   }
1072
1073   /* ÍÑÎã¼­½ñ¤òºî¤ë */
1074   make_ucdict(uc_out, mds->ud);
1075
1076   /* ¼­½ñ¥Õ¥¡¥¤¥ë¤Ë¤Þ¤È¤á¤ë */
1077   flush_output_files();
1078   link_dics(mds);
1079 }
1080
1081 static void
1082 show_command(char **tokens, int nr)
1083 {
1084   int i;
1085   printf("cmd:");
1086   for (i = 0; i < nr; i++) {
1087     printf(" %s", tokens[i]);
1088   }
1089   printf("\n");
1090 }
1091
1092 static int
1093 execute_batch(struct mkdic_stat *mds, const char *fn)
1094 {
1095   int nr;
1096   char **tokens;
1097   if (anthy_open_file(fn)) {
1098     printf("mkanthydic: failed to open %s\n", fn);
1099     return 1;
1100   }
1101   while (!anthy_read_line(&tokens, &nr)) {
1102     char *cmd = tokens[0];
1103     show_command(tokens, nr);
1104     if (!strcmp(cmd, "read") && nr == 2) {
1105       read_dict_file(mds, tokens[1]);
1106     } else if (!strcmp(cmd, "read_uc") && nr == 2) {
1107       read_udict_file(mds, tokens[1]);
1108     } else if (!strcmp(cmd, "build_reverse_dict")) {
1109       build_reverse_dict(mds);
1110     } else if (!strcmp(cmd, "write")) {
1111       write_dict_file(mds);
1112     } else if (!strcmp(cmd, "set_exclude_wtypes")) {
1113       set_exclude_wtypes(mds, nr, tokens);
1114     } else if (!strcmp(cmd, "clear_exclude_wtypes")) {
1115       clear_exclude_wtypes(mds);
1116     } else if (!strcmp(cmd, "set_dict_encoding") && nr == 2) {
1117       set_dict_encoding(mds, tokens[1]);
1118     } else if (!strcmp(cmd, "set_input_encoding") && nr == 2) {
1119       set_input_encoding(mds, tokens[1]);
1120     } else if (!strcmp(cmd, "done")) {
1121       anthy_free_line();
1122       break;
1123     } else {
1124       printf("Unknown command(%s).\n", cmd);
1125     }
1126     anthy_free_line();
1127   }
1128   anthy_close_file();
1129   return 0;
1130 }
1131
1132 /* ¼­½ñÀ¸À®¤Î¤¿¤á¤ÎÊÑ¿ô¤Î½é´ü²½ */
1133 static void
1134 init_mds(struct mkdic_stat *mds)
1135 {
1136   int i;
1137   mds->output_fn = DEFAULT_FN;
1138   mds->ud = NULL;
1139
1140   /* Ã±¸ì¼­½ñ¤ò½é´ü²½¤¹¤ë */
1141   mds->yl.head = NULL;
1142   mds->yl.nr_entries = 0;
1143   for (i = 0; i < YOMI_HASH; i++) {
1144     mds->yl.hash[i] = NULL;
1145   }
1146   mds->yl.index_encoding = ANTHY_UTF8_ENCODING;
1147   mds->yl.body_encoding = ANTHY_EUC_JP_ENCODING;
1148   /**/
1149   mds->ac_list.next = NULL;
1150   /**/
1151   mds->input_encoding = ANTHY_EUC_JP_ENCODING;
1152   /**/
1153   mds->nr_excluded = 0;
1154   mds->excluded_wtypes = NULL;
1155 }
1156
1157 /* libanthy¤Î»ÈÍѤ¹¤ëÉôʬ¤À¤±¤ò½é´ü²½¤¹¤ë */
1158 static void
1159 init_libs(void)
1160 {
1161   int res;
1162   res = anthy_init_xstr();
1163   if (res == -1) {
1164     fprintf (stderr, "failed to init dic lib\n");
1165     exit(1);
1166   }
1167 }
1168
1169 /**/
1170 int
1171 main(int argc, char **argv)
1172 {
1173   struct mkdic_stat mds;
1174   int i;
1175   char *script_fn = NULL;
1176   int help_mode = 0;
1177
1178   anthy_init_wtypes();
1179   init_libs();
1180   init_mds(&mds);
1181
1182   for (i = 1; i < argc; i++) {
1183     char *arg = argv[i];
1184     char *prev_arg = argv[i-1];
1185     if (!strcmp(arg, "--help")) {
1186       help_mode = 1;
1187     }
1188     if (!strcmp(prev_arg, "-f")) {
1189       script_fn = arg;
1190     }
1191   }
1192
1193   if (help_mode || !script_fn) {
1194     print_usage();
1195   }
1196
1197   return execute_batch(&mds, script_fn);
1198 }