mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 04:46:15 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1396 lines
		
	
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1396 lines
		
	
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* -*- c-basic-offset: 2 -*- */
 | 
						|
/*
 | 
						|
  Copyright(C) 2012-2017 Brazil
 | 
						|
 | 
						|
  This library is free software; you can redistribute it and/or
 | 
						|
  modify it under the terms of the GNU Lesser General Public
 | 
						|
  License version 2.1 as published by the Free Software Foundation.
 | 
						|
 | 
						|
  This library is distributed in the hope that it will be useful,
 | 
						|
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
  Lesser General Public License for more details.
 | 
						|
 | 
						|
  You should have received a copy of the GNU Lesser General Public
 | 
						|
  License along with this library; if not, write to the Free Software
 | 
						|
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335  USA
 | 
						|
*/
 | 
						|
#include "grn.h"
 | 
						|
#include "grn_ctx_impl_mrb.h"
 | 
						|
#include "grn_proc.h"
 | 
						|
#include <groonga/plugin.h>
 | 
						|
 | 
						|
#include <stdarg.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
#include <sys/stat.h>
 | 
						|
#ifdef HAVE_DIRENT_H
 | 
						|
# include <dirent.h>
 | 
						|
#endif /* HAVE_DIRENT_H */
 | 
						|
 | 
						|
#ifndef S_ISREG
 | 
						|
# ifdef _S_IFREG
 | 
						|
#  define S_ISREG(mode) (mode & _S_IFREG)
 | 
						|
# endif /* _S_IFREG */
 | 
						|
#endif /* !S_ISREG */
 | 
						|
 | 
						|
#include "grn_db.h"
 | 
						|
#include "grn_plugin.h"
 | 
						|
#include "grn_ctx_impl.h"
 | 
						|
#include "grn_util.h"
 | 
						|
 | 
						|
#ifdef GRN_WITH_MRUBY
 | 
						|
# include <mruby.h>
 | 
						|
#endif /* GRN_WITH_MRUBY */
 | 
						|
 | 
						|
static grn_hash *grn_plugins = NULL;
 | 
						|
static grn_critical_section grn_plugins_lock;
 | 
						|
static grn_ctx grn_plugins_ctx;
 | 
						|
 | 
						|
#ifdef HAVE_DLFCN_H
 | 
						|
#  include <dlfcn.h>
 | 
						|
#  define grn_dl_open(filename)      dlopen(filename, RTLD_LAZY | RTLD_LOCAL)
 | 
						|
#  define grn_dl_open_error_label()  dlerror()
 | 
						|
#  define grn_dl_close(dl)           (dlclose(dl) == 0)
 | 
						|
#  define grn_dl_close_error_label() dlerror()
 | 
						|
#  define grn_dl_sym(dl, symbol)     dlsym(dl, symbol)
 | 
						|
#  define grn_dl_sym_error_label()   dlerror()
 | 
						|
#  define grn_dl_clear_error()       dlerror()
 | 
						|
#else
 | 
						|
#  define grn_dl_open(filename)      LoadLibrary(filename)
 | 
						|
#  define grn_dl_open_error_label()  "LoadLibrary"
 | 
						|
#  define grn_dl_close(dl)           (FreeLibrary(dl) != 0)
 | 
						|
#  define grn_dl_close_error_label() "FreeLibrary"
 | 
						|
#  define grn_dl_sym(dl, symbol)     ((void *)GetProcAddress(dl, symbol))
 | 
						|
#  define grn_dl_sym_error_label()   "GetProcAddress"
 | 
						|
#  define grn_dl_clear_error()
 | 
						|
#endif
 | 
						|
 | 
						|
#define GRN_PLUGIN_KEY_SIZE(filename) (strlen((filename)) + 1)
 | 
						|
 | 
						|
static char grn_plugins_dir[GRN_ENV_BUFFER_SIZE];
 | 
						|
 | 
						|
void
 | 
						|
