From adce15243ac55b6a1d8a832f21e4eaf6c9e54cf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?= Date: Wed, 2 Oct 2013 02:17:38 +0200 Subject: [PATCH] Add a querying tool for use in e.g. IRC bots Now the tools don't get built on `make all' but instead have their own target called `tools'. It might be reasonable to move them into their own directory sometime, instead of cluttering `src'. --- CMakeLists.txt | 8 ++- src/query-tool.c | 176 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 src/query-tool.c diff --git a/CMakeLists.txt b/CMakeLists.txt index e2c1373..b9fed6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,10 +110,16 @@ add_executable (${CMAKE_PROJECT_NAME} target_link_libraries (${CMAKE_PROJECT_NAME} ${project_common_libraries}) # Tools -add_executable (add-pronunciation +add_executable (query-tool EXCLUDE_FROM_ALL + src/query-tool.c ${project_common_sources}) +target_link_libraries (query-tool ${project_common_libraries}) + +add_executable (add-pronunciation EXCLUDE_FROM_ALL src/add-pronunciation.c ${project_common_sources}) target_link_libraries (add-pronunciation ${project_common_libraries}) +add_custom_target (tools DEPENDS add-pronunciation query-tool) + # The files to be installed include (GNUInstallDirs) install (TARGETS ${CMAKE_PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/src/query-tool.c b/src/query-tool.c new file mode 100644 index 0000000..55fd823 --- /dev/null +++ b/src/query-tool.c @@ -0,0 +1,176 @@ +/* + * A tool to query multiple dictionaries for the specified word + * + * Intended for use in IRC bots and similar silly things---words go in, one + * on a line, and entries come out, one dictionary at a time, finalised with + * an empty line. Newlines are escaped with `\n', backslashes with `\\'. + * + * So far only the `m' field is supported. Feel free to extend the program + * according to your needs, it's not very complicated. + * + * Copyright (c) 2013, Přemysl Janouch + * All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include +#include +#include +#include + +#include +#include + +#include "stardict.h" +#include "stardict-private.h" +#include "generator.h" + +static guint +count_equal_chars (const gchar *a, const gchar *b) +{ + guint count = 0; + while (*a && *b) + if (*a++ == *b++) + count++; + return count; +} + +static void +do_dictionary (StardictDict *dict, const gchar *word) +{ + gboolean found; + StardictIterator *iter = stardict_dict_search (dict, word, &found); + if (!found) + goto out; + + // Default Stardict ordering is ASCII case-insensitive. + // Try to find a better matching entry based on letter case: + + gint64 best_offset = stardict_iterator_get_offset (iter); + guint best_score = count_equal_chars + (stardict_iterator_get_word (iter), word); + + while (TRUE) + { + stardict_iterator_next (iter); + if (!stardict_iterator_is_valid (iter)) + break; + + const gchar *iter_word = stardict_iterator_get_word (iter); + if (g_ascii_strcasecmp (iter_word, word)) + break; + + guint score = count_equal_chars (iter_word, word); + if (score > best_score) + { + best_offset = stardict_iterator_get_offset (iter); + best_score = score; + } + } + + stardict_iterator_set_offset (iter, best_offset, FALSE); + + StardictEntry *entry = stardict_iterator_get_entry (iter); + StardictInfo *info = stardict_dict_get_info (dict); + const GList *list = stardict_entry_get_fields (entry); + for (; list; list = list->next) + { + StardictEntryField *field = list->data; + if (field->type == STARDICT_FIELD_MEANING) + { + const gchar *desc = field->data; + printf ("%s:", info->book_name); + for (; *desc; desc++) + { + if (*desc == '\\') + printf ("\\\\"); + else if (*desc == '\n') + printf ("\\n"); + else + putchar (*desc); + } + putchar ('\n'); + } + } + g_object_unref (entry); +out: + g_object_unref (iter); +} + +int +main (int argc, char *argv[]) +{ +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + if (glib_check_version (2, 36, 0)) + g_type_init (); +G_GNUC_END_IGNORE_DEPRECATIONS + + GError *error = NULL; + GOptionContext *ctx = g_option_context_new + ("DICTIONARY.ifo... - query multiple dictionaries"); + if (!g_option_context_parse (ctx, &argc, &argv, &error)) + { + g_printerr ("Error: option parsing failed: %s\n", error->message); + exit (EXIT_FAILURE); + } + g_option_context_free (ctx); + + if (argc < 2) + { + g_printerr ("Error: no dictionaries given\n"); + exit (EXIT_FAILURE); + } + + guint n_dicts = argc - 1; + StardictDict **dicts = g_alloca (sizeof *dicts * n_dicts); + + guint i; + for (i = 1; i <= n_dicts; i++) + { + dicts[i - 1] = stardict_dict_new (argv[i], &error); + if (error) + { + g_printerr ("Error: opening dictionary `%s' failed: %s\n", + argv[i], error->message); + exit (EXIT_FAILURE); + } + } + + while (TRUE) + { + GString *s = g_string_new (NULL); + + gint c; + while ((c = getchar ()) != EOF && c != '\n') + if (c != '\r') + g_string_append_c (s, c); + + if (s->len) + for (i = 0; i < n_dicts; i++) + do_dictionary (dicts[i], s->str); + + printf ("\n"); + fflush (NULL); + g_string_free (s, TRUE); + + if (c == EOF) + break; + } + + for (i = 0; i < n_dicts; i++) + g_object_unref (dicts[i]); + + return 0; +}