1/* -*- c-basic-offset: 2 -*- */
2/*
3 Copyright(C) 2013-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-1301 USA
17*/
18
19#include "grn_mrb.h"
20#include "grn_ctx_impl.h"
21#include "grn_util.h"
22
23#include <string.h>
24
25#ifdef GRN_WITH_MRUBY
26# include <mruby/proc.h>
27# include <mruby/compile.h>
28# include <mruby/string.h>
29#endif
30
31#include <ctype.h>
32
33#ifdef WIN32
34# include <share.h>
35#endif /* WIN32 */
36
37#define BUFFER_SIZE 2048
38#define E_LOAD_ERROR (mrb_class_get(mrb, "LoadError"))
39
40static char grn_mrb_ruby_scripts_dir[GRN_ENV_BUFFER_SIZE];
41static grn_bool grn_mrb_order_by_estimated_size_enable = GRN_FALSE;
42
43void
44grn_mrb_init_from_env(void)
45{
46 grn_getenv("GRN_RUBY_SCRIPTS_DIR",
47 grn_mrb_ruby_scripts_dir,
48 GRN_ENV_BUFFER_SIZE);
49 {
50 char grn_order_by_estimated_size_enable_env[GRN_ENV_BUFFER_SIZE];
51 grn_getenv("GRN_ORDER_BY_ESTIMATED_SIZE_ENABLE",
52 grn_order_by_estimated_size_enable_env,
53 GRN_ENV_BUFFER_SIZE);
54 if (strcmp(grn_order_by_estimated_size_enable_env, "yes") == 0) {
55 grn_mrb_order_by_estimated_size_enable = GRN_TRUE;
56 } else {
57 grn_mrb_order_by_estimated_size_enable = GRN_FALSE;
58 }
59 }
60}
61
62grn_bool
63grn_mrb_is_order_by_estimated_size_enabled(void)
64{
65 return grn_mrb_order_by_estimated_size_enable;
66}
67
68#ifdef GRN_WITH_MRUBY
69# ifdef WIN32
70static char *windows_ruby_scripts_dir = NULL;
71static char windows_ruby_scripts_dir_buffer[PATH_MAX];
72static const char *
73grn_mrb_get_default_system_ruby_scripts_dir(void)
74{
75 if (!windows_ruby_scripts_dir) {
76 const char *base_dir;
77 const char *relative_path = GRN_RELATIVE_RUBY_SCRIPTS_DIR;
78 size_t base_dir_length;
79
80 base_dir = grn_windows_base_dir();
81 base_dir_length = strlen(base_dir);
82 grn_strcpy(windows_ruby_scripts_dir_buffer, PATH_MAX, base_dir);
83 grn_strcat(windows_ruby_scripts_dir_buffer, PATH_MAX, "/");
84 grn_strcat(windows_ruby_scripts_dir_buffer, PATH_MAX, relative_path);
85 windows_ruby_scripts_dir = windows_ruby_scripts_dir_buffer;
86 }
87 return windows_ruby_scripts_dir;
88}
89
90# else /* WIN32 */
91static const char *
92grn_mrb_get_default_system_ruby_scripts_dir(void)
93{
94 return GRN_RUBY_SCRIPTS_DIR;
95}
96# endif /* WIN32 */
97
98const char *
99grn_mrb_get_system_ruby_scripts_dir(grn_ctx *ctx)
100{
101 if (grn_mrb_ruby_scripts_dir[0]) {
102 return grn_mrb_ruby_scripts_dir;
103 } else {
104 return grn_mrb_get_default_system_ruby_scripts_dir();
105 }
106}
107
108static grn_bool
109grn_mrb_is_absolute_path(const char *path)
110{
111 if (path[0] == '/') {
112 return GRN_TRUE;
113 }
114
115 if (isalpha((unsigned char)path[0]) && path[1] == ':' && path[2] == '/') {
116 return GRN_TRUE;
117 }
118
119 return GRN_FALSE;
120}
121
122static grn_bool
123grn_mrb_expand_script_path(grn_ctx *ctx, const char *path,
124 char *expanded_path, size_t expanded_path_size)
125{
126 const char *ruby_scripts_dir;
127 char dir_last_char;
128 int path_length, max_path_length;
129
130 if (grn_mrb_is_absolute_path(path)) {
131 expanded_path[0] = '\0';
132 } else if (path[0] == '.' && path[1] == '/') {
133 grn_strcpy(expanded_path, expanded_path_size, ctx->impl->mrb.base_directory);
134 grn_strcat(expanded_path, expanded_path_size, "/");
135 } else {
136 ruby_scripts_dir = grn_mrb_get_system_ruby_scripts_dir(ctx);
137 grn_strcpy(expanded_path, expanded_path_size, ruby_scripts_dir);
138
139 dir_last_char = ruby_scripts_dir[strlen(expanded_path) - 1];
140 if (dir_last_char != '/') {
141 grn_strcat(expanded_path, expanded_path_size, "/");
142 }
143 }
144
145 path_length = strlen(path);
146 max_path_length = PATH_MAX - strlen(expanded_path) - 1;
147 if (path_length > max_path_length) {
148 ERR(GRN_INVALID_ARGUMENT,
149 "script path is too long: %d (max: %d) <%s%s>",
150 path_length, max_path_length,
151 expanded_path, path);
152 return GRN_FALSE;
153 }
154
155 grn_strcat(expanded_path, expanded_path_size, path);
156
157 return GRN_TRUE;
158}
159
160mrb_value
161grn_mrb_load(grn_ctx *ctx, const char *path)
162{
163 grn_mrb_data *data = &(ctx->impl->mrb);
164 mrb_state *mrb = data->state;
165 char expanded_path[PATH_MAX];
166 FILE *file;
167 mrb_value result;
168 struct mrb_parser_state *parser;
169
170 if (!mrb) {
171 return mrb_nil_value();
172 }
173
174 if (!grn_mrb_expand_script_path(ctx, path, expanded_path, PATH_MAX)) {
175 return mrb_nil_value();
176 }
177
178 file = grn_fopen(expanded_path, "r");
179 if (!file) {
180 mrb_value exception;
181 SERR("fopen: failed to open mruby script file: <%s>",
182 expanded_path);
183 exception = mrb_exc_new(mrb, E_LOAD_ERROR,
184 ctx->errbuf, strlen(ctx->errbuf));
185 mrb->exc = mrb_obj_ptr(exception);
186 return mrb_nil_value();
187 }
188
189 {
190 char current_base_directory[PATH_MAX];
191 char *last_directory;
192
193 grn_strcpy(current_base_directory, PATH_MAX, data->base_directory);
194 grn_strcpy(data->base_directory, PATH_MAX, expanded_path);
195 last_directory = strrchr(data->base_directory, '/');
196 if (last_directory) {
197 last_directory[0] = '\0';
198 }
199
200 parser = mrb_parser_new(mrb);
201 mrb_parser_set_filename(parser, expanded_path);
202 parser->s = parser->send = NULL;
203 parser->f = file;
204 mrb_parser_parse(parser, NULL);
205 fclose(file);
206
207 {
208 struct RProc *proc;
209 proc = mrb_generate_code(mrb, parser);
210 proc->target_class = mrb->object_class;
211 result = mrb_toplevel_run(mrb, proc);
212 }
213 mrb_parser_free(parser);
214
215 grn_strcpy(data->base_directory, PATH_MAX, current_base_directory);
216 }
217
218 return result;
219}
220#endif
221