Author: ekato
Date: Sun Dec 19 11:09:22 2004
New Revision: 45

Modified:
   trunk/scm/skk.scm
   trunk/uim/skk-dic.c

Log:
* uim/skk-dic.c : Make uim's skk support candidate arrays with
  different okurigana.  Also make sure to update and merge
  personal dictionary cache while saving if other process which
  uses libuim have modified the dictionary file.
(skk_cand_array) : Fix comment.
(skk_line) : Ditto.
(dic_info) : Add new member, cache_modified and cache_len.
(open_dic) : Initialize properly.
(okuri_in_bracket) : New function to get okurigana in bracket.
(compose_line_parts) : Get correct candidate from dictionary entry
  within bracket.
(copy_skk_line) : New function.
(add_line_to_cache_head) : Increment length of cached lines.
(merge_real_candidate_array) : New function.
(skk_commit_candidate) : Delete code for nth = 0.  Set candidate
  in candidate array with corresponding okurigana if okuri exists.
(parse_dic_line) : Add dic_info for argument.
(skk_read_personal_dictionary) : New function.  Splitted from
  skk_lib_read_personal_dictionary().  Store timestamp of personal
  dictionary while loading.
(skk_lib_read_personal_dictionary) : Most implementation moves
  into skk_read_personal_dictionary().
(push_back_candidate_array_to_sl) : New function.  Copy and add
  candidate array to skk_line.
(compare_and_merge_skk_line) : New function.  Merge candidates
  from two skk_lines with same heading entry.
(compare_entry) : New function for merge sort.
(cache_line_diffs) : New function.  Return lines with differential
  heading entry between two personal dictionary caches.  Also
  merge candidate arrays for line with same heading entry.
(lmerge) : New function for merge sort.
(lsort) : New function.  Do merge sort on personal dictionary
  cache.
(update_personal_dictionary_cache) : New function.  Update cache
  using updated personal dictionary file.
(skk_lib_save_personal_dictionary) : Check timestamp of personal
  dictionary and update cache if the file is modified by other
  process.
* scm/skk.scm (skk-release-handler) : Add release handler.  Call
  skk-save-personal-dictionary.


Modified: trunk/scm/skk.scm
==============================================================================
--- trunk/scm/skk.scm   (original)
+++ trunk/scm/skk.scm   Sun Dec 19 11:09:22 2004
@@ -1491,6 +1491,10 @@
       (update-style skk-style-spec (symbol-value skk-style))
       sc)))
 
+(define skk-release-handler
+  (lambda (sc)
+    (skk-save-personal-dictionary)))
+
 (define skk-press-key-handler
   (lambda (sc key state)
     (if (control-char? key)
@@ -1548,7 +1552,7 @@
  (N_ "Uim's SKK like input method")
  #f
  skk-init-handler
- #f
+ skk-release-handler
  context-mode-handler
  skk-press-key-handler
  skk-release-key-handler

Modified: trunk/uim/skk-dic.c
==============================================================================
--- trunk/uim/skk-dic.c (original)
+++ trunk/uim/skk-dic.c Sun Dec 19 11:09:22 2004
@@ -69,7 +69,7 @@
 
   /* this array was used and merged with okuri-nasi entry array */
   int is_used;
-  /* link to next entry in the list */
+  /* link to its parent line */
   struct skk_line *line;
 };
 
@@ -77,8 +77,8 @@
 struct skk_line {
   /* line index. head part */
   char *head;
-  /* line index. okurigana part. value will be 0,
-     if it is okuri-nasi entry */
+  /* line index. okurigana part. value will be 0 if it is okuri-nasi
+     entry */
   char okuri_head;
   /* array of candidate array for different okuri-gana */
   int nr_cand_array;
@@ -103,6 +103,10 @@
   struct skk_line head;
   /* timestamp of personal dictonary */
   time_t personal_dic_timestamp;
+  /* whether cached lines are modified or not */
+  int cache_modified;
+  /* length of cached lines */
+  int cache_len;
 } *skk_dic;
 
 /* completion */
@@ -197,10 +201,13 @@
   close(fd);
   di = (struct dic_info *)malloc(sizeof(struct dic_info));
   di->addr = addr;
-  di->size = success ? st.st_size : NULL;
-  di->first = success ? find_first_line(di) : NULL;
-  di->border = success ? find_border(di, st.st_size) : NULL;
+  di->size = success ? st.st_size : 0;
+  di->first = success ? find_first_line(di) : 0;
+  di->border = success ? find_border(di, st.st_size) : 0;
   di->head.next = NULL;
+  di->personal_dic_timestamp = 0;
+  di->cache_modified = 0;
+  di->cache_len = 0;
   return di;
 }
 
@@ -289,9 +296,23 @@
 }
 
 static char *
