1 /* -*- c-basic-offset: 2 -*- */
2 /*
3   Copyright(C) 2012-2017 Brazil
4 
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License version 2.1 as published by the Free Software Foundation.
8 
9   This library is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   Lesser General Public License for more details.
13 
14   You should have received a copy of the GNU Lesser General Public
15   License along with this library; if not, write to the Free Software
16   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335  USA
17 */
18 #include "grn.h"
19 #include "grn_ctx_impl_mrb.h"
20 #include "grn_proc.h"
21 #include <groonga/plugin.h>
22 
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <string.h>
26 
27 #include <sys/stat.h>
28 #ifdef HAVE_DIRENT_H
29 # include <dirent.h>
30 #endif /* HAVE_DIRENT_H */
31 
32 #ifndef S_ISREG
33 # ifdef _S_IFREG
34 #  define S_ISREG(mode) (mode & _S_IFREG)
35 # endif /* _S_IFREG */
36 #endif /* !S_ISREG */
37 
38 #include "grn_db.h"
39 #include "grn_plugin.h"
40 #include "grn_ctx_impl.h"
41 #include "grn_util.h"
42 
43 #ifdef GRN_WITH_MRUBY
44 # include <mruby.h>
45 #endif /* GRN_WITH_MRUBY */
46 
47 static grn_hash *grn_plugins = NULL;
48 static grn_critical_section grn_plugins_lock;
49 static grn_ctx grn_plugins_ctx;
50 
51 #ifdef HAVE_DLFCN_H
52 #  include <dlfcn.h>
53 #  define grn_dl_open(filename)      dlopen(filename, RTLD_LAZY | RTLD_LOCAL)
54 #  define grn_dl_open_error_label()  dlerror()
55 #  define grn_dl_close(dl)           (dlclose(dl) == 0)
56 #  define grn_dl_close_error_label() dlerror()
57 #  define grn_dl_sym(dl, symbol)     dlsym(dl, symbol)
58 #  define grn_dl_sym_error_label()   dlerror()
59 #  define grn_dl_clear_error()       dlerror()
60 #else
61 #  define grn_dl_open(filename)      LoadLibrary(filename)
62 #  define grn_dl_open_error_label()  "LoadLibrary"
63 #  define grn_dl_close(dl)           (FreeLibrary(dl) != 0)
64 #  define grn_dl_close_error_label() "FreeLibrary"
65 #  define grn_dl_sym(dl, symbol)     ((void *)GetProcAddress(dl, symbol))
66 #  define grn_dl_sym_error_label()   "GetProcAddress"
67 #  define grn_dl_clear_error()
68 #endif
69 
70 #define GRN_PLUGIN_KEY_SIZE(filename) (strlen((filename)) + 1)
71 
72 static char grn_plugins_dir[GRN_ENV_BUFFER_SIZE];
73 
74 void
grn_plugin_init_from_env(void)75 grn_plugin_init_from_env(void)
76 {
77   grn_getenv("GRN_PLUGINS_DIR",
78              grn_plugins_dir,
79              GRN_ENV_BUFFER_SIZE);
80 }
81 
82 static int
compute_name_size(const char * name,int name_size)83 compute_name_size(const char *name, int name_size)
84 {
85   if (name_size < 0) {
86     if (name) {
87       name_size = strlen(name);
88     } else {
89       name_size = 0;
90     }
91   }
92   return name_size;
93 }
94 
95 grn_id
grn_plugin_reference(grn_ctx * ctx,const char * filename)96 grn_plugin_reference(grn_ctx *ctx, const char *filename)
97 {
98   grn_id id;
99   grn_plugin **plugin = NULL;
100 
101   CRITICAL_SECTION_ENTER(grn_plugins_lock);
102   id = grn_hash_get(&grn_plugins_ctx, grn_plugins,
103                     filename, GRN_PLUGIN_KEY_SIZE(filename),
104                     (void **)&plugin);
105   if (plugin) {
106     (*plugin)->refcount++;
107   }
108   CRITICAL_SECTION_LEAVE(grn_plugins_lock);
109 
110   return id;
111 }
112 
113 const char *
grn_plugin_path(grn_ctx * ctx,grn_id id)114 grn_plugin_path(grn_ctx *ctx, grn_id id)
115 {
116   const char *path;
117   grn_plugin *plugin;
118   int value_size;
119   const char *system_plugins_dir;
120   size_t system_plugins_dir_size;
121 
122   if (id == GRN_ID_NIL) {
123     return NULL;
124   }
125 
126   CRITICAL_SECTION_ENTER(grn_plugins_lock);
127   value_size = grn_hash_get_value(&grn_plugins_ctx, grn_plugins, id, &plugin);
128   CRITICAL_SECTION_LEAVE(grn_plugins_lock);
129 
130   if (!plugin) {
131     return NULL;
132   }
133 
134   path = plugin->path;
135   system_plugins_dir = grn_plugin_get_system_plugins_dir();
136   system_plugins_dir_size = strlen(system_plugins_dir);
137   if (strncmp(system_plugins_dir, path, system_plugins_dir_size) == 0) {
138     const char *plugin_name = path + system_plugins_dir_size;
139     while (plugin_name[0] == '/') {
140       plugin_name++;
141     }
142     /* TODO: remove suffix too? */
143     return plugin_name;
144   } else {
145     return path;
146   }
147 }
148 
149 #define GRN_PLUGIN_FUNC_PREFIX "grn_plugin_impl_"
150 
151 static grn_rc
grn_plugin_call_init(grn_ctx * ctx,grn_id id)152 grn_plugin_call_init(grn_ctx *ctx, grn_id id)
153 {
154   grn_plugin *plugin;
155   int size;
156 
157   size = grn_hash_get_value(&grn_plugins_ctx, grn_plugins, id, &plugin);
158   if (size == 0) {
159     return GRN_INVALID_ARGUMENT;
160   }
161 
162   if (plugin->init_func) {
163     return plugin->init_func(ctx);
164   }
165 
166   return GRN_SUCCESS;
167 }
168 
169 #ifdef GRN_WITH_MRUBY
170 static grn_rc
grn_plugin_call_register_mrb(grn_ctx * ctx,grn_id id,grn_plugin * plugin)171 grn_plugin_call_register_mrb(grn_ctx *ctx, grn_id id, grn_plugin *plugin)
172 {
173   grn_mrb_data *data;
174   mrb_state *mrb;
175   struct RClass *module;
176   struct RClass *plugin_loader_class;
177   int arena_index;
178 
179   grn_ctx_impl_mrb_ensure_init(ctx);
180   if (ctx->rc != GRN_SUCCESS) {
181     return ctx->rc;
182   }
183 
184   data = &(ctx->impl->mrb);
185   mrb = data->state;
186   module = data->module;
187 
188   {
189     int added;
190     grn_hash_add(ctx, ctx->impl->mrb.registered_plugins,
191                  &id, sizeof(grn_id), NULL, &added);
192     if (!added) {
193       return ctx->rc;
194     }
195   }
196 
197   arena_index = mrb_gc_arena_save(mrb);
198   plugin_loader_class = mrb_class_get_under(mrb, module, "PluginLoader");
199   mrb_funcall(mrb, mrb_obj_value(plugin_loader_class),
200               "load_file", 1, mrb_str_new_cstr(mrb, ctx->impl->plugin_path));
201   mrb_gc_arena_restore(mrb, arena_index);
202   return ctx->rc;
203 }
204 #endif /*GRN_WITH_MRUBY */
205 
206 static grn_rc
grn_plugin_call_register(grn_ctx * ctx,grn_id id)207 grn_plugin_call_register(grn_ctx *ctx, grn_id id)
208 {
209   grn_plugin *plugin;
210   int size;
211 
212   CRITICAL_SECTION_ENTER(grn_plugins_lock);
213   size = grn_hash_get_value(&grn_plugins_ctx, grn_plugins, id, &plugin);
214   CRITICAL_SECTION_LEAVE(grn_plugins_lock);
215 
216   if (size == 0) {
217     return GRN_INVALID_ARGUMENT;
218   }
219 
220 #ifdef GRN_WITH_MRUBY
221   if (!plugin->dl) {
222     return grn_plugin_call_register_mrb(ctx, id, plugin);
223   }
224 #endif /* GRN_WITH_MRUBY */
225 
226   if (plugin->register_func) {
227     return plugin->register_func(ctx);
228   }
229 
230   return GRN_SUCCESS;
231 }
232 
233 static grn_rc
grn_plugin_call_fin(grn_ctx * ctx,grn_id id)234 grn_plugin_call_fin(grn_ctx *ctx, grn_id id)
235 {
236   grn_plugin *plugin;
237   int size;
238 
239   size = grn_hash_get_value(&grn_plugins_ctx, grn_plugins, id, &plugin);
240   if (size == 0) {
241     return GRN_INVALID_ARGUMENT;
242   }
243 
244   if (plugin->fin_func) {
245     return plugin->fin_func(ctx);
246   }
247 
248   return GRN_SUCCESS;
249 }
250 
251 static grn_rc
grn_plugin_initialize(grn_ctx * ctx,grn_plugin * plugin,grn_dl dl,grn_id id,const char * path)252 grn_plugin_initialize(grn_ctx *ctx, grn_plugin *plugin,
253                       grn_dl dl, grn_id id, const char *path)
254 {
255   plugin->dl = dl;
256 
257 #define GET_SYMBOL(type) do {                                           \
258   grn_dl_clear_error();                                                 \
259   plugin->type ## _func = grn_dl_sym(dl, GRN_PLUGIN_FUNC_PREFIX #type); \
260   if (!plugin->type ## _func) {                                         \
261     const char *label;                                                  \
262     label = grn_dl_sym_error_label();                                   \
263     SERR("%s", label);                                                  \
264   }                                                                     \
265 } while (0)
266 
267   GET_SYMBOL(init);
268   GET_SYMBOL(register);
269   GET_SYMBOL(fin);
270 
271 #undef GET_SYMBOL
272 
273   if (!plugin->init_func || !plugin->register_func || !plugin->fin_func) {
274     ERR(GRN_INVALID_FORMAT,
275         "init func (%s) %sfound, "
276         "register func (%s) %sfound and "
277         "fin func (%s) %sfound",
278         GRN_PLUGIN_FUNC_PREFIX "init", plugin->init_func ? "" : "not ",
279         GRN_PLUGIN_FUNC_PREFIX "register", plugin->register_func ? "" : "not ",
280         GRN_PLUGIN_FUNC_PREFIX "fin", plugin->fin_func ? "" : "not ");
281   }
282 
283   if (!ctx->rc) {
284     ctx->impl->plugin_path = path;
285     grn_plugin_call_init(ctx, id);
286     ctx->impl->plugin_path = NULL;
287   }
288 
289   return ctx->rc;
290 }
291 
292 #ifdef GRN_WITH_MRUBY
293 static grn_id
grn_plugin_open_mrb(grn_ctx * ctx,const char * filename,size_t filename_size)294 grn_plugin_open_mrb(grn_ctx *ctx, const char *filename, size_t filename_size)
295 {
296   grn_ctx *plugins_ctx = &grn_plugins_ctx;
297   grn_id id = GRN_ID_NIL;
298   grn_plugin **plugin = NULL;
299 
300   grn_ctx_impl_mrb_ensure_init(ctx);
301   if (ctx->rc != GRN_SUCCESS) {
302     return GRN_ID_NIL;
303   }
304 
305   if (!ctx->impl->mrb.state) {
306     ERR(GRN_FUNCTION_NOT_IMPLEMENTED, "mruby support isn't enabled");
307     return GRN_ID_NIL;
308   }
309 
310   id = grn_hash_add(plugins_ctx, grn_plugins, filename, filename_size,
311                     (void **)&plugin, NULL);
312   if (!id) {
313     return id;
314   }
315 
316   {
317     grn_ctx *ctx = plugins_ctx;
318     *plugin = GRN_MALLOCN(grn_plugin, 1);
319   }
320   if (!*plugin) {
321     grn_hash_delete_by_id(plugins_ctx, grn_plugins, id, NULL);
322     return GRN_ID_NIL;
323   }
324 
325   grn_memcpy((*plugin)->path, filename, filename_size);
326   (*plugin)->dl = NULL;
327   (*plugin)->init_func = NULL;
328   (*plugin)->register_func = NULL;
329   (*plugin)->fin_func = NULL;
330   (*plugin)->refcount = 1;
331 
332   return id;
333 }
334 #endif /* GRN_WITH_MRUBY */
335 
336 grn_id
grn_plugin_open(grn_ctx * ctx,const char * filename)337 grn_plugin_open(grn_ctx *ctx, const char *filename)
338 {
339   grn_ctx *plugins_ctx = &grn_plugins_ctx;
340   grn_id id = GRN_ID_NIL;
341   grn_dl dl;
342   grn_plugin **plugin = NULL;
343   size_t filename_size;
344 
345   filename_size = GRN_PLUGIN_KEY_SIZE(filename);
346 
347   CRITICAL_SECTION_ENTER(grn_plugins_lock);
348   if ((id = grn_hash_get(plugins_ctx, grn_plugins, filename, filename_size,
349                          (void **)&plugin))) {
350     (*plugin)->refcount++;
351     goto exit;
352   }
353 
354 #ifdef GRN_WITH_MRUBY
355   {
356     const char *mrb_suffix;
357     mrb_suffix = grn_plugin_get_ruby_suffix();
358     if (filename_size > strlen(mrb_suffix) &&
359       strcmp(filename + (strlen(filename) - strlen(mrb_suffix)),
360              mrb_suffix) == 0) {
361       id = grn_plugin_open_mrb(ctx, filename, filename_size);
362       goto exit;
363     }
364   }
365 #endif /* GRN_WITH_MRUBY */
366 
367   if ((dl = grn_dl_open(filename))) {
368     if ((id = grn_hash_add(plugins_ctx, grn_plugins, filename, filename_size,
369                            (void **)&plugin, NULL))) {
370       {
371         grn_ctx *ctx = plugins_ctx;
372         *plugin = GRN_MALLOCN(grn_plugin, 1);
373       }
374       if (*plugin) {
375         grn_memcpy((*plugin)->path, filename, filename_size);
376         if (grn_plugin_initialize(ctx, *plugin, dl, id, filename)) {
377           {
378             grn_ctx *ctx = plugins_ctx;
379             GRN_FREE(*plugin);
380           }
381           *plugin = NULL;
382         }
383       }
384       if (!*plugin) {
385         grn_hash_delete_by_id(plugins_ctx, grn_plugins, id, NULL);
386         if (grn_dl_close(dl)) {
387           /* Now, __FILE__ set in plugin is invalid. */
388           ctx->errline = 0;
389           ctx->errfile = NULL;
390         } else {
391           const char *label;
392           label = grn_dl_close_error_label();
393           SERR("%s", label);
394         }
395         id = GRN_ID_NIL;
396       } else {
397         (*plugin)->refcount = 1;
398       }
399     } else {
400       if (!grn_dl_close(dl)) {
401         const char *label;
402         label = grn_dl_close_error_label();
403         SERR("%s", label);
404       }
405     }
406   } else {
407     const char *label;
408     label = grn_dl_open_error_label();
409     SERR("%s", label);
410   }
411 
412 exit:
413   CRITICAL_SECTION_LEAVE(grn_plugins_lock);
414 
415   return id;
416 }
417 
418 grn_rc
grn_plugin_close(grn_ctx * ctx,grn_id id)419 grn_plugin_close(grn_ctx *ctx, grn_id id)
420 {
421   grn_ctx *plugins_ctx = &grn_plugins_ctx;
422   grn_rc rc;
423   grn_plugin *plugin;
424 
425   if (id == GRN_ID_NIL) {
426     return GRN_INVALID_ARGUMENT;
427   }
428 
429   CRITICAL_SECTION_ENTER(grn_plugins_lock);
430   if (!grn_hash_get_value(plugins_ctx, grn_plugins, id, &plugin)) {
431     rc = GRN_INVALID_ARGUMENT;
432     goto exit;
433   }
434   if (--plugin->refcount) {
435     rc = GRN_SUCCESS;
436     goto exit;
437   }
438   if (plugin->dl) {
439     grn_plugin_call_fin(ctx, id);
440     if (!grn_dl_close(plugin->dl)) {
441       const char *label;
442       label = grn_dl_close_error_label();
443       SERR("%s", label);
444     }
445   }
446   {
447     grn_ctx *ctx = plugins_ctx;
448     GRN_FREE(plugin);
449   }
450   rc = grn_hash_delete_by_id(plugins_ctx, grn_plugins, id, NULL);
451 
452 exit:
453   CRITICAL_SECTION_LEAVE(grn_plugins_lock);
454 
455   return rc;
456 }
457 
458 void *
grn_plugin_sym(grn_ctx * ctx,grn_id id,const char * symbol)459 grn_plugin_sym(grn_ctx *ctx, grn_id id, const char *symbol)
460 {
461   grn_plugin *plugin;
462   grn_dl_symbol func;
463 
464   if (id == GRN_ID_NIL) {
465     return NULL;
466   }
467 
468   CRITICAL_SECTION_ENTER(grn_plugins_lock);
469   if (!grn_hash_get_value(&grn_plugins_ctx, grn_plugins, id, &plugin)) {
470     func = NULL;
471     goto exit;
472   }
473   grn_dl_clear_error();
474   if (!(func = grn_dl_sym(plugin->dl, symbol))) {
475     const char *label;
476     label = grn_dl_sym_error_label();
477     SERR("%s", label);
478   }
479 
480 exit:
481   CRITICAL_SECTION_LEAVE(grn_plugins_lock);
482 
483   return func;
484 }
485 
486 grn_rc
grn_plugins_init(void)487 grn_plugins_init(void)
488 {
489   CRITICAL_SECTION_INIT(grn_plugins_lock);
490   grn_ctx_init(&grn_plugins_ctx, 0);
491   grn_plugins = grn_hash_create(&grn_plugins_ctx, NULL,
492                                 PATH_MAX, sizeof(grn_plugin *),
493                                 GRN_OBJ_KEY_VAR_SIZE);
494   if (!grn_plugins) {
495     grn_ctx_fin(&grn_plugins_ctx);
496     return GRN_NO_MEMORY_AVAILABLE;
497   }
498   return GRN_SUCCESS;
499 }
500 
501 grn_rc
grn_plugins_fin(void)502 grn_plugins_fin(void)
503 {
504   grn_rc rc;
505   if (!grn_plugins) { return GRN_INVALID_ARGUMENT; }
506   GRN_HASH_EACH(&grn_plugins_ctx, grn_plugins, id, NULL, NULL, NULL, {
507     grn_plugin_close(&grn_plugins_ctx, id);
508   });
509   rc = grn_hash_close(&grn_plugins_ctx, grn_plugins);
510   grn_ctx_fin(&grn_plugins_ctx);
511   CRITICAL_SECTION_FIN(grn_plugins_lock);
512   return rc;
513 }
514 
515 const char *
grn_plugin_get_suffix(void)516 grn_plugin_get_suffix(void)
517 {
518   return GRN_PLUGIN_SUFFIX;
519 }
520 
521 const char *
grn_plugin_get_ruby_suffix(void)522 grn_plugin_get_ruby_suffix(void)
523 {
524   return ".rb";
525 }
526 
527 grn_rc
grn_plugin_register_by_path(grn_ctx * ctx,const char * path)528 grn_plugin_register_by_path(grn_ctx *ctx, const char *path)
529 {
530   grn_obj *db;
531   if (!ctx || !ctx->impl || !(db = ctx->impl->db)) {
532     ERR(GRN_INVALID_ARGUMENT, "db not initialized");
533     return ctx->rc;
534   }
535   GRN_API_ENTER;
536   if (GRN_DB_P(db)) {
537     grn_id id;
538     id = grn_plugin_open(ctx, path);
539     if (id) {
540       ctx->impl->plugin_path = path;
541       ctx->rc = grn_plugin_call_register(ctx, id);
542       ctx->impl->plugin_path = NULL;
543       grn_plugin_close(ctx, id);
544     }
545   } else {
546     ERR(GRN_INVALID_ARGUMENT, "invalid db assigned");
547   }
548   GRN_API_RETURN(ctx->rc);
549 }
550 
551 #ifdef WIN32
552 static char *windows_plugins_dir = NULL;
553 static char windows_plugins_dir_buffer[PATH_MAX];
554 static const char *
grn_plugin_get_default_system_plugins_dir(void)555 grn_plugin_get_default_system_plugins_dir(void)
556 {
557   if (!windows_plugins_dir) {
558     const char *base_dir;
559     const char *relative_path = GRN_RELATIVE_PLUGINS_DIR;
560     size_t base_dir_length;
561 
562     base_dir = grn_windows_base_dir();
563     base_dir_length = strlen(base_dir);
564     grn_strcpy(windows_plugins_dir_buffer, PATH_MAX, base_dir);
565     grn_strcat(windows_plugins_dir_buffer, PATH_MAX, "/");
566     grn_strcat(windows_plugins_dir_buffer, PATH_MAX, relative_path);
567     windows_plugins_dir = windows_plugins_dir_buffer;
568   }
569   return windows_plugins_dir;
570 }
571 
572 #else /* WIN32 */
573 static const char *
grn_plugin_get_default_system_plugins_dir(void)574 grn_plugin_get_default_system_plugins_dir(void)
575 {
576   return GRN_PLUGINS_DIR;
577 }
578 #endif /* WIN32 */
579 
580 const char *
grn_plugin_get_system_plugins_dir(void)581 grn_plugin_get_system_plugins_dir(void)
582 {
583   if (grn_plugins_dir[0]) {
584     return grn_plugins_dir;
585   } else {
586     return grn_plugin_get_default_system_plugins_dir();
587   }
588 }
589 
590 static char *
grn_plugin_find_path_raw(grn_ctx * ctx,const char * path)591 grn_plugin_find_path_raw(grn_ctx *ctx, const char *path)
592 {
593   struct stat path_stat;
594 
595   if (stat(path, &path_stat) != 0) {
596     return NULL;
597   }
598 
599   if (!S_ISREG(path_stat.st_mode)) {
600     return NULL;
601   }
602 
603   return GRN_STRDUP(path);
604 }
605 
606 #ifdef GRN_WITH_MRUBY
607 static char *
grn_plugin_find_path_mrb(grn_ctx * ctx,const char * path,size_t path_len)608 grn_plugin_find_path_mrb(grn_ctx *ctx, const char *path, size_t path_len)
609 {
610   char mrb_path[PATH_MAX];
611   const char *mrb_suffix;
612   size_t mrb_path_len;
613 
614   grn_ctx_impl_mrb_ensure_init(ctx);
615   if (ctx->rc != GRN_SUCCESS) {
616     return NULL;
617   }
618 
619   if (!ctx->impl->mrb.state) {
620     return NULL;
621   }
622 
623   mrb_suffix = grn_plugin_get_ruby_suffix();
624   mrb_path_len = path_len + strlen(mrb_suffix);
625   if (mrb_path_len >= PATH_MAX) {
626     ERR(GRN_FILENAME_TOO_LONG,
627         "too long plugin path: <%s%s>",
628         path, mrb_suffix);
629     return NULL;
630   }
631 
632   grn_strcpy(mrb_path, PATH_MAX, path);
633   grn_strcat(mrb_path, PATH_MAX, mrb_suffix);
634   return grn_plugin_find_path_raw(ctx, mrb_path);
635 }
636 #else /* GRN_WITH_MRUBY */
637 static char *
grn_plugin_find_path_mrb(grn_ctx * ctx,const char * path,size_t path_len)638 grn_plugin_find_path_mrb(grn_ctx *ctx, const char *path, size_t path_len)
639 {
640   return NULL;
641 }
642 #endif /* GRN_WITH_MRUBY */
643 
644 static char *
grn_plugin_find_path_so(grn_ctx * ctx,const char * path,size_t path_len)645 grn_plugin_find_path_so(grn_ctx *ctx, const char *path, size_t path_len)
646 {
647   char so_path[PATH_MAX];
648   const char *so_suffix;
649   size_t so_path_len;
650 
651   so_suffix = grn_plugin_get_suffix();
652   so_path_len = path_len + strlen(so_suffix);
653   if (so_path_len >= PATH_MAX) {
654     ERR(GRN_FILENAME_TOO_LONG,
655         "too long plugin path: <%s%s>",
656         path, so_suffix);
657     return NULL;
658   }
659 
660   grn_strcpy(so_path, PATH_MAX, path);
661   grn_strcat(so_path, PATH_MAX, so_suffix);
662   return grn_plugin_find_path_raw(ctx, so_path);
663 }
664 
665 static char *
grn_plugin_find_path_libs_so(grn_ctx * ctx,const char * path,size_t path_len)666 grn_plugin_find_path_libs_so(grn_ctx *ctx, const char *path, size_t path_len)
667 {
668   char libs_so_path[PATH_MAX];
669   const char *base_name;
670   const char *so_suffix;
671   const char *libs_path = "/.libs";
672   size_t libs_so_path_len;
673 
674   base_name = strrchr(path, '/');
675   if (!base_name) {
676     return NULL;
677   }
678 
679   so_suffix = grn_plugin_get_suffix();
680   libs_so_path_len =
681     base_name - path +
682     strlen(libs_path) +
683     strlen(base_name) +
684     strlen(so_suffix);
685   if (libs_so_path_len >= PATH_MAX) {
686     ERR(GRN_FILENAME_TOO_LONG,
687         "too long plugin path: <%.*s/.libs%s%s>",
688         (int)(base_name - path), path, base_name, so_suffix);
689     return NULL;
690   }
691 
692   libs_so_path[0] = '\0';
693   grn_strncat(libs_so_path, PATH_MAX, path, base_name - path);
694   grn_strcat(libs_so_path, PATH_MAX, libs_path);
695   grn_strcat(libs_so_path, PATH_MAX, base_name);
696   grn_strcat(libs_so_path, PATH_MAX, so_suffix);
697   return grn_plugin_find_path_raw(ctx, libs_so_path);
698 }
699 
700 char *
grn_plugin_find_path(grn_ctx * ctx,const char * name)701 grn_plugin_find_path(grn_ctx *ctx, const char *name)
702 {
703   const char *plugins_dir;
704   char dir_last_char;
705   char path[PATH_MAX];
706   int name_length, max_name_length;
707   char *found_path = NULL;
708   size_t path_len;
709 
710   GRN_API_ENTER;
711   if (name[0] == '/') {
712     path[0] = '\0';
713   } else {
714     plugins_dir = grn_plugin_get_system_plugins_dir();
715     grn_strcpy(path, PATH_MAX, plugins_dir);
716 
717     dir_last_char = plugins_dir[strlen(path) - 1];
718     if (dir_last_char != '/') {
719       grn_strcat(path, PATH_MAX, "/");
720     }
721   }
722 
723   name_length = strlen(name);
724   max_name_length = PATH_MAX - strlen(path) - 1;
725   if (name_length > max_name_length) {
726     ERR(GRN_INVALID_ARGUMENT,
727         "plugin name is too long: %d (max: %d) <%s%s>",
728         name_length, max_name_length,
729         path, name);
730     goto exit;
731   }
732   grn_strcat(path, PATH_MAX, name);
733 
734   found_path = grn_plugin_find_path_raw(ctx, path);
735   if (found_path) {
736     goto exit;
737   }
738 
739   path_len = strlen(path);
740 
741   found_path = grn_plugin_find_path_so(ctx, path, path_len);
742   if (found_path) {
743     goto exit;
744   }
745   if (ctx->rc) {
746     goto exit;
747   }
748 
749   found_path = grn_plugin_find_path_libs_so(ctx, path, path_len);
750   if (found_path) {
751     goto exit;
752   }
753   if (ctx->rc) {
754     goto exit;
755   }
756 
757   found_path = grn_plugin_find_path_mrb(ctx, path, path_len);
758   if (found_path) {
759     goto exit;
760   }
761   if (ctx->rc) {
762     goto exit;
763   }
764 
765 exit :
766   GRN_API_RETURN(found_path);
767 }
768 
769 static void
grn_plugin_set_name_resolve_error(grn_ctx * ctx,const char * name,const char * tag)770 grn_plugin_set_name_resolve_error(grn_ctx *ctx, const char *name,
771                                   const char *tag)
772 {
773   const char *prefix, *prefix_separator, *suffix;
774 
775   if (name[0] == '/') {
776     prefix = "";
777     prefix_separator = "";
778     suffix = "";
779   } else {
780     prefix = grn_plugin_get_system_plugins_dir();
781     if (prefix[strlen(prefix) - 1] != '/') {
782       prefix_separator = "/";
783     } else {
784       prefix_separator = "";
785     }
786     suffix = grn_plugin_get_suffix();
787   }
788   ERR(GRN_NO_SUCH_FILE_OR_DIRECTORY,
789       "%s cannot find plugin file: <%s%s%s%s>",
790       tag, prefix, prefix_separator, name, suffix);
791 }
792 
793 grn_rc
grn_plugin_register(grn_ctx * ctx,const char * name)794 grn_plugin_register(grn_ctx *ctx, const char *name)
795 {
796   grn_rc rc;
797   char *path;
798 
799   GRN_API_ENTER;
800   path = grn_plugin_find_path(ctx, name);
801   if (path) {
802     rc = grn_plugin_register_by_path(ctx, path);
803     GRN_FREE(path);
804   } else {
805     if (ctx->rc == GRN_SUCCESS) {
806       grn_plugin_set_name_resolve_error(ctx, name, "[plugin][register]");
807     }
808     rc = ctx->rc;
809   }
810   GRN_API_RETURN(rc);
811 }
812 
813 grn_rc
grn_plugin_unregister_by_path(grn_ctx * ctx,const char * path)814 grn_plugin_unregister_by_path(grn_ctx *ctx, const char *path)
815 {
816   grn_obj *db;
817   grn_id plugin_id;
818 
819   if (!ctx || !ctx->impl) {
820     ERR(GRN_INVALID_ARGUMENT, "[plugin][unregister] ctx isn't initialized");
821     return ctx->rc;
822   }
823 
824   db = ctx->impl->db;
825   if (!db) {
826     ERR(GRN_INVALID_ARGUMENT, "[plugin][unregister] DB isn't initialized");
827     return ctx->rc;
828   }
829 
830   GRN_API_ENTER;
831 
832   CRITICAL_SECTION_ENTER(grn_plugins_lock);
833   plugin_id = grn_hash_get(&grn_plugins_ctx, grn_plugins,
834                            path, GRN_PLUGIN_KEY_SIZE(path),
835                            NULL);
836   CRITICAL_SECTION_LEAVE(grn_plugins_lock);
837 
838   if (plugin_id == GRN_ID_NIL) {
839     GRN_API_RETURN(ctx->rc);
840   }
841 
842   {
843     grn_table_cursor *cursor;
844     grn_id id;
845 
846     cursor = grn_table_cursor_open(ctx, db,
847                                    NULL, 0,
848                                    NULL, 0,
849                                    0, -1, GRN_CURSOR_BY_ID);
850     if (!cursor) {
851       GRN_API_RETURN(ctx->rc);
852     }
853 
854     while ((id = grn_table_cursor_next(ctx, cursor))) {
855       grn_obj *obj;
856       obj = grn_ctx_at(ctx, id);
857       if (!obj) {
858         continue;
859       }
860       if (obj->header.type == GRN_PROC && DB_OBJ(obj)->range == plugin_id) {
861         grn_obj_remove(ctx, obj);
862       } else {
863         grn_obj_unlink(ctx, obj);
864       }
865     }
866     grn_table_cursor_close(ctx, cursor);
867   }
868 
869   GRN_API_RETURN(ctx->rc);
870 }
871 
872 grn_rc
grn_plugin_unregister(grn_ctx * ctx,const char * name)873 grn_plugin_unregister(grn_ctx *ctx, const char *name)
874 {
875   grn_rc rc;
876   char *path;
877 
878   GRN_API_ENTER;
879   path = grn_plugin_find_path(ctx, name);
880   if (path) {
881     rc = grn_plugin_unregister_by_path(ctx, path);
882     GRN_FREE(path);
883   } else {
884     if (ctx->rc == GRN_SUCCESS) {
885       grn_plugin_set_name_resolve_error(ctx, name, "[plugin][unregister]");
886     }
887     rc = ctx->rc;
888   }
889   GRN_API_RETURN(rc);
890 }
891 
892 void
grn_plugin_ensure_registered(grn_ctx * ctx,grn_obj * proc)893 grn_plugin_ensure_registered(grn_ctx *ctx, grn_obj *proc)
894 {
895 #ifdef GRN_WITH_MRUBY
896   grn_id plugin_id;
897   grn_plugin *plugin = NULL;
898 
899   if (!(proc->header.flags & GRN_OBJ_CUSTOM_NAME)) {
900     return;
901   }
902 
903   plugin_id = DB_OBJ(proc)->range;
904   CRITICAL_SECTION_ENTER(grn_plugins_lock);
905   {
906     const char *value;
907     value = grn_hash_get_value_(&grn_plugins_ctx, grn_plugins, plugin_id, NULL);
908     if (value) {
909       plugin = *((grn_plugin **)value);
910     }
911   }
912   CRITICAL_SECTION_LEAVE(grn_plugins_lock);
913 
914   if (!plugin) {
915     return;
916   }
917 
918   if (plugin->dl) {
919     return;
920   }
921 
922   grn_ctx_impl_mrb_ensure_init(ctx);
923   if (ctx->rc != GRN_SUCCESS) {
924     return;
925   }
926 
927   if (!ctx->impl->mrb.state) {
928     return;
929   }
930 
931   {
932     grn_id id;
933     int added;
934     id = DB_OBJ(proc)->id;
935     grn_hash_add(ctx, ctx->impl->mrb.checked_procs,
936                  &id, sizeof(grn_id), NULL, &added);
937     if (!added) {
938       return;
939     }
940   }
941 
942   ctx->impl->plugin_path = plugin->path;
943   grn_plugin_call_register_mrb(ctx, plugin_id, plugin);
944   ctx->impl->plugin_path = NULL;
945 #endif /* GRN_WITH_MRUBY */
946 }
947 
948 grn_rc
grn_plugin_get_names(grn_ctx * ctx,grn_obj * names)949 grn_plugin_get_names(grn_ctx *ctx, grn_obj *names)
950 {
951   grn_hash *processed_paths;
952   const char *system_plugins_dir;
953   const char *native_plugin_suffix;
954   const char *ruby_plugin_suffix;
955   grn_bool is_close_opened_object_mode = GRN_FALSE;
956 
957   GRN_API_ENTER;
958 
959   if (ctx->rc) {
960     GRN_API_RETURN(ctx->rc);
961   }
962 
963   if (grn_thread_get_limit() == 1) {
964     is_close_opened_object_mode = GRN_TRUE;
965   }
966 
967   processed_paths = grn_hash_create(ctx, NULL, GRN_TABLE_MAX_KEY_SIZE, 0,
968                                     GRN_OBJ_TABLE_HASH_KEY |
969                                     GRN_OBJ_KEY_VAR_SIZE);
970   if (!processed_paths) {
971     GRN_API_RETURN(ctx->rc);
972   }
973 
974   system_plugins_dir = grn_plugin_get_system_plugins_dir();
975   native_plugin_suffix = grn_plugin_get_suffix();
976   ruby_plugin_suffix = grn_plugin_get_ruby_suffix();
977 
978   GRN_TABLE_EACH_BEGIN_FLAGS(ctx, grn_ctx_db(ctx), cursor, id,
979                              GRN_CURSOR_BY_ID | GRN_CURSOR_ASCENDING) {
980     void *name;
981     int name_size;
982     grn_obj *object;
983     const char *path;
984     grn_id processed_path_id;
985 
986     if (grn_id_is_builtin(ctx, id)) {
987       continue;
988     }
989 
990     name_size = grn_table_cursor_get_key(ctx, cursor, &name);
991     if (grn_obj_name_is_column(ctx, name, name_size)) {
992       continue;
993     }
994 
995     if (is_close_opened_object_mode) {
996       grn_ctx_push_temporary_open_space(ctx);
997     }
998 
999     object = grn_ctx_at(ctx, id);
1000     if (!object) {
1001       ERRCLR(ctx);
1002       goto next_loop;
1003     }
1004 
1005     if (!grn_obj_is_proc(ctx, object)) {
1006       goto next_loop;
1007     }
1008 
1009     path = grn_obj_path(ctx, object);
1010     if (!path) {
1011       goto next_loop;
1012     }
1013 
1014     processed_path_id = grn_hash_get(ctx, processed_paths,
1015                                      path, strlen(path),
1016                                      NULL);
1017     if (processed_path_id != GRN_ID_NIL) {
1018       goto next_loop;
1019     }
1020 
1021     grn_hash_add(ctx, processed_paths,
1022                  path, strlen(path),
1023                  NULL, NULL);
1024 
1025     {
1026       const char *relative_path;
1027       const char *libs_path = "/.libs/";
1028       const char *start_libs;
1029       char name[PATH_MAX];
1030 
1031       name[0] = '\0';
1032       if (strncmp(path, system_plugins_dir, strlen(system_plugins_dir)) == 0) {
1033         relative_path = path + strlen(system_plugins_dir);
1034       } else {
1035         relative_path = path;
1036       }
1037       start_libs = strstr(relative_path, libs_path);
1038       if (start_libs) {
1039         grn_strncat(name, PATH_MAX, relative_path, start_libs - relative_path);
1040         grn_strcat(name, PATH_MAX, "/");
1041         grn_strcat(name, PATH_MAX, start_libs + strlen(libs_path));
1042       } else {
1043         grn_strcat(name, PATH_MAX, relative_path);
1044       }
1045       if (strlen(name) > strlen(native_plugin_suffix) &&
1046           strcmp(name + strlen(name) - strlen(native_plugin_suffix),
1047                  native_plugin_suffix) == 0) {
1048         name[strlen(name) - strlen(native_plugin_suffix)] = '\0';
1049       } else if (strlen(name) > strlen(ruby_plugin_suffix) &&
1050                  strcmp(name + strlen(name) - strlen(ruby_plugin_suffix),
1051                         ruby_plugin_suffix) == 0) {
1052         name[strlen(name) - strlen(ruby_plugin_suffix)] = '\0';
1053       }
1054       grn_vector_add_element(ctx, names,
1055                              name, strlen(name),
1056                              0, GRN_DB_TEXT);
1057     }
1058 
1059   next_loop :
1060     if (is_close_opened_object_mode) {
1061       grn_ctx_pop_temporary_open_space(ctx);
1062     }
1063   } GRN_TABLE_EACH_END(ctx, cursor);
1064 
1065   grn_hash_close(ctx, processed_paths);
1066 
1067   GRN_API_RETURN(ctx->rc);
1068 }
1069 
1070 void *
grn_plugin_malloc(grn_ctx * ctx,size_t size,const char * file,int line,const char * func)1071 grn_plugin_malloc(grn_ctx *ctx, size_t size, const char *file, int line,
1072                   const char *func)
1073 {
1074   return grn_malloc(ctx, size, file, line, func);
1075 }
1076 
1077 void *
grn_plugin_calloc(grn_ctx * ctx,size_t size,const char * file,int line,const char * func)1078 grn_plugin_calloc(grn_ctx *ctx, size_t size, const char *file, int line,
1079                   const char *func)
1080 {
1081   return grn_calloc(ctx, size, file, line, func);
1082 }
1083 
1084 void *
grn_plugin_realloc(grn_ctx * ctx,void * ptr,size_t size,const char * file,int line,const char * func)1085 grn_plugin_realloc(grn_ctx *ctx, void *ptr, size_t size,
1086                    const char *file, int line, const char *func)
1087 {
1088   return grn_realloc(ctx, ptr, size, file, line, func);
1089 }
1090 
1091 void
grn_plugin_free(grn_ctx * ctx,void * ptr,const char * file,int line,const char * func)1092 grn_plugin_free(grn_ctx *ctx, void *ptr, const char *file, int line,
1093                 const char *func)
1094 {
1095   grn_free(ctx, ptr, file, line, func);
1096 }
1097 
1098 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,...)1099 grn_plugin_set_error(grn_ctx *ctx, grn_log_level level, grn_rc error_code,
1100                      const char *file, int line, const char *func,
1101                      const char *format, ...)
1102 {
1103   char old_error_message[GRN_CTX_MSGSIZE];
1104 
1105   ctx->errlvl = level;
1106   ctx->rc = error_code;
1107   ctx->errfile = file;
1108   ctx->errline = line;
1109   ctx->errfunc = func;
1110 
1111   grn_strcpy(old_error_message, GRN_CTX_MSGSIZE, ctx->errbuf);
1112 
1113   {
1114     va_list ap;
1115     va_start(ap, format);
1116     grn_ctx_logv(ctx, format, ap);
1117     va_end(ap);
1118   }
1119 
1120   if (grn_ctx_impl_should_log(ctx)) {
1121     grn_ctx_impl_set_current_error_message(ctx);
1122     if (grn_logger_pass(ctx, level)) {
1123       char new_error_message[GRN_CTX_MSGSIZE];
1124       grn_strcpy(new_error_message, GRN_CTX_MSGSIZE, ctx->errbuf);
1125       grn_strcpy(ctx->errbuf, GRN_CTX_MSGSIZE, old_error_message);
1126       {
1127         va_list ap;
1128         va_start(ap, format);
1129         grn_logger_putv(ctx, level, file, line, func, format, ap);
1130         va_end(ap);
1131       }
1132       grn_strcpy(ctx->errbuf, GRN_CTX_MSGSIZE, new_error_message);
1133     }
1134     if (level <= GRN_LOG_ERROR) {
1135       grn_plugin_logtrace(ctx, level);
1136     }
1137   }
1138 }
1139 
1140 void
grn_plugin_clear_error(grn_ctx * ctx)1141 grn_plugin_clear_error(grn_ctx *ctx)
1142 {
1143   ERRCLR(ctx);
1144 }
1145 
1146 void
grn_plugin_backtrace(grn_ctx * ctx)1147 grn_plugin_backtrace(grn_ctx *ctx)
1148 {
1149   BACKTRACE(ctx);
1150 }
1151 
1152 void
grn_plugin_logtrace(grn_ctx * ctx,grn_log_level level)1153 grn_plugin_logtrace(grn_ctx *ctx, grn_log_level level)
1154 {
1155   if (level <= GRN_LOG_ERROR) {
1156     grn_plugin_backtrace(ctx);
1157     LOGTRACE(ctx, level);
1158   }
1159 }
1160 
1161 struct _grn_plugin_mutex {
1162   grn_critical_section critical_section;
1163 };
1164 
1165 grn_plugin_mutex *
grn_plugin_mutex_open(grn_ctx * ctx)1166 grn_plugin_mutex_open(grn_ctx *ctx)
1167 {
1168   grn_plugin_mutex * const mutex =
1169       GRN_PLUGIN_MALLOC(ctx, sizeof(grn_plugin_mutex));
1170   if (mutex != NULL) {
1171     CRITICAL_SECTION_INIT(mutex->critical_section);
1172   }
1173   return mutex;
1174 }
1175 
1176 grn_plugin_mutex *
grn_plugin_mutex_create(grn_ctx * ctx)1177 grn_plugin_mutex_create(grn_ctx *ctx)
1178 {
1179   return grn_plugin_mutex_open(ctx);
1180 }
1181 
1182 void
grn_plugin_mutex_close(grn_ctx * ctx,grn_plugin_mutex * mutex)1183 grn_plugin_mutex_close(grn_ctx *ctx, grn_plugin_mutex *mutex)
1184 {
1185   if (mutex != NULL) {
1186     CRITICAL_SECTION_FIN(mutex->critical_section);
1187     GRN_PLUGIN_FREE(ctx, mutex);
1188   }
1189 }
1190 
1191 void
grn_plugin_mutex_destroy(grn_ctx * ctx,grn_plugin_mutex * mutex)1192 grn_plugin_mutex_destroy(grn_ctx *ctx, grn_plugin_mutex *mutex)
1193 {
1194   grn_plugin_mutex_close(ctx, mutex);
1195 }
1196 
1197 void
grn_plugin_mutex_lock(grn_ctx * ctx,grn_plugin_mutex * mutex)1198 grn_plugin_mutex_lock(grn_ctx *ctx, grn_plugin_mutex *mutex)
1199 {
1200   if (mutex != NULL) {
1201     CRITICAL_SECTION_ENTER(mutex->critical_section);
1202   }
1203 }
1204 
1205 void
grn_plugin_mutex_unlock(grn_ctx * ctx,grn_plugin_mutex * mutex)1206 grn_plugin_mutex_unlock(grn_ctx *ctx, grn_plugin_mutex *mutex)
1207 {
1208   if (mutex != NULL) {
1209     CRITICAL_SECTION_LEAVE(mutex->critical_section);
1210   }
1211 }
1212 
1213 grn_obj *
grn_plugin_proc_alloc(grn_ctx * ctx,grn_user_data * user_data,grn_id domain,unsigned char flags)1214 grn_plugin_proc_alloc(grn_ctx *ctx, grn_user_data *user_data,
1215                       grn_id domain, unsigned char flags)
1216 {
1217   return grn_proc_alloc(ctx, user_data, domain, flags);
1218 }
1219 
1220 grn_obj *
grn_plugin_proc_get_vars(grn_ctx * ctx,grn_user_data * user_data)1221 grn_plugin_proc_get_vars(grn_ctx *ctx, grn_user_data *user_data)
1222 {
1223   return grn_proc_get_vars(ctx, user_data);
1224 }
1225 
1226 grn_obj *
grn_plugin_proc_get_var(grn_ctx * ctx,grn_user_data * user_data,const char * name,int name_size)1227 grn_plugin_proc_get_var(grn_ctx *ctx, grn_user_data *user_data,
1228                         const char *name, int name_size)
1229 {
1230   name_size = compute_name_size(name, name_size);
1231   return grn_proc_get_var(ctx, user_data, name, name_size);
1232 }
1233 
1234 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)1235 grn_plugin_proc_get_var_bool(grn_ctx *ctx,
1236                              grn_user_data *user_data,
1237                              const char *name,
1238                              int name_size,
1239                              grn_bool default_value)
1240 {
1241   grn_obj *var;
1242 
1243   var = grn_plugin_proc_get_var(ctx, user_data, name, name_size);
1244   return grn_proc_option_value_bool(ctx, var, default_value);
1245 }
1246 
1247 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)1248 grn_plugin_proc_get_var_int32(grn_ctx *ctx,
1249                               grn_user_data *user_data,
1250                               const char *name,
1251                               int name_size,
1252                               int32_t default_value)
1253 {
1254   grn_obj *var;
1255 
1256   var = grn_plugin_proc_get_var(ctx, user_data, name, name_size);
1257   return grn_proc_option_value_int32(ctx, var, default_value);
1258 }
1259 
1260 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)1261 grn_plugin_proc_get_var_string(grn_ctx *ctx,
1262                                grn_user_data *user_data,
1263                                const char *name,
1264                                int name_size,
1265                                size_t *size)
1266 {
1267   grn_obj *var;
1268 
1269   var = grn_plugin_proc_get_var(ctx, user_data, name, name_size);
1270   return grn_proc_option_value_string(ctx, var, size);
1271 }
1272 
1273 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)1274 grn_plugin_proc_get_var_content_type(grn_ctx *ctx,
1275                                      grn_user_data *user_data,
1276                                      const char *name,
1277                                      int name_size,
1278                                      grn_content_type default_value)
1279 {
1280   grn_obj *var;
1281 
1282   var = grn_plugin_proc_get_var(ctx, user_data, name, name_size);
1283   return grn_proc_option_value_content_type(ctx, var, default_value);
1284 }
1285 
1286 grn_obj *
grn_plugin_proc_get_var_by_offset(grn_ctx * ctx,grn_user_data * user_data,unsigned int offset)1287 grn_plugin_proc_get_var_by_offset(grn_ctx *ctx, grn_user_data *user_data,
1288                                   unsigned int offset)
1289 {
1290   return grn_proc_get_var_by_offset(ctx, user_data, offset);
1291 }
1292 
1293 grn_obj *
grn_plugin_proc_get_caller(grn_ctx * ctx,grn_user_data * user_data)1294 grn_plugin_proc_get_caller(grn_ctx *ctx, grn_user_data *user_data)
1295 {
1296   grn_obj *caller = NULL;
1297   GRN_API_ENTER;
1298   grn_proc_get_info(ctx, user_data, NULL, NULL, &caller);
1299   GRN_API_RETURN(caller);
1300 }
1301 
1302 const char *
grn_plugin_win32_base_dir(void)1303 grn_plugin_win32_base_dir(void)
1304 {
1305   return grn_plugin_windows_base_dir();
1306 }
1307 
1308 const char *
grn_plugin_windows_base_dir(void)1309 grn_plugin_windows_base_dir(void)
1310 {
1311 #ifdef WIN32
1312   return grn_windows_base_dir();
1313 #else /* WIN32 */
1314   return NULL;
1315 #endif /* WIN32 */
1316 }
1317 
1318 /*
1319   grn_plugin_charlen() takes the length of a string, unlike grn_charlen_().
1320  */
1321 int
grn_plugin_charlen(grn_ctx * ctx,const char * str_ptr,unsigned int str_length,grn_encoding encoding)1322 grn_plugin_charlen(grn_ctx *ctx, const char *str_ptr,
1323                    unsigned int str_length, grn_encoding encoding)
1324 {
1325   return grn_charlen_(ctx, str_ptr, str_ptr + str_length, encoding);
1326 }
1327 
1328 /*
1329   grn_plugin_isspace() takes the length of a string, unlike grn_isspace().
1330  */
1331 int
grn_plugin_isspace(grn_ctx * ctx,const char * str_ptr,unsigned int str_length,grn_encoding encoding)1332 grn_plugin_isspace(grn_ctx *ctx, const char *str_ptr,
1333                    unsigned int str_length, grn_encoding encoding)
1334 {
1335   if ((str_ptr == NULL) || (str_length == 0)) {
1336     return 0;
1337   }
1338   switch ((unsigned char)str_ptr[0]) {
1339   case ' ' :
1340   case '\f' :
1341   case '\n' :
1342   case '\r' :
1343   case '\t' :
1344   case '\v' :
1345     return 1;
1346   case 0x81 :
1347     if ((encoding == GRN_ENC_SJIS) && (str_length >= 2) &&
1348         ((unsigned char)str_ptr[1] == 0x40)) {
1349       return 2;
1350     }
1351     break;
1352   case 0xA1 :
1353     if ((encoding == GRN_ENC_EUC_JP) && (str_length >= 2) &&
1354         ((unsigned char)str_ptr[1] == 0xA1)) {
1355       return 2;
1356     }
1357     break;
1358   case 0xE3 :
1359     if ((encoding == GRN_ENC_UTF8) && (str_length >= 3) &&
1360         ((unsigned char)str_ptr[1] == 0x80) &&
1361         ((unsigned char)str_ptr[2] == 0x80)) {
1362       return 3;
1363     }
1364     break;
1365   default :
1366     break;
1367   }
1368   return 0;
1369 }
1370 
1371 grn_rc
grn_plugin_expr_var_init(grn_ctx * ctx,grn_expr_var * var,const char * name,int name_size)1372 grn_plugin_expr_var_init(grn_ctx *ctx,
1373                          grn_expr_var *var,
1374                          const char *name,
1375                          int name_size)
1376 {
1377   var->name = name;
1378   var->name_size = compute_name_size(name, name_size);
1379   GRN_TEXT_INIT(&var->value, 0);
1380   return GRN_SUCCESS;
1381 }
1382 
1383 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)1384 grn_plugin_command_create(grn_ctx *ctx,
1385                           const char *name,
1386                           int name_size,
1387                           grn_proc_func func,
1388                           unsigned int n_vars,
1389                           grn_expr_var *vars)
1390 {
1391   grn_obj *proc;
1392   name_size = compute_name_size(name, name_size);
1393   proc = grn_proc_create(ctx, name, name_size, GRN_PROC_COMMAND,
1394                          func, NULL, NULL, n_vars, vars);
1395   return proc;
1396 }
1397