/* * Copyright (C) 2004, 2005 Jean-Yves Lefort * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Jean-Yves Lefort nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include #include #include #include #include "translate.h" #include "translate-rfc3066-private.h" const unsigned int translate_major_version = TRANSLATE_MAJOR_VERSION; const unsigned int translate_minor_version = TRANSLATE_MINOR_VERSION; const unsigned int translate_micro_version = TRANSLATE_MICRO_VERSION; gboolean translate_initialized = FALSE; static GSList *services = NULL; G_LOCK_DEFINE_STATIC(services); static GHashTable *languages = NULL; G_LOCK_DEFINE_STATIC(languages); static char *proxy_uri = NULL; G_LOCK_DEFINE_STATIC(proxy_uri); static void translate_load_modules (const char *directory); static gboolean translate_load_module (const char *filename, GError **err); GQuark translate_error_quark (void) { return g_quark_from_static_string("translate-error"); } GQuark translate_init_error_quark (void) { return g_quark_from_static_string("translate-init-error"); } /** * translate_init: * @err: a location to report errors, or %NULL. Any of the errors in * #TranslateInitError or other domains may occur. * * Initializes libtranslate. This function can safely be called * multiple times (however, it is not * thread-safe, read below). When it is called more than once, it * returns the status (and error if any) of the initial invocation. * * * Unlike other libtranslate functions, translate_init() is * not thread-safe, and must * not be called while any mutex is held. The * reason is that translate_init() may call g_thread_init(). Refer to * the g_thread_init() documentation for more details. * * * Return value: %TRUE if the initialization was successful, %FALSE * otherwise (in such case @err is set to the error that has * occurred). **/ gboolean translate_init (GError **err) { if (! translate_initialized) { static GError *init_err = NULL; if (! init_err) { #ifdef ENABLE_NLS bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); #endif /* ENABLE_NLS */ g_type_init(); if (! g_thread_supported()) g_thread_init(NULL); if (g_thread_supported()) { char *user_modules; translate_initialized = TRUE; languages = g_hash_table_new(translate_ascii_strcase_hash, translate_ascii_strcase_equal); translate_rfc3066_init(languages); translate_load_modules(MODULESDIR); user_modules = g_build_filename(g_get_home_dir(), ".libtranslate", "modules", NULL); translate_load_modules(user_modules); g_free(user_modules); } else init_err = g_error_new(TRANSLATE_INIT_ERROR, TRANSLATE_INIT_ERROR_MULTI_THREADING_NOT_SUPPORTED, _("multi-threading is not supported")); } if (init_err && err) *err = g_error_copy(init_err); } return translate_initialized; } static void translate_load_modules (const char *directory) { GError *err = NULL; GDir *dir; const char *filename; g_return_if_fail(directory != NULL); if (! g_file_test(directory, G_FILE_TEST_IS_DIR)) return; dir = g_dir_open(directory, 0, &err); if (! dir) { g_warning(_("unable to scan modules directory \"%s\": %s"), directory, err->message); g_error_free(err); return; } while ((filename = g_dir_read_name(dir))) { char *pathname; char *extension; pathname = g_build_filename(directory, filename, NULL); if (g_file_test(pathname, G_FILE_TEST_IS_REGULAR) && (extension = strrchr(filename, '.')) && ! strcmp(++extension, G_MODULE_SUFFIX)) { if (! translate_load_module(pathname, &err)) { g_warning(_("unable to load module \"%s\": %s"), pathname, err->message); g_clear_error(&err); } } g_free(pathname); } g_dir_close(dir); } static gboolean translate_load_module (const char *filename, GError **err) { GModule *module; gpointer func; g_return_val_if_fail(filename != NULL, FALSE); module = g_module_open(filename, 0); if (! module) { g_set_error(err, TRANSLATE_ERROR, TRANSLATE_ERROR_FAILED, "%s", g_module_error()); return FALSE; } if (g_module_symbol(module, "translate_module_init", &func)) { if (((TranslateModuleInitFunc) func)(err)) return TRUE; } else g_set_error(err, TRANSLATE_ERROR, TRANSLATE_ERROR_FAILED, _("unable to find translate_module_init() function")); /* failed, close the module */ g_module_close(module); return FALSE; } /** * translate_add_service: * @service: a service. * * Adds @service to the libtranslate service list. If a service with * the same name is already in the list, nothing is done and %FALSE is * returned. * * Return value: %TRUE if the service was added, %FALSE if it was * already in the list. **/ gboolean translate_add_service (TranslateService *service) { const char *name; gboolean added = TRUE; GSList *l; g_return_val_if_fail(TRANSLATE_IS_SERVICE(service), FALSE); name = translate_service_get_name(service); g_return_val_if_fail(name != NULL, FALSE); G_LOCK(services); for (l = services; l != NULL && added; l = l->next) if (! strcmp(translate_service_get_name(l->data), name)) added = FALSE; if (added) services = g_slist_append(services, g_object_ref(service)); G_UNLOCK(services); return added; } /** * translate_get_service: * @name: a service name, encoded in ASCII. * * Looks up a service in the libtranslate service list. * * Return value: a new reference to the service with name @name, or * %NULL if not found. **/ TranslateService * translate_get_service (const char *name) { TranslateService *service = NULL; GSList *l; g_return_val_if_fail(name != NULL, NULL); G_LOCK(services); for (l = services; l != NULL && ! service; l = l->next) if (! strcmp(translate_service_get_name(l->data), name)) service = g_object_ref(l->data); G_UNLOCK(services); return service; } /** * translate_get_services: * * Gets a copy of the libtranslate service list. When no longer * needed, the list should be freed with: * * * g_slist_foreach(list, (GFunc) g_object_unref, NULL); * g_slist_free(list); * * * Return value: a copy of the libtranslate service list. **/ GSList * translate_get_services (void) { GSList *copy; G_LOCK(services); copy = g_slist_copy(services); g_slist_foreach(copy, (GFunc) g_object_ref, NULL); G_UNLOCK(services); return copy; } /** * translate_add_language: * @tag: a RFC 3066 language tag. * @name: the language human-readable name. * * Adds a language tag to name mapping to the libtranslate language * database. If a language with the same tag already exists in the * database, nothing is done and %FALSE is returned. * * * Some RFC 3066 tag to name mappings are built into libtranslate and do not * need to be added. * * * Return value: %TRUE if the language was added, %FALSE if it was * already in the database. **/ gboolean translate_add_language (const char *tag, const char *name) { gboolean added; g_return_val_if_fail(tag != NULL, FALSE); g_return_val_if_fail(name != NULL, FALSE); G_LOCK(languages); if (g_hash_table_lookup(languages, tag)) added = FALSE; else { g_hash_table_insert(languages, g_strdup(tag), g_strdup(name)); added = TRUE; } G_UNLOCK(languages); return added; } /** * translate_get_language_name: * @tag: a RFC 3066 language tag. * * Looks up a language tag in the libtranslate language database, and * returns its human-readable name. * * Return value: the human-readable name of the language with tag * @tag, or @tag itself if not found. If the returned string is not * @tag, it is owned by libtranslate and must not be modified or * freed. **/ const char * translate_get_language_name (const char *tag) { const char *name; g_return_val_if_fail(tag != NULL, NULL); G_LOCK(languages); /* * We do not return a copy of the name (less overhead and * thread-safe, because once a language is added in the hash, it * cannot be removed). */ name = g_hash_table_lookup(languages, tag); G_UNLOCK(languages); return name ? name : tag; } /** * translate_set_proxy: * @uri: the URI of a proxy server, or %NULL to unset. * * Sets the URI of a proxy server to use for network transfers. **/ void translate_set_proxy (const char *uri) { G_LOCK(proxy_uri); g_free(proxy_uri); proxy_uri = g_strdup(uri); G_UNLOCK(proxy_uri); } /** * translate_get_proxy: * * Gets the URI thas has been previously set with * translate_set_proxy(). * * If you are implementing a service, you must * use this URI for proxying network transfers. * * Return value: the libtranslate proxy URI, or %NULL if no proxy is * set. The returned string should be freed when no longer needed. **/ char * translate_get_proxy (void) { char *copy; G_LOCK(proxy_uri); copy = g_strdup(proxy_uri); G_UNLOCK(proxy_uri); return copy; }