+okuri_in_bracket(char *str)
+{
+  char *p, *term;
+
+  if (!str)
+    return NULL;
+
+  p = strdup(str);
+  term = next_slash(p);
+  *term = '\0';
+  return p;
+}
+
+static char *
 nth_candidate(char *str, int nth)
 {
-  char *p , *term;
+  char *p, *term;
   int i;
 
   str = first_space(str);
@@ -421,7 +442,8 @@
     tmp = nth_candidate(line, nth);
     if (tmp && strlen(tmp)) {
       if (tmp[0] == '[') {
-       compose_line_parts(di, sl, nth_candidate(&tmp[1], -1), &tmp[1]);
+       tmp[0] = ' ';
+       compose_line_parts(di, sl, okuri_in_bracket(&tmp[1]), &tmp[0]);
       } else if (tmp[0] != ']') {
        push_back_candidate_to_array(ca, tmp);
       }
@@ -431,7 +453,6 @@
       break;
     }
   } while (1);
-
 }
 
 static struct skk_line *
@@ -453,6 +474,39 @@
   return sl;
 }
 
+static struct skk_line *
+copy_skk_line(struct skk_line *p)
+{
+  int i, j;
+  struct skk_line *sl;
+
+  if (!p)
+    return NULL;
+
+  sl = malloc(sizeof(struct skk_line));
+  sl->need_save = p->need_save;
+  sl->head = strdup(p->head);
+  sl->okuri_head = p->okuri_head;
+  sl->nr_cand_array = p->nr_cand_array;
+  sl->cands = malloc(sizeof(struct skk_cand_array) * sl->nr_cand_array);
+  for (i = 0; i < sl->nr_cand_array; i++) {
+    struct skk_cand_array *ca = &sl->cands[i];
+    struct skk_cand_array *q = &p->cands[i];
+
+    ca->okuri = q->okuri ? strdup(q->okuri) : NULL;
+    ca->nr_cands = q->nr_cands;
+    ca->nr_real_cands = q->nr_real_cands;
+    ca->cands = malloc(sizeof(char *) * ca->nr_cands);
+    for (j = 0; j < ca->nr_cands; j++) {
+      ca->cands[j] = strdup(q->cands[j]);
+    }
+    ca->is_used = q->is_used;
+    ca->line = sl;
+  }
+  sl->next = NULL;
+  return sl;
+}
+
 /*
  * Compose skk line
  */
@@ -474,6 +528,8 @@
 {
   sl->next = di->head.next;
   di->head.next = sl;
+
+  di->cache_len++;
 }
 
 static void
@@ -508,6 +564,8 @@
     prev->next = sl;
   }
   sl->next = NULL;
+
+  di->cache_len++;
 }
 
 static struct skk_line *
@@ -1320,6 +1378,25 @@
   }
 }
 
+static void
+merge_real_candidate_array(struct skk_cand_array *src_ca,
+                          struct skk_cand_array *dst_ca)
+{
+  int i, j;
+  if (!src_ca || !dst_ca) {
+    return ;
+  }
+  for (i = 0; i < src_ca->nr_real_cands; i++) {
+    int dup = 0;
+    for (j = 0; j < dst_ca->nr_real_cands; j++) {
+      if (!strcmp(src_ca->cands[i], dst_ca->cands[j]))
+       dup = 1;
+    }
+    if (!dup)
+      reorder_candidate(dst_ca, src_ca->cands[i]);
+  }
+}
+
 static LISP
 skk_commit_candidate(LISP head_, LISP okuri_head_,
                     LISP okuri_, LISP nth_, LISP numlst_)