grn_plugin_init_from_env(void)
 | 
						|
{
 | 
						|
  grn_getenv("GRN_PLUGINS_DIR",
 | 
						|
             grn_plugins_dir,
 | 
						|
             GRN_ENV_BUFFER_SIZE);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compute_name_size(const char *name, int name_size)
 | 
						|
{
 | 
						|
  if (name_size < 0) {
 | 
						|
    if (name) {
 | 
						|
      name_size = strlen(name);
 | 
						|
    } else {
 | 
						|
      name_size = 0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return name_size;
 | 
						|
}
 | 
						|
 | 
						|
grn_id
 | 
						|
grn_plugin_reference(grn_ctx *ctx, const char *filename)
 | 
						|
{
 | 
						|
  grn_id id;
 | 
						|
  grn_plugin **plugin = NULL;
 | 
						|
 | 
						|
  CRITICAL_SECTION_ENTER(grn_plugins_lock);
 | 
						|
  id = grn_hash_get(&grn_plugins_ctx, grn_plugins,
 | 
						|
                    filename, GRN_PLUGIN_KEY_SIZE(filename),
 | 
						|
                    (void **)&plugin);
 | 
						|
  if (plugin) {
 | 
						|
    (*plugin)->refcount++;
 | 
						|
  }
 | 
						|
  CRITICAL_SECTION_LEAVE(grn_plugins_lock);
 | 
						|
 | 
						|
  return id;
 | 
						|
}
 | 
						|
 | 
						|
const char *
 | 
						|
grn_plugin_path(grn_ctx *ctx, grn_id id)
 | 
						|
{
 | 
						|
  const char *path;
 | 
						|
  grn_plugin *plugin;
 | 
						|
  int value_size;
 | 
						|
  const char *system_plugins_dir;
 | 
						|
  size_t system_plugins_dir_size;
 | 
						|
 | 
						|
  if (id == GRN_ID_NIL) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  CRITICAL_SECTION_ENTER(grn_plugins_lock);
 | 
						|
  value_size = grn_hash_get_value(&grn_plugins_ctx, grn_plugins, id, &plugin);
 | 
						|
  CRITICAL_SECTION_LEAVE(grn_plugins_lock);
 | 
						|
 | 
						|
  if (!plugin) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  path = plugin->path;
 | 
						|
  system_plugins_dir = grn_plugin_get_system_plugins_dir();
 | 
						|
  system_plugins_dir_size = strlen(system_plugins_dir);
 | 
						|
  if (strncmp(system_plugins_dir, path, system_plugins_dir_size) == 0) {
 | 
						|
    const char *plugin_name = path + system_plugins_dir_size;
 | 
						|
    while (plugin_name[0] == '/') {
 | 
						|
      plugin_name++;
 | 
						|
    }
 | 
						|
    /* TODO: remove suffix too? */
 | 
						|
    return plugin_name;
 | 
						|
  } else {
 | 
						|
    return path;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
#define GRN_PLUGIN_FUNC_PREFIX "grn_plugin_impl_"
 | 
						|
 | 
						|
static grn_rc
 | 
						|
grn_plugin_call_init(grn_ctx *ctx, grn_id id)
 | 
						|
{
 | 
						|
  grn_plugin *plugin;
 | 
						|
  int size;
 | 
						|
 | 
						|
  size = grn_hash_get_value(&grn_plugins_ctx, grn_plugins, id, &plugin);
 | 
						|
  if (size == 0) {
 | 
						|
    return GRN_INVALID_ARGUMENT;
 | 
						|
  }
 | 
						|
 | 
						|
  if (plugin->init_func) {
 | 
						|
    return plugin->init_func(ctx);
 | 
						|
  }
 | 
						|
 | 
						|
  return GRN_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef GRN_WITH_MRUBY
 | 
						|
static grn_rc
 | 
						|
grn_plugin_call_register_mrb(grn_ctx *ctx, grn_id id, grn_plugin *plugin)
 | 
						|
{
 | 
						|
  grn_mrb_data *data;
 | 
						|
  mrb_state *mrb;
 | 
						|
  struct RClass *module;
 | 
						|
  struct RClass *plugin_loader_class;
 | 
						|
  int arena_index;
 | 
						|
 | 
						|
  grn_ctx_impl_mrb_ensure_init(ctx);
 | 
						|
  if (ctx->rc != GRN_SUCCESS) {
 | 
						|
    return ctx->rc;
 | 
						|
  }
 | 
						|
 | 
						|
  data = &(ctx->impl->mrb);
 | 
						|
  mrb = data->state;
 | 
						|
  module = data->module;
 | 
						|
 | 
						|
  {
 | 
						|
    int added;
 | 
						|
    grn_hash_add(ctx, ctx->impl->mrb.registered_plugins,
 | 
						|
                 &id, sizeof(grn_id), NULL, &added);
 | 
						|
    if (!added) {
 | 
						|
      return ctx->rc;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  arena_index = mrb_gc_arena_save(mrb);
 | 
						|
  plugin_loader_class = mrb_class_get_under(mrb, module, "PluginLoader");
 | 
						|
  mrb_funcall(mrb, mrb_obj_value(plugin_loader_class),
 | 
						|
              "load_file", 1, mrb_str_new_cstr(mrb, ctx->impl->plugin_path));
 | 
						|
  mrb_gc_arena_restore(mrb, arena_index);
 | 
						|
  return ctx->rc;
 | 
						|
}
 | 
						|
#endif /*GRN_WITH_MRUBY */
 | 
						|
 | 
						|
static grn_rc
 | 
						|
grn_plugin_call_register(grn_ctx *ctx, grn_id id)
 | 
						|
{
 | 
						|
  grn_plugin *plugin;
 | 
						|
  int size;
 | 
						|
 | 
						|
  CRITICAL_SECTION_ENTER(grn_plugins_lock);
 | 
						|
  size = grn_hash_get_value(&grn_plugins_ctx, grn_plugins, id, &plugin);
 | 
						|
  CRITICAL_SECTION_LEAVE(grn_plugins_lock);
 | 
						|
 | 
						|
  if (size == 0) {
 | 
						|
    return GRN_INVALID_ARGUMENT;
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef GRN_WITH_MRUBY
 | 
						|
  if (!plugin->dl) {
 | 
						|
    return grn_plugin_call_register_mrb(ctx, id, plugin);
 | 
						|
  }
 | 
						|
#endif /* GRN_WITH_MRUBY */
 | 
						|
 | 
						|
  if (plugin->register_func) {
 | 
						|
    return plugin->register_func(ctx);
 | 
						|
  }
 | 
						|
 | 
						|
  return GRN_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
static grn_rc
 | 
						|
grn_plugin_call_fin(grn_ctx *ctx, grn_id id)
 | 
						|
{
 | 
						|
  grn_plugin *plugin;
 | 
						|
  int size;
 | 
						|
 | 
						|
  size = grn_hash_get_value(&grn_plugins_ctx, grn_plugins, id, &plugin);
 | 
						|
  if (size == 0) {
 | 
						|
    return GRN_INVALID_ARGUMENT;
 | 
						|
  }
 | 
						|
 | 
						|
  if (plugin->fin_func) {
 | 
						|
    return plugin->fin_func(ctx);
 | 
						|
  }
 | 
						|
 | 
						|
  return GRN_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
static grn_rc
 | 
						|
grn_plugin_initialize(grn_ctx *ctx, grn_plugin *plugin,
 | 
						|
                      grn_dl dl, grn_id id, const char *path)
 | 
						|
{
 | 
						|
  plugin->dl = dl;
 | 
						|
 | 
						|
#define GET_SYMBOL(type) do {                                           \
 | 
						|
  grn_dl_clear_error();                                                 \
 | 
						|
  plugin->type ## _func = grn_dl_sym(dl, GRN_PLUGIN_FUNC_PREFIX #type); \
 | 
						|
  if (!plugin->type ## _func) {                                         \
 | 
						|
    const char *label;                                                  \
 | 
						|
    label = grn_dl_sym_error_label();                                   \
 | 
						|
    SERR("%s", label);                                                  \
 | 
						|
  }                                                                     \
 | 
						|
} while (0)
 | 
						|
 | 
						|
  GET_SYMBOL(init);
 | 
						|
  GET_SYMBOL(register);
 | 
						|
  GET_SYMBOL(fin);
 | 
						|
 | 
						|
#undef GET_SYMBOL
 | 
						|
 | 
						|
  if (!plugin->init_func || !plugin->register_func || !plugin->fin_func) {
 | 
						|
    ERR(GRN_INVALID_FORMAT,
 | 
						|
        "init func (%s) %sfound, "
 | 
						|
        "register func (%s) %sfound and "
 | 
						|
        "fin func (%s) %sfound",
 | 
						|
        GRN_PLUGIN_FUNC_PREFIX "init", plugin->init_func ? "" : "not ",
 | 
						|
        GRN_PLUGIN_FUNC_PREFIX "register", plugin->register_func ? "" : "not ",
 | 
						|
        GRN_PLUGIN_FUNC_PREFIX "fin", plugin->fin_func ? "" : "not ");
 | 
						|
  }
 | 
						|
 | 
						|
  if (!ctx->rc) {
 | 
						|
    ctx->impl->plugin_path = path;
 | 
						|
    grn_plugin_call_init(ctx, id);
 | 
						|
    ctx->impl->plugin_path = NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  return ctx->rc;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef GRN_WITH_MRUBY
 | 
						|
static grn_id
 | 
						|
grn_plugin_open_mrb(grn_ctx *ctx, const char *filename, size_t filename_size)
 | 
						|
{
 | 
						|
  grn_ctx *plugins_ctx = &grn_plugins_ctx;
 | 
						|
  grn_id id = GRN_ID_NIL;
 | 
						|
  grn_plugin **plugin = NULL;
 | 
						|
 | 
						|
  grn_ctx_impl_mrb_ensure_init(ctx);
 | 
						|
  if (ctx->rc != GRN_SUCCESS) {
 | 
						|
    return GRN_ID_NIL;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!ctx->impl->mrb.state) {
 | 
						|
    ERR(GRN_FUNCTION_NOT_IMPLEMENTED, "mruby support isn't enabled");
 | 
						|
    return GRN_ID_NIL;
 | 
						|
  }
 | 
						|
 | 
						|
  id = grn_hash_add(plugins_ctx, grn_plugins, filename, filename_size,
 | 
						|
                    (void **)&plugin, NULL);
 | 
						|
  if (!id) {
 | 
						|
    return id;
 | 
						|
  }
 | 
						|
 | 
						|
  {
 | 
						|
    grn_ctx *ctx = plugins_ctx;
 | 
						|
    *plugin = GRN_MALLOCN(grn_plugin, 1);
 | 
						|
  }
 | 
						|
  if (!*plugin) {
 | 
						|
    grn_hash_delete_by_id(plugins_ctx, grn_plugins, id, NULL);
 | 
						|
    return GRN_ID_NIL;
 | 
						|
  }
 | 
						|
 | 
						|
  grn_memcpy((*plugin)->path, filename, filename_size);
 | 
						|
  (*plugin)->dl = NULL;
 | 
						|
  (*plugin)->init_func = NULL;
 | 
						|
  (*plugin)->register_func = NULL;
 | 
						|
  (*plugin)->fin_func = NULL;
 | 
						|
  (*plugin)->refcount = 1;
 | 
						|
 | 
						|
  return id;
 | 
						|
}
 | 
						|
#endif /* GRN_WITH_MRUBY */
 | 
						|
 | 
						|
grn_id
 | 
						|
grn_plugin_open(grn_ctx *ctx, const char *filename)
 | 
						|
{
 | 
						|
  grn_ctx *plugins_ctx = &grn_plugins_ctx;
 | 
						|
  grn_id id = GRN_ID_NIL;
 | 
						|
  grn_dl dl;
 | 
						|
  grn_plugin **plugin = NULL;
 | 
						|
  size_t filename_size;
 | 
						|
 | 
						|
  filename_size = GRN_PLUGIN_KEY_SIZE(filename);
 | 
						|
 | 
						|
  CRITICAL_SECTION_ENTER(grn_plugins_lock);
 | 
						|
  if ((id = grn_hash_get(plugins_ctx, grn_plugins, filename, filename_size,
 | 
						|
                         (void **)&plugin))) {
 | 
						|
    (*plugin)->refcount++;
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef GRN_WITH_MRUBY
 | 
						|
  {
 | 
						|
    const char *mrb_suffix;
 | 
						|
    mrb_suffix = grn_plugin_get_ruby_suffix();
 | 
						|
    if (filename_size > strlen(mrb_suffix) &&
 | 
						|
      strcmp(filename + (strlen(filename) - strlen(mrb_suffix)),
 | 
						|
             mrb_suffix) == 0) {
 | 
						|
      id = grn_plugin_open_mrb(ctx, filename, filename_size);
 | 
						|
      goto exit;
 | 
						|
    }
 | 
						|
  }
 | 
						|
#endif /* GRN_WITH_MRUBY */
 | 
						|
 | 
						|
  if ((dl = grn_dl_open(filename))) {
 | 
						|
    if ((id = grn_hash_add(plugins_ctx, grn_plugins, filename, filename_size,
 | 
						|
                           (void **)&plugin, NULL))) {
 | 
						|
      {
 | 
						|
        grn_ctx *ctx = plugins_ctx;
 | 
						|
        *plugin = GRN_MALLOCN(grn_plugin, 1);
 | 
						|
      }
 | 
						|
      if (*plugin) {
 | 
						|
        grn_memcpy((*plugin)->path, filename, filename_size);
 | 
						|
        if (grn_plugin_initialize(ctx, *plugin, dl, id, filename)) {
 | 
						|
          {
 | 
						|
            grn_ctx *ctx = plugins_ctx;
 | 
						|
            GRN_FREE(*plugin);
 | 
						|
          }
 | 
						|
          *plugin = NULL;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      if (!*plugin) {
 | 
						|
        grn_hash_delete_by_id(plugins_ctx, grn_plugins, id, NULL);
 | 
						|
        if (grn_dl_close(dl)) {
 | 
						|
          /* Now, __FILE__ set in plugin is invalid. */
 | 
						|
          ctx->errline = 0;
 | 
						|
          ctx->errfile = NULL;
 | 
						|
        } else {
 | 
						|
          const char *label;
 | 
						|
          label = grn_dl_close_error_label();
 | 
						|
          SERR("%s", label);
 | 
						|
        }
 | 
						|
        id = GRN_ID_NIL;
 | 
						|
      } else {
 | 
						|
        (*plugin)->refcount = 1;
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      if (!grn_dl_close(dl)) {
 | 
						|
        const char *label;
 | 
						|
        label = grn_dl_close_error_label();
 | 
						|
        SERR("%s", label);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    const char *label;
 | 
						|
    label = grn_dl_open_error_label();
 | 
						|
    SERR("%s", label);
 | 
						|
  }
 | 
						|
 | 
						|
exit:
 | 
						|
  CRITICAL_SECTION_LEAVE(grn_plugins_lock);
 | 
						|
 | 
						|
  return id;
 | 
						|
}
 | 
						|
 | 
						|
grn_rc
 | 
						|
grn_plugin_close(grn_ctx *ctx, grn_id id)
 | 
						|
{
 | 
						|
  grn_ctx *plugins_ctx = &grn_plugins_ctx;
 | 
						|
  grn_rc rc;
 | 
						|
  grn_plugin *plugin;
 | 
						|
 | 
						|
  if (id == GRN_ID_NIL) {
 | 
						|
    return GRN_INVALID_ARGUMENT;
 | 
						|
  }
 | 
						|
 | 
						|
  CRITICAL_SECTION_ENTER(grn_plugins_lock);
 | 
						|
  if (!grn_hash_get_value(plugins_ctx, grn_plugins, id, &plugin)) {
 | 
						|
    rc = GRN_INVALID_ARGUMENT;
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
  if (--plugin->refcount) {
 | 
						|
    rc = GRN_SUCCESS;
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
  if (plugin->dl) {
 | 
						|
    grn_plugin_call_fin(ctx, id);
 | 
						|
    if (!grn_dl_close(plugin->dl)) {
 | 
						|
      const char *label;
 | 
						|
      label = grn_dl_close_error_label();
 | 
						|
      SERR("%s", label);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  {
 | 
						|
    grn_ctx *ctx = plugins_ctx;
 | 
						|
    GRN_FREE(plugin);
 | 
						|
  }
 | 
						|
  rc = grn_hash_delete_by_id(plugins_ctx, grn_plugins, id, NULL);
 | 
						|
 | 
						|
exit:
 | 
						|
  CRITICAL_SECTION_LEAVE(grn_plugins_lock);
 | 
						|
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
 | 
						|
void *
 | 
						|
grn_plugin_sym(grn_ctx *ctx, grn_id id, const char *symbol)
 | 
						|
{
 | 
						|
  grn_plugin *plugin;
 | 
						|
  grn_dl_symbol func;
 | 
						|
 | 
						|
  if (id == GRN_ID_NIL) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  CRITICAL_SECTION_ENTER(grn_plugins_lock);
 | 
						|
  if (!grn_hash_get_value(&grn_plugins_ctx, grn_plugins, id, &plugin)) {
 | 
						|
    func = NULL;
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
  grn_dl_clear_error();
 | 
						|
  if (!(func = grn_dl_sym(plugin->dl, symbol))) {
 | 
						|
    const char *label;
 | 
						|
    label = grn_dl_sym_error_label();
 | 
						|
    SERR("%s", label);
 | 
						|
  }
 | 
						|
 | 
						|
exit:
 | 
						|
  CRITICAL_SECTION_LEAVE(grn_plugins_lock);
 | 
						|
 | 
						|
  return func;
 | 
						|
}
 | 
						|
 | 
						|
grn_rc
 | 
						|
grn_plugins_init(void)
 | 
						|
{
 | 
						|
  CRITICAL_SECTION_INIT(grn_plugins_lock);
 | 
						|
  grn_ctx_init(&grn_plugins_ctx, 0);
 | 
						|
  grn_plugins = grn_hash_create(&grn_plugins_ctx, NULL,
 | 
						|
                                PATH_MAX, sizeof(grn_plugin *),
 | 
						|
                                GRN_OBJ_KEY_VAR_SIZE);
 | 
						|
  if (!grn_plugins) {
 | 
						|
    grn_ctx_fin(&grn_plugins_ctx);
 | 
						|
    return GRN_NO_MEMORY_AVAILABLE;
 | 
						|
  }
 | 
						|
  return GRN_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
grn_rc
 | 
						|
grn_plugins_fin(void)
 | 
						|
{
 | 
						|
  grn_rc rc;
 | 
						|
  if (!grn_plugins) { return GRN_INVALID_ARGUMENT; }
 | 
						|
  GRN_HASH_EACH(&grn_plugins_ctx, grn_plugins, id, NULL, NULL, NULL, {
 | 
						|
    grn_plugin_close(&grn_plugins_ctx, id);
 | 
						|
  });
 | 
						|
  rc = grn_hash_close(&grn_plugins_ctx, grn_plugins);
 | 
						|
  grn_ctx_fin(&grn_plugins_ctx);
 | 
						|
  CRITICAL_SECTION_FIN(grn_plugins_lock);
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
 | 
						|
const char *
 | 
						|
grn_plugin_get_suffix(void)
 | 
						|
{
 | 
						|
  return GRN_PLUGIN_SUFFIX;
 | 
						|
}
 | 
						|
 | 
						|
const char *
 | 
						|
grn_plugin_get_ruby_suffix(void)
 | 
						|
{
 | 
						|
  return ".rb";
 | 
						|
}
 | 
						|
 | 
						|
grn_rc
 | 
						|
grn_plugin_register_by_path(grn_ctx *ctx, const char *path)
 | 
						|
{
 | 
						|
  grn_obj *db;
 | 
						|
  if (!ctx || !ctx->impl || !(db = ctx->impl->db)) {
 | 
						|
    ERR(GRN_INVALID_ARGUMENT, "db not initialized");
 | 
						|
    return ctx->rc;
 | 
						|
  }
 | 
						|
  GRN_API_ENTER;
 | 
						|
  if (GRN_DB_P(db)) {
 | 
						|
    grn_id id;
 | 
						|
    id = grn_plugin_open(ctx, path);
 | 
						|
    if (id) {
 | 
						|
      ctx->impl->plugin_path = path;
 | 
						|
      ctx->rc = grn_plugin_call_register(ctx, id);
 | 
						|
      ctx->impl->plugin_path = NULL;
 | 
						|
      grn_plugin_close(ctx, id);
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    ERR(GRN_INVALID_ARGUMENT, "invalid db assigned");
 | 
						|
  }
 | 
						|
  GRN_API_RETURN(ctx->rc);
 | 
						|
}
 | 
						|
 | 
						|
#ifdef WIN32
 | 
						|
static char *windows_plugins_dir = NULL;
 | 
						|
static char windows_plugins_dir_buffer[PATH_MAX];
 | 
						|
static const char *
 | 
						|
grn_plugin_get_default_system_plugins_dir(void)
 | 
						|
{
 | 
						|
  if (!windows_plugins_dir) {
 | 
						|
    const char *base_dir;
 | 
						|
    const char *relative_path = GRN_RELATIVE_PLUGINS_DIR;
 | 
						|
    size_t base_dir_length;
 | 
						|
 | 
						|
    base_dir = grn_windows_base_dir();
 | 
						|
    base_dir_length = strlen(base_dir);
 | 
						|
    grn_strcpy(windows_plugins_dir_buffer, PATH_MAX, base_dir);
 | 
						|
    grn_strcat(windows_plugins_dir_buffer, PATH_MAX, "/");
 | 
						|
    grn_strcat(windows_plugins_dir_buffer, PATH_MAX, relative_path);
 | 
						|
    windows_plugins_dir = windows_plugins_dir_buffer;
 | 
						|
  }
 | 
						|
  return windows_plugins_dir;
 | 
						|
}
 | 
						|
 | 
						|
#else /* WIN32 */
 | 
						|
static const char *
 | 
						|
grn_plugin_get_default_system_plugins_dir(void)
 | 
						|
{
 | 
						|
  return GRN_PLUGINS_DIR;
 | 
						|
}
 | 
						|
#endif /* WIN32 */
 | 
						|
 | 
						|
const char *
 | 
						|
grn_plugin_get_system_plugins_dir(void)
 | 
						|
{
 | 
						|
  if (grn_plugins_dir[0]) {
 | 
						|
    return grn_plugins_dir;
 | 
						|
  } else {
 | 
						|
    return grn_plugin_get_default_system_plugins_dir();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static char *
 | 
						|
grn_plugin_find_path_raw(grn_ctx *ctx, const char *path)
 | 
						|
{
 | 
						|
  struct stat path_stat;
 | 
						|
 | 
						|
  if (stat(path, &path_stat) != 0) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!S_ISREG(path_stat.st_mode)) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  return GRN_STRDUP(path);
 | 
						|
}
 | 
						|
 | 
						|
#ifdef GRN_WITH_MRUBY
 | 
						|
static char *
 | 
						|
grn_plugin_find_path_mrb(grn_ctx *ctx, const char *path, size_t path_len)
 | 
						|
{
 | 
						|
  char mrb_path[PATH_MAX];
 | 
						|
  const char *mrb_suffix;
 | 
						|
  size_t mrb_path_len;
 | 
						|
 | 
						|
  grn_ctx_impl_mrb_ensure_init(ctx);
 | 
						|
  if (ctx->rc != GRN_SUCCESS) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!ctx->impl->mrb.state) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  mrb_suffix = grn_plugin_get_ruby_suffix();
 | 
						|
  mrb_path_len = path_len + strlen(mrb_suffix);
 | 
						|
  if (mrb_path_len >= PATH_MAX) {
 | 
						|
    ERR(GRN_FILENAME_TOO_LONG,
 | 
						|
        "too long plugin path: <%s%s>",
 | 
						|
        path, mrb_suffix);
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  grn_strcpy(mrb_path, PATH_MAX, path);
 | 
						|
  grn_strcat(mrb_path, PATH_MAX, mrb_suffix);
 | 
						|
  return grn_plugin_find_path_raw(ctx, mrb_path);
 | 
						|
}
 | 
						|
#else /* GRN_WITH_MRUBY */
 | 
						|
static char *
 | 
						|
grn_plugin_find_path_mrb(grn_ctx *ctx, const char *path, size_t path_len)
 | 
						|
{
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
#endif /* GRN_WITH_MRUBY */
 | 
						|
 | 
						|
static char *
 | 
						|
grn_plugin_find_path_so(grn_ctx *ctx, const char *path, size_t path_len)
 | 
						|
{
 | 
						|
  char so_path[PATH_MAX];
 | 
						|
  const char *so_suffix;
 | 
						|
  size_t so_path_len;
 | 
						|
 | 
						|
  so_suffix = grn_plugin_get_suffix();
 | 
						|
  so_path_len = path_len + strlen(so_suffix);
 | 
						|
  if (so_path_len >= PATH_MAX) {
 | 
						|
    ERR(GRN_FILENAME_TOO_LONG,
 | 
						|
        "too long plugin path: <%s%s>",
 | 
						|
        path, so_suffix);
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  grn_strcpy(so_path, PATH_MAX, path);
 | 
						|
  grn_strcat(so_path, PATH_MAX, so_suffix);
 | 
						|
  return grn_plugin_find_path_raw(ctx, so_path);
 | 
						|
}
 | 
						|
 | 
						|
static char *
 | 
						|
grn_plugin_find_path_libs_so(grn_ctx *ctx, const char *path, size_t path_len)
 | 
						|
{
 | 
						|
  char libs_so_path[PATH_MAX];
 | 
						|
  const char *base_name;
 | 
						|
  const char *so_suffix;
 | 
						|
  const char *libs_path = "/.libs";
 | 
						|
  size_t libs_so_path_len;
 | 
						|
 | 
						|
  base_name = strrchr(path, '/');
 | 
						|
  if (!base_name) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  so_suffix = grn_plugin_get_suffix();
 | 
						|
  libs_so_path_len =
 | 
						|
    base_name - path +
 | 
						|
    strlen(libs_path) +
 | 
						|
    strlen(base_name) +
 | 
						|
    strlen(so_suffix);
 | 
						|
  if (libs_so_path_len >= PATH_MAX) {
 | 
						|
    ERR(GRN_FILENAME_TOO_LONG,
 | 
						|
        "too long plugin path: <%.*s/.libs%s%s>",
 | 
						|
        (int)(base_name - path), path, base_name, so_suffix);
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  libs_so_path[0] = '\0';
 | 
						|
  grn_strncat(libs_so_path, PATH_MAX, path, base_name - path);
 | 
						|
  grn_strcat(libs_so_path, PATH_MAX, libs_path);
 | 
						|
  grn_strcat(libs_so_path, PATH_MAX, base_name);
 | 
						|
  grn_strcat(libs_so_path, PATH_MAX, so_suffix);
 | 
						|
  return grn_plugin_find_path_raw(ctx, libs_so_path);
 | 
						|
}
 | 
						|
 | 
						|
char *
 | 
						|
grn_plugin_find_path(grn_ctx *ctx, const char *name)
 | 
						|
{
 | 
						|
  const char *plugins_dir;
 | 
						|
  char dir_last_char;
 | 
						|
  char path[PATH_MAX];
 | 
						|
  int name_length, max_name_length;
 | 
						|
  char *found_path = NULL;
 | 
						|
  size_t path_len;
 | 
						|
 | 
						|
  GRN_API_ENTER;
 | 
						|
  if (name[0] == '/') {
 | 
						|
    path[0] = '\0';
 | 
						|
  } else {
 | 
						|
    plugins_dir = grn_plugin_get_system_plugins_dir();
 | 
						|
    grn_strcpy(path, PATH_MAX, plugins_dir);
 | 
						|
 | 
						|
    dir_last_char = plugins_dir[strlen(path) - 1];
 | 
						|
    if (dir_last_char != '/') {
 | 
						|
      grn_strcat(path, PATH_MAX, "/");
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  name_length = strlen(name);
 | 
						|
  max_name_length = PATH_MAX - strlen(path) - 1;
 | 
						|
  if (name_length > max_name_length) {
 | 
						|
    ERR(GRN_INVALID_ARGUMENT,
 | 
						|
        "plugin name is too long: %d (max: %d) <%s%s>",
 | 
						|
        name_length, max_name_length,
 | 
						|
        path, name);
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
  grn_strcat(path, PATH_MAX, name);
 | 
						|
 | 
						|
  found_path = grn_plugin_find_path_raw(ctx, path);
 | 
						|
  if (found_path) {
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
 | 
						|
  path_len = strlen(path);
 | 
						|
 | 
						|
  found_path = grn_plugin_find_path_so(ctx, path, path_len);
 | 
						|
  if (found_path) {
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
  if (ctx->rc) {
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
 | 
						|
  found_path = grn_plugin_find_path_libs_so(ctx, path, path_len);
 | 
						|
  if (found_path) {
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
  if (ctx->rc) {
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
 | 
						|
  found_path = grn_plugin_find_path_mrb(ctx, path, path_len);
 | 
						|
  if (found_path) {
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
  if (ctx->rc) {
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
 | 
						|
exit :
 | 
						|
  GRN_API_RETURN(found_path);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
grn_plugin_set_name_resolve_error(grn_ctx *ctx, const char *name,
 | 
						|
                                  const char *tag)
 | 
						|
{
 | 
						|
  const char *prefix, *prefix_separator, *suffix;
 | 
						|
 | 
						|
  if (name[0] == '/') {
 | 
						|
    prefix = "";
 | 
						|
    prefix_separator = "";
 | 
						|
    suffix = "";
 | 
						|
  } else {
 | 
						|
    prefix = grn_plugin_get_system_plugins_dir();
 | 
						|
    if (prefix[strlen(prefix) - 1] != '/') {
 | 
						|
      prefix_separator = "/";
 | 
						|
    } else {
 | 
						|
      prefix_separator = "";
 | 
						|
    }
 | 
						|
    suffix = grn_plugin_get_suffix();
 | 
						|
  }
 | 
						|
  ERR(GRN_NO_SUCH_FILE_OR_DIRECTORY,
 | 
						|
      "%s cannot find plugin file: <%s%s%s%s>",
 | 
						|
      tag, prefix, prefix_separator, name, suffix);
 | 
						|
}
 | 
						|
 | 
						|
grn_rc
 | 
						|
grn_plugin_register(grn_ctx *ctx, const char *name)
 | 
						|
{
 | 
						|
  grn_rc rc;
 | 
						|
  char *path;
 | 
						|
 | 
						|
  GRN_API_ENTER;
 | 
						|
  path = grn_plugin_find_path(ctx, name);
 | 
						|
  if (path) {
 | 
						|
    rc = grn_plugin_register_by_path(ctx, path);
 | 
						|
    GRN_FREE(path);
 | 
						|
  } else {
 | 
						|
    if (ctx->rc == GRN_SUCCESS) {
 | 
						|
      grn_plugin_set_name_resolve_error(ctx, name, "[plugin][register]");
 | 
						|
    }
 | 
						|
    rc = ctx->rc;
 | 
						|
  }
 | 
						|
  GRN_API_RETURN(rc);
 | 
						|
}
 | 
						|
 | 
						|
grn_rc
 | 
						|
grn_plugin_unregister_by_path(grn_ctx *ctx, const char *path)
 | 
						|
{
 | 
						|
  grn_obj *db;
 | 
						|
  grn_id plugin_id;
 | 
						|
 | 
						|
  if (!ctx || !ctx->impl) {
 | 
						|
    ERR(GRN_INVALID_ARGUMENT, "[plugin][unregister] ctx isn't initialized");
 | 
						|
    return ctx->rc;
 | 
						|
  }
 | 
						|
 | 
						|
  db = ctx->impl->db;
 | 
						|
  if (!db) {
 | 
						|
    ERR(GRN_INVALID_ARGUMENT, "[plugin][unregister] DB isn't initialized");
 | 
						|
    return ctx->rc;
 | 
						|
  }
 | 
						|
 | 
						|
  GRN_API_ENTER;
 | 
						|
 | 
						|
  CRITICAL_SECTION_ENTER(grn_plugins_lock);
 | 
						|
  plugin_id = grn_hash_get(&grn_plugins_ctx, grn_plugins,
 | 
						|
                           path, GRN_PLUGIN_KEY_SIZE(path),
 | 
						|
                           NULL);
 | 
						|
  CRITICAL_SECTION_LEAVE(grn_plugins_lock);
 | 
						|
 | 
						|
  if (plugin_id == GRN_ID_NIL) {
 | 
						|
    GRN_API_RETURN(ctx->rc);
 | 
						|
  }
 | 
						|
 | 
						|
  {
 | 
						|
    grn_table_cursor *cursor;
 | 
						|
    grn_id id;
 | 
						|
 | 
						|
    cursor = grn_table_cursor_open(ctx, db,
 | 
						|
                                   NULL, 0,
 | 
						|
                                   NULL, 0,
 | 
						|
                                   0, -1, GRN_CURSOR_BY_ID);
 | 
						|
    if (!cursor) {
 | 
						|
      GRN_API_RETURN(ctx->rc);
 | 
						|
    }
 | 
						|
 | 
						|
    while ((id = grn_table_cursor_next(ctx, cursor))) {
 | 
						|
      grn_obj *obj;
 | 
						|
      obj = grn_ctx_at(ctx, id);
 | 
						|
      if (!obj) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      if (obj->header.type == GRN_PROC && DB_OBJ(obj)->range == plugin_id) {
 | 
						|
        grn_obj_remove(ctx, obj);
 | 
						|
      } else {
 | 
						|
        grn_obj_unlink(ctx, obj);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    grn_table_cursor_close(ctx, cursor);
 | 
						|
  }
 | 
						|
 | 
						|
  GRN_API_RETURN(ctx->rc);
 | 
						|
}
 | 
						|
 | 
						|
grn_rc
 | 
						|
grn_plugin_unregister(grn_ctx *ctx, const char *name)
 | 
						|
{
 | 
						|
  grn_rc rc;
 | 
						|
  char *path;
 | 
						|
 | 
						|
  GRN_API_ENTER;
 | 
						|
  path = grn_plugin_find_path(ctx, name);
 | 
						|
  if (path) {
 | 
						|
    rc = grn_plugin_unregister_by_path(ctx, path);
 | 
						|
    GRN_FREE(path);
 | 
						|
  } else {
 | 
						|
    if (ctx->rc == GRN_SUCCESS) {
 | 
						|
      grn_plugin_set_name_resolve_error(ctx, name, "[plugin][unregister]");
 | 
						|
    }
 | 
						|
    rc = ctx->rc;
 | 
						|
  }
 | 
						|
  GRN_API_RETURN(rc);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
grn_plugin_ensure_registered(grn_ctx *ctx, grn_obj *proc)
 | 
						|
{
 | 
						|
#ifdef GRN_WITH_MRUBY
 | 
						|
  grn_id plugin_id;
 | 
						|
  grn_plugin *plugin = NULL;
 | 
						|
 | 
						|
  if (!(proc->header.flags & GRN_OBJ_CUSTOM_NAME)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  plugin_id = DB_OBJ(proc)->range;
 | 
						|
  CRITICAL_SECTION_ENTER(grn_plugins_lock);
 | 
						|
  {
 | 
						|
    const char *value;
 | 
						|
    value = grn_hash_get_value_(&grn_plugins_ctx, grn_plugins, plugin_id, NULL);
 | 
						|
    if (value) {
 | 
						|
      plugin = *((grn_plugin **)value);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CRITICAL_SECTION_LEAVE(grn_plugins_lock);
 | 
						|
 | 
						|
  if (!plugin) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (plugin->dl) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  grn_ctx_impl_mrb_ensure_init(ctx);
 | 
						|
  if (ctx->rc != GRN_SUCCESS) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!ctx->impl->mrb.state) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  {
 | 
						|
    grn_id id;
 | 
						|
    int added;
 | 
						|
    id = DB_OBJ(proc)->id;
 | 
						|
    grn_hash_add(ctx, ctx->impl->mrb.checked_procs,
 | 
						|
                 &id, sizeof(grn_id), NULL, &added);
 | 
						|
    if (!added) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  ctx->impl->plugin_path = plugin->path;
 | 
						|
  grn_plugin_call_register_mrb(ctx, plugin_id, plugin);
 | 
						|
  ctx->impl->plugin_path = NULL;
 | 
						|
#endif /* GRN_WITH_MRUBY */
 | 
						|
}
 | 
						|
 | 
						|
grn_rc
 | 
						|
grn_plugin_get_names(grn_ctx *ctx, grn_obj *names)
 | 
						|
{
 | 
						|
  grn_hash *processed_paths;
 | 
						|
  const char *system_plugins_dir;
 | 
						|
  const char *native_plugin_suffix;
 | 
						|
  const char *ruby_plugin_suffix;
 | 
						|
  grn_bool is_close_opened_object_mode = GRN_FALSE;
 | 
						|
 | 
						|
  GRN_API_ENTER;
 | 
						|
 | 
						|
  if (ctx->rc) {
 | 
						|
    GRN_API_RETURN(ctx->rc);
 | 
						|
  }
 | 
						|
 | 
						|
  if (grn_thread_get_limit() == 1) {
 | 
						|
    is_close_opened_object_mode = GRN_TRUE;
 | 
						|
  }
 | 
						|
 | 
						|
  processed_paths = grn_hash_create(ctx, NULL, GRN_TABLE_MAX_KEY_SIZE, 0,
 | 
						|
                                    GRN_OBJ_TABLE_HASH_KEY |
 | 
						|
                                    GRN_OBJ_KEY_VAR_SIZE);
 | 
						|
  if (!processed_paths) {
 | 
						|
    GRN_API_RETURN(ctx->rc);
 | 
						|
  }
 | 
						|
 | 
						|
  system_plugins_dir = grn_plugin_get_system_plugins_dir();
 | 
						|
  native_plugin_suffix = grn_plugin_get_suffix();
 | 
						|
  ruby_plugin_suffix = grn_plugin_get_ruby_suffix();
 | 
						|
 | 
						|
  GRN_TABLE_EACH_BEGIN_FLAGS(ctx, grn_ctx_db(ctx), cursor, id,
 | 
						|
                             GRN_CURSOR_BY_ID | GRN_CURSOR_ASCENDING) {
 | 
						|
    void *name;
 | 
						|
    int name_size;
 | 
						|
    grn_obj *object;
 | 
						|
    const char *path;
 | 
						|
    grn_id processed_path_id;
 | 
						|
 | 
						|
    if (grn_id_is_builtin(ctx, id)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    name_size = grn_table_cursor_get_key(ctx, cursor, &name);
 | 
						|
    if (grn_obj_name_is_column(ctx, name, name_size)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (is_close_opened_object_mode) {
 | 
						|
      grn_ctx_push_temporary_open_space(ctx);
 | 
						|
    }
 | 
						|
 | 
						|
    object = grn_ctx_at(ctx, id);
 | 
						|
    if (!object) {
 | 
						|
      ERRCLR(ctx);
 | 
						|
      goto next_loop;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!grn_obj_is_proc(ctx, object)) {
 | 
						|
      goto next_loop;
 | 
						|
    }
 | 
						|
 | 
						|
    path = grn_obj_path(ctx, object);
 | 
						|
    if (!path) {
 | 
						|
      goto next_loop;
 | 
						|
    }
 | 
						|
 | 
						|
    processed_path_id = grn_hash_get(ctx, processed_paths,
 | 
						|
                                     path, strlen(path),
 | 
						|
                                     NULL);
 | 
						|
    if (processed_path_id != GRN_ID_NIL) {
 | 
						|
      goto next_loop;
 | 
						|
    }
 | 
						|
 | 
						|
    grn_hash_add(ctx, processed_paths,
 | 
						|
                 path, strlen(path),
 | 
						|
                 NULL, NULL);
 | 
						|
 | 
						|
    {
 | 
						|
      const char *relative_path;
 | 
						|
      const char *libs_path = "/.libs/";
 | 
						|
      const char *start_libs;
 | 
						|
      char name[PATH_MAX];
 | 
						|
 | 
						|
      name[0] = '\0';
 | 
						|
      if (strncmp(path, system_plugins_dir, strlen(system_plugins_dir)) == 0) {
 | 
						|
        relative_path = path + strlen(system_plugins_dir);
 | 
						|
      } else {
 | 
						|
        relative_path = path;
 | 
						|
      }
 | 
						|
      start_libs = strstr(relative_path, libs_path);
 | 
						|
      if (start_libs) {
 | 
						|
        grn_strncat(name, PATH_MAX, relative_path, start_libs - relative_path);
 | 
						|
        grn_strcat(name, PATH_MAX, "/");
 | 
						|
        grn_strcat(name, PATH_MAX, start_libs + strlen(libs_path));
 | 
						|
      } else {
 | 
						|
        grn_strcat(name, PATH_MAX, relative_path);
 | 
						|
      }
 | 
						|
      if (strlen(name) > strlen(native_plugin_suffix) &&
 | 
						|
          strcmp(name + strlen(name) - strlen(native_plugin_suffix),
 | 
						|
                 native_plugin_suffix) == 0) {
 | 
						|
        name[strlen(name) - strlen(native_plugin_suffix)] = '\0';
 | 
						|
      } else if (strlen(name) > strlen(ruby_plugin_suffix) &&
 | 
						|
                 strcmp(name + strlen(name) - strlen(ruby_plugin_suffix),
 | 
						|
                        ruby_plugin_suffix) == 0) {
 | 
						|
        name[strlen(name) - strlen(ruby_plugin_suffix)] = '\0';
 | 
						|
      }
 | 
						|
      grn_vector_add_element(ctx, names,
 | 
						|
                             name, strlen(name),
 | 
						|
                             0, GRN_DB_TEXT);
 | 
						|
    }
 | 
						|
 | 
						|
  next_loop :
 | 
						|
    if (is_close_opened_object_mode) {
 | 
						|
      grn_ctx_pop_temporary_open_space(ctx);
 | 
						|
    }
 | 
						|
  } GRN_TABLE_EACH_END(ctx, cursor);
 | 
						|
 | 
						|
  grn_hash_close(ctx, processed_paths);
 | 
						|
 | 
						|
  GRN_API_RETURN(ctx->rc);
 | 
						|
}
 | 
						|
 | 
						|
void *
 | 
						|
grn_plugin_malloc(grn_ctx *ctx, size_t size, const char *file, int line,
 | 
						|
                  const char *func)
 | 
						|
{
 | 
						|
  return grn_malloc(ctx, size, file, line, func);
 | 
						|
}
 | 
						|
 | 
						|
void *
 | 
						|
grn_plugin_calloc(grn_ctx *ctx, size_t size, const char *file, int line,
 | 
						|
                  const char *func)
 | 
						|
{
 | 
						|
  return grn_calloc(ctx, size, file, line, func);
 | 
						|
}
 | 
						|
 | 
						|
void *
 | 
						|
grn_plugin_realloc(grn_ctx *ctx, void *ptr, size_t size,
 | 
						|
                   const char *file, int line, const char *func)
 | 
						|
{
 | 
						|
  return grn_realloc(ctx, ptr, size, file, line, func);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
grn_plugin_free(grn_ctx *ctx, void *ptr, const char *file, int line,
 | 
						|
                const char *func)
 | 
						|
{
 | 
						|
  grn_free(ctx, ptr, file, line, func);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
grn_plugin_set_error(grn_ctx *ctx, grn_log_level level, grn_rc error_code,
 | 
						|
                     const char *file, int line, const char *func,
 | 
						|
                     const char *format, ...)
 | 
						|
{
 | 
						|
  char old_error_message[GRN_CTX_MSGSIZE];
 | 
						|
 | 
						|
  ctx->errlvl = level;
 | 
						|
  ctx->rc = error_code;
 | 
						|
  ctx->errfile = file;
 | 
						|
  ctx->errline = line;
 | 
						|
  ctx->errfunc = func;
 | 
						|
 | 
						|
  grn_strcpy(old_error_message, GRN_CTX_MSGSIZE, ctx->errbuf);
 | 
						|
 | 
						|
  {
 | 
						|
    va_list ap;
 | 
						|
    va_start(ap, format);
 | 
						|
    grn_ctx_logv(ctx, format, ap);
 | 
						|
    va_end(ap);
 | 
						|
  }
 | 
						|
 | 
						|
  if (grn_ctx_impl_should_log(ctx)) {
 | 
						|
    grn_ctx_impl_set_current_error_message(ctx);
 | 
						|
    if (grn_logger_pass(ctx, level)) {
 | 
						|
      char new_error_message[GRN_CTX_MSGSIZE];
 | 
						|
      grn_strcpy(new_error_message, GRN_CTX_MSGSIZE, ctx->errbuf);
 | 
						|
      grn_strcpy(ctx->errbuf, GRN_CTX_MSGSIZE, old_error_message);
 | 
						|
      {
 | 
						|
        va_list ap;
 | 
						|
        va_start(ap, format);
 | 
						|
        grn_logger_putv(ctx, level, file, line, func, format, ap);
 | 
						|
        va_end(ap);
 | 
						|
      }
 | 
						|
      grn_strcpy(ctx->errbuf, GRN_CTX_MSGSIZE, new_error_message);
 | 
						|
    }
 | 
						|
    if (level <= GRN_LOG_ERROR) {
 | 
						|
      grn_plugin_logtrace(ctx, level);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
grn_plugin_clear_error(grn_ctx *ctx)
 | 
						|
{
 | 
						|
  ERRCLR(ctx);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
grn_plugin_backtrace(grn_ctx *ctx)
 | 
						|
{
 | 
						|
  BACKTRACE(ctx);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
grn_plugin_logtrace(grn_ctx *ctx, grn_log_level level)
 | 
						|
{
 | 
						|
  if (level <= GRN_LOG_ERROR) {
 | 
						|
    grn_plugin_backtrace(ctx);
 | 
						|
    LOGTRACE(ctx, level);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
struct _grn_plugin_mutex {
 | 
						|
  grn_critical_section critical_section;
 | 
						|
};
 | 
						|
 | 
						|
grn_plugin_mutex *
 | 
						|
grn_plugin_mutex_open(grn_ctx *ctx)
 | 
						|
{
 | 
						|
  grn_plugin_mutex * const mutex =
 | 
						|
      GRN_PLUGIN_MALLOC(ctx, sizeof(grn_plugin_mutex));
 | 
						|
  if (mutex != NULL) {
 | 
						|
    CRITICAL_SECTION_INIT(mutex->critical_section);
 | 
						|
  }
 | 
						|
  return mutex;
 | 
						|
}
 | 
						|
 | 
						|
grn_plugin_mutex *
 | 
						|
grn_plugin_mutex_create(grn_ctx *ctx)
 | 
						|
{
 | 
						|
  return grn_plugin_mutex_open(ctx);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
grn_plugin_mutex_close(grn_ctx *ctx, grn_plugin_mutex *mutex)
 | 
						|
{
 | 
						|
  if (mutex != NULL) {
 | 
						|
    CRITICAL_SECTION_FIN(mutex->critical_section);
 | 
						|
    GRN_PLUGIN_FREE(ctx, mutex);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
grn_plugin_mutex_destroy(grn_ctx *ctx, grn_plugin_mutex *mutex)
 | 
						|
{
 | 
						|
  grn_plugin_mutex_close(ctx, mutex);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
grn_plugin_mutex_lock(grn_ctx *ctx, grn_plugin_mutex *mutex)
 | 
						|
{
 | 
						|
  if (mutex != NULL) {
 | 
						|
    CRITICAL_SECTION_ENTER(mutex->critical_section);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
grn_plugin_mutex_unlock(grn_ctx *ctx, grn_plugin_mutex *mutex)
 | 
						|
{
 | 
						|
  if (mutex != NULL) {
 | 
						|
    CRITICAL_SECTION_LEAVE(mutex->critical_section);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
grn_obj *
 | 
						|
grn_plugin_proc_alloc(grn_ctx *ctx, grn_user_data *user_data,
 | 
						|
                      grn_id domain, unsigned char flags)
 | 
						|
{
 | 
						|
  return grn_proc_alloc(ctx, user_data, domain, flags);
 | 
						|
}
 | 
						|
 | 
						|
grn_obj *
 | 
						|
grn_plugin_proc_get_vars(grn_ctx *ctx, grn_user_data *user_data)
 | 
						|
{
 | 
						|
  return grn_proc_get_vars(ctx, user_data);
 | 
						|
}
 | 
						|
 | 
						|
grn_obj *
 | 
						|
grn_plugin_proc_get_var(grn_ctx *ctx, grn_user_data *user_data,
 | 
						|
                        const char *name, int name_size)
 | 
						|
{
 | 
						|
  name_size = compute_name_size(name, name_size);
 | 
						|
  return grn_proc_get_var(ctx, user_data, name, name_size);
 | 
						|
}
 | 
						|
 | 
						|
grn_bool
 | 
						|
grn_plugin_proc_get_var_bool(grn_ctx *ctx,
 | 
						|
                             grn_user_data *user_data,
 | 
						|
                             const char *name,
 | 
						|
                             int name_size,
 | 
						|
                             grn_bool default_value)
 | 
						|
{
 | 
						|
  grn_obj *var;
 | 
						|
 | 
						|
  var = grn_plugin_proc_get_var(ctx, user_data, name, name_size);
 | 
						|
  return grn_proc_option_value_bool(ctx, var, default_value);
 | 
						|
}
 | 
						|
 | 
						|
int32_t
 | 
						|
grn_plugin_proc_get_var_int32(grn_ctx *ctx,
 | 
						|
                              grn_user_data *user_data,
 | 
						|
                              const char *name,
 | 
						|
                              int name_size,
 | 
						|
                              int32_t default_value)
 | 
						|
{
 | 
						|
  grn_obj *var;
 | 
						|
 | 
						|
  var = grn_plugin_proc_get_var(ctx, user_data, name, name_size);
 | 
						|
  return grn_proc_option_value_int32(ctx, var, default_value);
 | 
						|
}
 | 
						|
 | 
						|
const char *
 | 
						|
grn_plugin_proc_get_var_string(grn_ctx *ctx,
 | 
						|
                               grn_user_data *user_data,
 | 
						|
                               const char *name,
 | 
						|
                               int name_size,
 | 
						|
                               size_t *size)
 | 
						|
{
 | 
						|
  grn_obj *var;
 | 
						|
 | 
						|
  var = grn_plugin_proc_get_var(ctx, user_data, name, name_size);
 | 
						|
  return grn_proc_option_value_string(ctx, var, size);
 | 
						|
}
 | 
						|
 | 
						|
grn_content_type
 | 
						|
grn_plugin_proc_get_var_content_type(grn_ctx *ctx,
 | 
						|
                                     grn_user_data *user_data,
 | 
						|
                                     const char *name,
 | 
						|
                                     int name_size,
 | 
						|
                                     grn_content_type default_value)
 | 
						|
{
 | 
						|
  grn_obj *var;
 | 
						|
 | 
						|
  var = grn_plugin_proc_get_var(ctx, user_data, name, name_size);
 | 
						|
  return grn_proc_option_value_content_type(ctx, var, default_value);
 | 
						|
}
 | 
						|
 | 
						|
grn_obj *
 | 
						|
grn_plugin_proc_get_var_by_offset(grn_ctx *ctx, grn_user_data *user_data,
 | 
						|
                                  unsigned int offset)
 | 
						|
{
 | 
						|
  return grn_proc_get_var_by_offset(ctx, user_data, offset);
 | 
						|
}
 | 
						|
 | 
						|
grn_obj *
 | 
						|
grn_plugin_proc_get_caller(grn_ctx *ctx, grn_user_data *user_data)
 | 
						|
{
 | 
						|
  grn_obj *caller = NULL;
 | 
						|
  GRN_API_ENTER;
 | 
						|
  grn_proc_get_info(ctx, user_data, NULL, NULL, &caller);
 | 
						|
  GRN_API_RETURN(caller);
 | 
						|
}
 | 
						|
 | 
						|
const char *
 | 
						|
grn_plugin_win32_base_dir(void)
 | 
						|
{
 | 
						|
  return grn_plugin_windows_base_dir();
 | 
						|
}
 | 
						|
 | 
						|
const char *
 | 
						|
grn_plugin_windows_base_dir(void)
 | 
						|
{
 | 
						|
#ifdef WIN32
 | 
						|
  return grn_windows_base_dir();
 | 
						|
#else /* WIN32 */
 | 
						|
  return NULL;
 | 
						|
#endif /* WIN32 */
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  grn_plugin_charlen() takes the length of a string, unlike grn_charlen_().
 | 
						|
 */
 | 
						|
int
 | 
						|
grn_plugin_charlen(grn_ctx *ctx, const char *str_ptr,
 | 
						|
                   unsigned int str_length, grn_encoding encoding)
 | 
						|
{
 | 
						|
  return grn_charlen_(ctx, str_ptr, str_ptr + str_length, encoding);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  grn_plugin_isspace() takes the length of a string, unlike grn_isspace().
 | 
						|
 */
 | 
						|
int
 | 
						|
grn_plugin_isspace(grn_ctx *ctx, const char *str_ptr,
 | 
						|
                   unsigned int str_length, grn_encoding encoding)
 | 
						|
{
 | 
						|
  if ((str_ptr == NULL) || (str_length == 0)) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  switch ((unsigned char)str_ptr[0]) {
 | 
						|
  case ' ' :
 | 
						|
  case '\f' :
 | 
						|
  case '\n' :
 | 
						|
  case '\r' :
 | 
						|
  case '\t' :
 | 
						|
  case '\v' :
 | 
						|
    return 1;
 | 
						|
  case 0x81 :
 | 
						|
    if ((encoding == GRN_ENC_SJIS) && (str_length >= 2) &&
 | 
						|
        ((unsigned char)str_ptr[1] == 0x40)) {
 | 
						|
      return 2;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  case 0xA1 :
 | 
						|
    if ((encoding == GRN_ENC_EUC_JP) && (str_length >= 2) &&
 | 
						|
        ((unsigned char)str_ptr[1] == 0xA1)) {
 | 
						|
      return 2;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  case 0xE3 :
 | 
						|
    if ((encoding == GRN_ENC_UTF8) && (str_length >= 3) &&
 | 
						|
        ((unsigned char)str_ptr[1] == 0x80) &&
 | 
						|
        ((unsigned char)str_ptr[2] == 0x80)) {
 | 
						|
      return 3;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  default :
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
grn_rc
 | 
						|
grn_plugin_expr_var_init(grn_ctx *ctx,
 | 
						|
                         grn_expr_var *var,
 | 
						|
                         const char *name,
 | 
						|
                         int name_size)
 | 
						|
{
 | 
						|
  var->name = name;
 | 
						|
  var->name_size = compute_name_size(name, name_size);
 | 
						|
  GRN_TEXT_INIT(&var->value, 0);
 | 
						|
  return GRN_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
grn_obj *
 | 
						|
grn_plugin_command_create(grn_ctx *ctx,
 | 
						|
                          const char *name,
 | 
						|
                          int name_size,
 | 
						|
                          grn_proc_func func,
 | 
						|
                          unsigned int n_vars,
 | 
						|
                          grn_expr_var *vars)
 | 
						|
{
 | 
						|
  grn_obj *proc;
 | 
						|
  name_size = compute_name_size(name, name_size);
 | 
						|
  proc = grn_proc_create(ctx, name, name_size, GRN_PROC_COMMAND,
 | 
						|
                         func, NULL, NULL, n_vars, vars);
 | 
						|
  return proc;
 | 
						|
}
 |