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 | |
40 | static char grn_mrb_ruby_scripts_dir[GRN_ENV_BUFFER_SIZE]; |
41 | static grn_bool grn_mrb_order_by_estimated_size_enable = GRN_FALSE; |
42 | |
43 | void |
44 | grn_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 | |
62 | grn_bool |
63 | grn_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 |
70 | static char *windows_ruby_scripts_dir = NULL; |
71 | static char windows_ruby_scripts_dir_buffer[PATH_MAX]; |
72 | static const char * |
73 | grn_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 */ |
91 | static const char * |
92 | grn_mrb_get_default_system_ruby_scripts_dir(void) |
93 | { |
94 | return GRN_RUBY_SCRIPTS_DIR; |
95 | } |
96 | # endif /* WIN32 */ |
97 | |
98 | const char * |
99 | grn_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 | |
108 | static grn_bool |
109 | grn_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 | |
122 | static grn_bool |
123 | grn_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 | |
160 | mrb_value |
161 | grn_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 | |