@@ -1335,23 +1412,6 @@
 
   nth = get_c_int(nth_);
 
-  if (nth == 0) {
-    ca = find_cand_array_lisp(head_, okuri_head_, okuri_, 0);
-#if 1
-    /*
-     * Even with 0th candidate, save the word into personal
-     * dictionary.  Note that this entry will be saved at the timing
-     * of calling skk-prepare-commit-string with skk-context-nth > 0
-     * for now.
-     */
-    ca->line->need_save = 1;
-    str = ca->cands[nth];
-    reorder_candidate(ca, str);
-#endif
-    move_line_to_cache_head(skk_dic, ca->line);
-    return NIL;
-  }
-
   ca = find_cand_array_lisp(head_, okuri_head_, okuri_, 0);
   if (!ca) {
     return NIL;
@@ -1398,15 +1458,28 @@
   reorder_candidate(ca, str);
 
   if (okuri_ != NIL) {
-    ca = find_cand_array_lisp(head_, okuri_head_, NIL, 0);
-    if (!ca || nr_cands <= nth) {
-      return NIL;
+    struct skk_line *sl;
+    char *okuri;
+    int found = 0;
+
+    okuri = uim_get_c_string(okuri_);
+    sl = ca->line;
+    for (i = 1; i < sl->nr_cand_array; i++) {
+      if (!strcmp(okuri, sl->cands[i].okuri)) {
+       found = 1;
+       break;
+      }
+    }
+    free(okuri);
+    if (!found) {
+      ca = find_cand_array_lisp(head_, okuri_head_, okuri_, 1);
+      reorder_candidate(ca, str);
     }
-    reorder_candidate(ca, str);
   }
 
   ca->line->need_save = 1;
   move_line_to_cache_head(skk_dic, ca->line);
+  skk_dic->cache_modified = 1;
 
   return NIL;
 }
@@ -1473,7 +1546,7 @@
 }
 
 static void
-parse_dic_line(char *line)
+parse_dic_line(struct dic_info *di, char *line)
 {
   char *buf, *sep;
   struct skk_line *sl;
@@ -1492,9 +1565,9 @@
   if (!islower(buf[0]) && islower(sep[-1])) { /* okuri-ari entry */
     char okuri_head = sep[-1];
     sep[-1] = 0;
-    sl = compose_line(skk_dic, buf, okuri_head, line);
+    sl = compose_line(di, buf, okuri_head, line);
   } else {
-    sl = compose_line(skk_dic, buf, 0, line);
+    sl = compose_line(di, buf, 0, line);
   }
   sl->need_save = 1;
   /**/
@@ -1502,7 +1575,7 @@
     sl->cands[i].nr_real_cands = sl->cands[i].nr_cands;
   }
   /**/
-  add_line_to_cache_last(skk_dic, sl);
+  add_line_to_cache_last(di, sl);
 }
 
 static void
@@ -1541,49 +1614,29 @@
 }
 
 static LISP
-skk_lib_save_personal_dictionary(LISP fn_)
+skk_read_personal_dictionary(struct dic_info *di, char *fn)
 {
+  struct stat st;
   FILE *fp;
-  char *fn = uim_get_c_string(fn_);
-  struct skk_line *sl;
+  char buf[4096];
+  int err_flag = 0;
 
-  if (fn) {
-    fp = fopen(fn, "w");
-    free(fn);
-  } else {
-    fp = stdout;
-  }
-  if (!fp) {
+  if (stat(fn, &st) == -1)
     return NIL;
-  }
 
-  for (sl = skk_dic->head.next; sl; sl = sl->next) {
-    if (sl->need_save) {
-      write_out_line(fp, sl);
-    }
-  }
-  fclose(fp);
-  return NIL;
-}
+  fp = fopen(fn, "r");
+  if (!fp)
+    return NIL;
 
