diff options
author | Benjamin Otte <otte@gnome.org> | 2009-07-08 13:13:38 +0200 |
---|---|---|
committer | Benjamin Otte <otte@gnome.org> | 2009-07-08 13:13:38 +0200 |
commit | 9e969883459bbc31c8709ea76dc741d6c1e6d64b (patch) | |
tree | 66c3d4fca3fdf32bf308786bc398cde5b8f9fb50 | |
parent | 982fa0b5800de513bd9e6bb33245b6aad611bf48 (diff) |
add custom functions "match", "markup" and "regexp"
This also allows using match and regexp in where clauses.
-rw-r--r-- | ephy-history.c | 189 |
1 files changed, 188 insertions, 1 deletions
diff --git a/ephy-history.c b/ephy-history.c index 26d99c5..9d8d5b6 100644 --- a/ephy-history.c +++ b/ephy-history.c @@ -22,6 +22,7 @@ #endif #include <sqlite3.h> +#include <string.h> #include "ephy-history.h" @@ -173,11 +174,174 @@ check_sqlite_result (EphyHistory * history, return FALSE; } +static void +_g_string_append_escaped (GString *string, + const char *text, + gssize len) +{ + char *s = g_markup_escape_text (text, len); + g_string_append (string, s); + g_free (s); +} + +static void +custom_function_markup (sqlite3_context *context, + int argc, + sqlite3_value** argv) +{ + const char *text; + GRegex *regex; + GString *string; + GMatchInfo *match; + int i, current, end; + + if (argc < 2) + { + sqlite3_result_null (context); + return; + } + + text = (const char *) sqlite3_value_text (argv[0]); + if (text == NULL || text[0] == 0) + { + sqlite3_result_null (context); + return; + } + + regex = sqlite3_get_auxdata (context, 0); + if (regex == NULL) + { + regex = g_regex_new (text, + G_REGEX_CASELESS | G_REGEX_OPTIMIZE, + G_REGEX_MATCH_NOTEMPTY, + NULL); + + sqlite3_set_auxdata (context, 0, regex, (GDestroyNotify) g_regex_unref); + } + + string = g_string_new (""); + current = end = 0; + for (i = 1; i < argc; i++) + { + const char *argument = (const char *) sqlite3_value_text (argv[1]); + if (i > 1) + g_string_append_c (string, '\n'); + if (!g_regex_match (regex, + argument, + G_REGEX_MATCH_NOTEMPTY, + &match)) + { + _g_string_append_escaped (string, argument, -1); + continue; + } + + do { + int from, to; + if (!g_match_info_fetch_pos (match, 0, &from, &to)) + continue; + g_assert (from >= 0 && to >= 0); + if (from > end) + { + if (end > current) + { + g_string_append (string, "<b>"); + _g_string_append_escaped (string, argument + current, end - current); + g_string_append (string, "</b>"); + } + _g_string_append_escaped (string, argument + end, from - end); + current = from; + } + end = MAX (end, to); + } while (g_match_info_next (match, NULL)); + if (end > current) + { + g_string_append (string, "<b>"); + _g_string_append_escaped (string, argument + current, end - current); + g_string_append (string, "</b>"); + } + _g_string_append_escaped (string, argument + end, -1); + g_match_info_free (match); + } + + sqlite3_result_text (context, + string->str, + string->len, + g_free); + g_string_free (string, FALSE); +} + +static void +custom_function_match (sqlite3_context *context, + int argc, + sqlite3_value** argv) +{ + const char *has_match, *haystack, *needle; + + haystack = (char *) sqlite3_value_text (argv[1]); + needle = (char *) sqlite3_value_text (argv[0]); +#if 0 + /* + * - requires _GNU_SOURCE when including string.h + * - the version below is 50% faster (huh?) + */ + has_match = strcasestr (haystack, needle); +#else + { + gsize needle_len = strlen (needle); + char find[3] = { g_ascii_tolower (*needle), g_ascii_toupper (*needle), 0 }; + has_match = haystack; + while ((has_match = strpbrk (has_match, find))) + { + if (strncasecmp (has_match, needle, needle_len) == 0) + break; + has_match++; + } + } +#endif + sqlite3_result_int (context, has_match ? 1 : 0); +} + +static void +custom_function_regexp (sqlite3_context *context, + int argc, + sqlite3_value** argv) +{ + GRegex *regex; + gboolean has_match; + + regex = sqlite3_get_auxdata (context, 0); + if (regex == NULL) + { + regex = g_regex_new ((char *) sqlite3_value_text (argv[0]), + G_REGEX_CASELESS | G_REGEX_OPTIMIZE, + G_REGEX_MATCH_NOTEMPTY, + NULL); + + sqlite3_set_auxdata (context, 0, regex, (GDestroyNotify) g_regex_unref); + } + + has_match = g_regex_match (regex, + (char *) sqlite3_value_text (argv[1]), + G_REGEX_MATCH_NOTEMPTY, + NULL); + sqlite3_result_int (context, has_match ? 1 : 0); +} + static gboolean ephy_history_open_database (EphyHistory * history, GSimpleAsyncResult *res) { + static const struct { + const char *name; + int n_args; + void (* func) (sqlite3_context *, int, sqlite3_value **); + } custom_functions[] = { + { "markup", -1, custom_function_markup }, + { "match", 2, custom_function_match }, + { "regexp", 2, custom_function_regexp } + }; EphyHistoryPrivate *priv = history->priv; + guint i; if (priv->db) return TRUE; @@ -189,8 +353,28 @@ ephy_history_open_database (EphyHistory * history, NULL))) return FALSE; + /* register custom functions */ + for (i = 0; i < G_N_ELEMENTS (custom_functions); i++) + { + if (!check_sqlite_result (history, res, + sqlite3_create_function (priv->db, + custom_functions[i].name, + custom_functions[i].n_args, + SQLITE_UTF8, + NULL, + custom_functions[i].func, + NULL, + NULL))) + goto fail; + } + /* FIXME: create database if it doesn't exist */ return TRUE; + +fail: + sqlite3_close (priv->db); + priv->db = NULL; + return FALSE; } GQuark @@ -298,8 +482,11 @@ ephy_history_select_thread (GSimpleAsyncResult *res, g_value_init (&array->values[i], G_TYPE_DOUBLE); g_value_set_int64 (&array->values[i], sqlite3_column_double (stmt, i)); break; - case SQLITE_BLOB: case SQLITE_NULL: + g_value_init (&array->values[i], G_TYPE_STRING); + /* leave values as NULL */ + break; + case SQLITE_BLOB: /* FIXME: implement */ default: g_warning ("unhandled column type %d", sqlite3_column_type (stmt, i)); |