static MPlist * parse_m17n_value (MPlist *plist, gchar *text) { MPlist *value; if (mplist_key (plist) == Msymbol) { value = mplist (); mplist_add (value, Msymbol, msymbol (text)); return value; } if (mplist_key (plist) == Mtext) { MText *mtext; mtext = mtext_from_data (text, strlen (text), MTEXT_FORMAT_UTF_8); value = mplist (); mplist_add (value, Mtext, mtext); return value; } if (mplist_key (plist) == Minteger) { long val; errno = 0; val = strtol (text, NULL, 10); if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || (errno != 0 && val == 0)) return NULL; value = mplist (); mplist_add (value, Minteger, (void *)val); return value; } return NULL; }
static MPlist * add_action (MPlist *actions, MSymbol name, MSymbol key, void *val) { MPlist *action = mplist (); mplist_add (action, Msymbol, name); if (key != Mnil) mplist_add (action, key, val); mplist_add (actions, Mplist, action); m17n_object_unref (action); return actions; }
static MPlist * paginate (MPlist *candidates) { MPlist *p = candidates, *pl = mplist (), *plist = mplist (); int i; for (i = 0; mplist_key (p) == Mtext; p = mplist_next (p), i++) { mplist_add (pl, Mtext, mplist_value (p)); if (i % 10 == 9) { mplist_add (plist, Mplist, pl); m17n_object_unref (pl); pl = mplist (); } } if (mplist_key (pl) != Mnil) mplist_add (plist, Mplist, pl); m17n_object_unref (pl); return plist; }
static MPlist * parse_m17n_value (MPlist *plist, gchar *text) { MPlist *value; if (mplist_key (plist) == Msymbol) { value = mplist (); mplist_add (value, Msymbol, msymbol (text)); return value; } if (mplist_key (plist) == Mtext) { MText *mtext; mtext = mconv_decode_buffer (Mcoding_utf_8, (const unsigned char *) text, strlen (text)); value = mplist (); mplist_add (value, Mtext, mtext); return value; } if (mplist_key (plist) == Minteger) { long val; errno = 0; val = strtol (text, NULL, 10); if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || (errno != 0 && val == 0)) return NULL; value = mplist (); mplist_add (value, Minteger, (void *)val); return value; } return NULL; }
MPlist * lookup (MPlist *args) { MInputContext *ic; MPlist *actions = NULL, *candidates, *plist; MSymbol init_state; MSymbol select_state; TableContext *context; MText *mt; ic = mplist_value (args); context = get_context (ic); args = mplist_next (args); init_state = (MSymbol) mplist_value (args); args = mplist_next (args); select_state = (MSymbol) mplist_value (args); if (context->desc) candidates = (*context->desc->lookup) (context, args); else candidates = mplist (); if (mplist_length (candidates) == 0) { m17n_object_unref (candidates); return NULL; } #if 0 /* FIXME: if only one candidate is matching, we should insert it and commit immediately. However, this feature is disabled for now since users would type extra characters after the match. For example, with mr-inscript-typing-booster, an Indic word "Epgyepgne" is only one candidate when a user type "Epgyepgn", but the user will likely to type "e" after commit. */ if (mplist_length (candidates) == 1) { actions = mplist (); add_action (actions, msymbol ("delete"), Msymbol, msymbol ("@<")); add_action (actions, msymbol ("insert"), Mtext, mplist_value (candidates)); add_action (actions, msymbol ("commit"), Mnil, NULL); m17n_object_unref (candidates); return actions; } #endif actions = mplist (); mt = mtext_dup (ic->preedit); mplist_push (candidates, Mtext, mt); m17n_object_unref (mt); plist = paginate (candidates); m17n_object_unref (candidates); add_action (actions, msymbol ("delete"), Msymbol, msymbol ("@<")); mplist_add (actions, Mplist, plist); m17n_object_unref (plist); add_action (actions, msymbol ("select"), Minteger, (void *)1); add_action (actions, msymbol ("show"), Mnil, NULL); add_action (actions, msymbol ("shift"), Msymbol, select_state); return actions; }
static MPlist * lookup_ibus (TableContext *context, MPlist *args) { unsigned char buf[256]; char *word = NULL, *sql = NULL, *msql = NULL; MPlist *candidates = mplist (); int len, xlen, wlen, mlen; int i, rc; int *m = NULL; sqlite3_stmt *stmt; MText *mt; if (!context->db) goto out; rc = mtext_to_utf8 (context, context->ic->preedit, buf, sizeof (buf)); if (rc < 0) goto out; word = strdup ((const char *)buf); len = rc; mlen = CLAMP(context->mlen, 0, 99); rc = encode_phrase ((const unsigned char *)word, &m); if (rc) goto out; /* strlen(" AND mXX = XX") = 13 */ if (len > mlen) len = mlen; msql = calloc (sizeof (char), 13 * len + 1); if (!msql) goto out; for (i = 0; i < len; i++) { char s[14]; sqlite3_snprintf (13, s, " AND m%d = %d", i, m[i]); strcat (msql, s); } sql = calloc (sizeof (char), 128 + strlen (msql) + 1); if (!sql) goto out; /* issue query repeatedly until at least one candidates are found or the key length is exceeds mlen */ xlen = context->xlen; wlen = mlen - len + 1; for (; xlen <= wlen + 1; xlen++) { sqlite3_snprintf (128, sql, "SELECT id, phrase FROM phrases WHERE mlen < %lu", len + xlen); strcat (sql, msql); strcat (sql, " ORDER BY mlen ASC, user_freq DESC, freq DESC, id ASC"); if (context->max_candidates) sqlite3_snprintf (128, sql + strlen (sql), " LIMIT %lu", context->max_candidates); #ifdef DEBUG fprintf (stderr, "%s\n", sql); #endif rc = sqlite3_prepare (context->db, sql, strlen (sql), &stmt, NULL); if (rc != SQLITE_OK) { sqlite3_finalize (stmt); goto out; } while (sqlite3_step (stmt) == SQLITE_ROW) { const unsigned char *text; text = sqlite3_column_text (stmt, 1); #ifdef DEBUG fprintf (stderr, " %s\n", text); #endif mt = mtext_from_utf8 (context, text, strlen ((const char *)text)); mplist_add (candidates, Mtext, mt); m17n_object_unref (mt); } sqlite3_finalize (stmt); if (mplist_length (candidates) > 0) break; } out: if (word) free (word); if (m) free (m); if (sql) free (sql); if (msql) free (msql); return candidates; }