-static LISP
-skk_lib_read_personal_dictionary(LISP fn_)
-{
-  char *fn = get_c_string(fn_);
-  FILE *fp = fopen(fn, "r");
-  char buf[4096];
-  int err_flag = 0;
+  di->personal_dic_timestamp = st.st_mtime;
 
-  if (!fp) {
-    return NIL;
-  }
   while (fgets(buf, 4096, fp)) {
     int len = strlen(buf);
     if (buf[len-1] == '\n') {
       if (err_flag == 0) {
        if (buf[0] != ';') {
          buf[len-1] = 0;
-         parse_dic_line(buf);
+         parse_dic_line(di, buf);
        }
       } else {
        /* erroneous line ends here */
@@ -1598,6 +1651,253 @@
 }
 
 static LISP
+skk_lib_read_personal_dictionary(LISP fn_)
+{
+  char *fn = get_c_string(fn_);
+  return skk_read_personal_dictionary(skk_dic, fn);
+}
+
+static void push_back_candidate_array_to_sl(struct skk_line *sl,
+                                     struct skk_cand_array *src_ca)
+{
+  int i;
+  struct skk_cand_array *ca;
+
+  sl->nr_cand_array++;
+  sl->cands = realloc(sl->cands,
+                 sizeof(struct skk_cand_array) * sl->nr_cand_array);
+  ca = &sl->cands[sl->nr_cand_array - 1];
+  ca->is_used = src_ca->is_used;
+  ca->nr_cands = src_ca->nr_cands;
+  ca->cands = malloc(sizeof(char *) * src_ca->nr_cands);
+  for (i = 0; i < ca->nr_cands; i++) {
+    ca->cands[i] = strdup(src_ca->cands[i]);
+  }
+
+  ca->nr_real_cands = src_ca->nr_real_cands;
+  ca->okuri = strdup(src_ca->okuri);
+  ca->line = sl;
+}
+
+static void compare_and_merge_skk_line(struct skk_line *dst_sl,
+                                      struct skk_line *src_sl)
+{
+  int i, j;
+  struct skk_cand_array *dst_ca, *src_ca;
+
+  if (dst_sl == NULL || src_sl == NULL)
+    return;
+
+  src_ca = &src_sl->cands[0];
+  dst_ca = &dst_sl->cands[0];
+  if (src_ca->nr_real_cands > dst_ca->nr_real_cands)
+    merge_real_candidate_array(src_ca, dst_ca);
+
+  for (i = 1; i < src_sl->nr_cand_array; i++) {
+    int dup = 0;
+    src_ca = &src_sl->cands[i];
+
+    for (j = 1; j < dst_sl->nr_cand_array; j++) {
+      dst_ca = &dst_sl->cands[j];
+      if (!strcmp(src_ca->okuri, dst_ca->okuri)) {
+       dup = 1;
+       if (src_ca->nr_real_cands > dst_ca->nr_real_cands)
+         merge_real_candidate_array(src_ca, dst_ca);
+      }
+    }
+    if (!dup)
+      push_back_candidate_array_to_sl(dst_sl, src_ca);
+  }
+}
+
+/* for merge sort */
+static int
+compare_entry(struct skk_line *p, struct skk_line *q)
+{
+  int ret;
+  ret = strcmp(p->head, q->head);
+
+  if (ret != 0)
+    return ret;
+  else
+    return p->okuri_head - q->okuri_head;
+}
+
+/*
+ * Retern lines with differential "midashi-go" between two personal
+ * dictionaly caches.  Also merge candidate arrays for line with same
+ * "midashi-go".  p and q are needed to be sorted.
+ */
+static struct skk_line *
+cache_line_diffs(struct skk_line *p, struct skk_line *q)
+{
+  struct skk_line *r, *s, head;
+  int cmp;
+  
+  for (r = &head; p && q; ) {
+    cmp = compare_entry(p, q);
+    if (cmp < 0) {
+      p = p->next;
+    } else if (cmp > 0) {
+      s = copy_skk_line(q);
+      r->next = s;
+      r = s;
+      q = q->next;
+    } else {
+      compare_and_merge_skk_line(p, q);
+      p = p->next;
+      q = q->next;
+    }
+  }
+  while (q) {
+    s = copy_skk_line(q);
+    r->next = s;
+    r = s;
+    q = q->next;
+  }
+  r->next = NULL;
+  return head.next;
+}
+
+/* for merge sort */
+static struct skk_line *
+lmerge(struct skk_line *p, struct skk_line *q)
+{
+  struct skk_line *r, head;
+
+  for (r = &head; p && q; ) {
+    if (compare_entry(p, q) < 0) {
+      r->next = p;
+      r = p;
+      p = p->next;
+    } else {
+      r->next = q;
+      r = q;
+      q = q->next;
+    }
+  }
+  r->next = (p ? p : q);
+  return head.next;
+}
+
+/* merge sort */
+static struct skk_line *
+lsort(struct skk_line *p)
+{
+  struct skk_line *q, *r;
+
+  if (p) {
+    q = p;
+    for (r = q->next; r && (r = r->next) != NULL; r = r->next)
+      q = q->next;
+    r = q->next;
+    q->next = NULL;
+    if (r)
+      p = lmerge(lsort(r), lsort(p));
+  }
+  return p;
+}
+
+static void
+update_personal_dictionary_cache(char *fn)
+{
+  struct dic_info *di;
+  struct skk_line *sl, *tmp, *diff, **cache_array;
+  int i;
+
+  di = (struct dic_info *)malloc(sizeof(struct dic_info));
+  if (di == NULL)
+    return;
+  di->head.next = NULL;
+  skk_read_personal_dictionary(di, fn);
+  di->head.next = lsort(di->head.next);
+
+  /* keep original sequence of cache */
+  cache_array = (struct skk_line **)malloc(sizeof(struct skk_line *)
+                 * skk_dic->cache_len);
+  if (cache_array == NULL)
+    return;
+  i = 0;
+  sl = skk_dic->head.next;
+  while (sl) {
+    cache_array[i] = sl;
+    sl = sl->next;
+    i++;
+  }
+
+  skk_dic->head.next = lsort(skk_dic->head.next);
+
+  /* get differential lines and merge candidate */
+  diff = cache_line_diffs(skk_dic->head.next, di->head.next);
+
+  /* revert sequence of the cache */
+  if (cache_array[0]) {
+    sl = skk_dic->head.next = cache_array[0];
+    for (i = 0; i < skk_dic->cache_len - 2; i++) {
+      sl->next = cache_array[i + 1];
+      sl = sl->next;
+    }
+    sl->next = NULL;
+  }
+
+  /* add differential lines at the top of the cache */
+  if (diff != NULL) {
+    diff->next = skk_dic->head.next;
+    skk_dic->head.next = diff;
+  }
+  skk_dic->cache_modified = 1;
+
+  sl = di->head.next;
+  while (sl) {
+    tmp = sl;
+    sl = sl->next;
+    free_skk_line(tmp);
+  }
+  free(di);
+  free(cache_array);
+}
+
+static LISP
+skk_lib_save_personal_dictionary(LISP fn_)
+{
+  FILE *fp;
+  char *fn = uim_get_c_string(fn_);
+  struct skk_line *sl;
+  struct stat st;
+
+  if (fn) {
+    if (stat(fn, &st) != -1) {
+      if (st.st_mtime != skk_dic->personal_dic_timestamp)
+       update_personal_dictionary_cache(fn);
+    }
+    if (skk_dic->cache_modified == 0) {
+      free(fn);
+      return NIL;
+    }
+    fp = fopen(fn, "w");
+    free(fn);
+    if (!fp) {
+      return NIL;
+    }
+  } else {
+    fp = stdout;
+  }
+
+  for (sl = skk_dic->head.next; sl; sl = sl->next) {
+    if (sl->need_save) {
+      write_out_line(fp, sl);
+    }
+  }
+  fclose(fp);
+
+  if (stat(fn, &st) != -1)
+    skk_dic->personal_dic_timestamp = st.st_mtime;
+  skk_dic->cache_modified = 0;
+
+  return NIL;
+}
+
+static LISP
 skk_lib_get_annotation(LISP str_)
 {
   char *str = uim_get_c_string(str_);
@@ -1654,7 +1954,7 @@
 {
   struct skk_line *sl, *tmp;
 
-  if(!skk_dic)
+  if (!skk_dic)
     return;
 
   if (skk_dic->addr) {

Reply via email to