1 /*
2
3 Copyright (c) 2009-2013 uim Project https://github.com/uim/uim
4
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
16 3. Neither the name of authors nor the names of its contributors
17 may be used to endorse or promote products derived from this software
18 without specific prior written permission.
19
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
21 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
24 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 SUCH DAMAGE.
31
32 */
33
34 #include <config.h>
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <stdint.h>
40 #include <errno.h>
41 #include <dlfcn.h>
42 #include <ffi.h>
43
44 #include "uim.h"
45 #include "uim-scm.h"
46 #include "uim-scm-abbrev.h"
47 #include "uim-posix.h"
48 #include "uim-notify.h"
49 #include "dynlib.h"
50
51 static const char *ffi_strerr_;
52
53 typedef struct {
54 int flag;
55 char *arg;
56 } opt_args;
57
58 static uim_lisp
make_arg_cons(const opt_args * arg)59 make_arg_cons(const opt_args *arg)
60 {
61 return CONS(MAKE_SYM(arg->arg), MAKE_INT(arg->flag));
62 }
63
64 static uim_lisp
make_arg_list(const opt_args * list)65 make_arg_list(const opt_args *list)
66 {
67 uim_lisp ret_;
68 int i = 0;
69
70 ret_ = uim_scm_null();
71 while (list[i].arg != 0) {
72 ret_ = CONS((uim_lisp)uim_scm_call_with_gc_ready_stack((uim_gc_gate_func_ptr)make_arg_cons,
73 (void *)&list[i]), ret_);
74 i++;
75 }
76 return ret_;
77 }
78
79 static const opt_args dlopen_mode[] = {
80 #ifdef RTLD_LAZY
81 { RTLD_LAZY, "$RTLD_LAZY" },
82 #endif
83 #ifdef RTLD_NOW
84 { RTLD_NOW, "$RTLD_NOW" },
85 #endif
86 #ifdef RTLD_GLOBAL
87 { RTLD_GLOBAL, "$RTLD_GLOBAL" },
88 #endif
89 #ifdef RTLD_LOCAL
90 { RTLD_LOCAL, "$RTLD_LOCAL" },
91 #endif
92 #ifdef DL_LAZY
93 { DL_LAZY, "$DL_LAZY" },
94 #endif
95 #ifdef RTLD_TRACE
96 { RTLD_TRACE, "$RTLD_TRACE" },
97 #endif
98 { 0, 0 }
99 };
100
101 static uim_lisp uim_lisp_dlopen_mode_;
102 static uim_lisp
c_uim_lisp_dlopen_mode(void)103 c_uim_lisp_dlopen_mode(void)
104 {
105 return uim_lisp_dlopen_mode_;
106 }
107
108 static uim_lisp
c_dlstrerr(void)109 c_dlstrerr(void)
110 {
111 if (ffi_strerr_)
112 return MAKE_STR(ffi_strerr_);
113 return MAKE_STR("");
114 }
115
116 static uim_lisp
c_dlopen(uim_lisp path_,uim_lisp mode_)117 c_dlopen(uim_lisp path_, uim_lisp mode_)
118 {
119 const char *s;
120 void *handle = dlopen(REFER_C_STR(path_), C_INT(mode_));
121
122 if ((s = dlerror()) != NULL) {
123 ffi_strerr_ = s;
124 return uim_scm_f();
125 }
126 ffi_strerr_ = NULL;
127 return MAKE_PTR(handle);
128 }
129
130 static uim_lisp
c_dlclose(uim_lisp handle_)131 c_dlclose(uim_lisp handle_)
132 {
133 if (!PTRP(handle_))
134 return uim_scm_f();
135 dlclose(C_PTR(handle_));
136 ffi_strerr_ = NULL;
137 return uim_scm_t();
138 }
139
140 static uim_lisp
c_dlsym(uim_lisp handle_,uim_lisp symbol_)141 c_dlsym(uim_lisp handle_, uim_lisp symbol_)
142 {
143 const char *s;
144 void *fun;
145
146 fun = dlsym(C_PTR(handle_), REFER_C_STR(symbol_));
147 if ((s = dlerror()) != NULL) {
148 ffi_strerr_ = s;
149 return uim_scm_f();
150 }
151 ffi_strerr_ = NULL;
152 return MAKE_PTR(fun);
153 }
154
155 typedef enum {
156 RET_UNKNOWN,
157 RET_VOID,
158 RET_UCHAR, RET_SCHAR,
159 RET_USHORT, RET_SSHORT,
160 RET_UINT, RET_SINT,
161 RET_ULONG, RET_SLONG,
162 RET_FLOAT, RET_DOUBLE,
163 RET_STR,
164 RET_PTR,
165 RET_SCM
166 } object_type;
167
168 #define FFI_STRERR_BAD_TYPEDEF 0
169 #define FFI_STRERR_BAD_ABI 1
170 #define FFI_STRERR_UNKOWN 2
171
172 static const char *ffi_strerr_messages[] = {
173 "ffi_prep_cif: FFI_BAD_TYPEDEF\n",
174 "ffi_prep_cif: FFI_BAD_ABI\n",
175 "ffi_prep_cif: unkown error\n"
176 };
177
178 static object_type
select_object_type(uim_lisp type_)179 select_object_type(uim_lisp type_)
180 {
181 if (strcmp(REFER_C_STR(type_), "void") == 0)
182 return RET_VOID;
183 if (strcmp(REFER_C_STR(type_), "unsigned-char") == 0)
184 return RET_UCHAR;
185 if (strcmp(REFER_C_STR(type_), "signed-char") == 0)
186 return RET_SCHAR;
187 if (strcmp(REFER_C_STR(type_), "char") == 0)
188 return RET_SCHAR;
189 if (strcmp(REFER_C_STR(type_), "unsigned-short") == 0)
190 return RET_USHORT;
191 if (strcmp(REFER_C_STR(type_), "signed-short") == 0)
192 return RET_SSHORT;
193 if (strcmp(REFER_C_STR(type_), "short") == 0)
194 return RET_SSHORT;
195 if (strcmp(REFER_C_STR(type_), "unsigned-int") == 0)
196 return RET_SINT;
197 if (strcmp(REFER_C_STR(type_), "signed-int") == 0)
198 return RET_SINT;
199 if (strcmp(REFER_C_STR(type_), "int") == 0)
200 return RET_SINT;
201 if (strcmp(REFER_C_STR(type_), "unsigned-long") == 0)
202 return RET_ULONG;
203 if (strcmp(REFER_C_STR(type_), "signed-long") == 0)
204 return RET_SLONG;
205 if (strcmp(REFER_C_STR(type_), "long") == 0)
206 return RET_SLONG;
207 if (strcmp(REFER_C_STR(type_), "float") == 0)
208 return RET_FLOAT;
209 if (strcmp(REFER_C_STR(type_), "double") == 0)
210 return RET_DOUBLE;
211 if (strcmp(REFER_C_STR(type_), "string") == 0)
212 return RET_STR;
213 if (strcmp(REFER_C_STR(type_), "pointer") == 0)
214 return RET_PTR;
215 if (strcmp(REFER_C_STR(type_), "scheme-object") == 0)
216 return RET_SCM;
217
218 ERROR_OBJ("unknown object type", type_);
219 return RET_UNKNOWN;
220 }
221
222 static uim_lisp
c_ffi_call(uim_lisp result_,uim_lisp fun_,uim_lisp argv_)223 c_ffi_call(uim_lisp result_, uim_lisp fun_, uim_lisp argv_)
224 {
225 ffi_cif cif;
226 ffi_type **arg_types;
227 void **arg_values;
228 ffi_status status;
229 ffi_type *result_type = NULL;
230 void *result;
231 int args;
232 int i;
233 void *p;
234 uim_lisp ret_;
235 object_type return_object_type;
236 int input_void = 0;
237
238 args = uim_scm_length(argv_);
239 arg_types = uim_malloc(args * sizeof(void *));
240 arg_values = uim_malloc(args * sizeof(ffi_type *));
241
242 return_object_type = select_object_type(result_);
243
244 switch (return_object_type) {
245 case RET_UNKNOWN:
246 break;
247 case RET_VOID:
248 result_type = &ffi_type_void;
249 break;
250 case RET_UCHAR:
251 result_type = &ffi_type_uchar;
252 break;
253 case RET_SCHAR:
254 result_type = &ffi_type_schar;
255 break;
256 case RET_USHORT:
257 result_type = &ffi_type_ushort;
258 break;
259 case RET_SSHORT:
260 result_type = &ffi_type_sshort;
261 break;
262 case RET_ULONG:
263 result_type = &ffi_type_ulong;
264 break;
265 case RET_SLONG:
266 result_type = &ffi_type_slong;
267 break;
268 case RET_UINT:
269 result_type = &ffi_type_uint;
270 break;
271 case RET_SINT:
272 result_type = &ffi_type_sint;
273 break;
274 case RET_FLOAT:
275 result_type = &ffi_type_float;
276 break;
277 case RET_DOUBLE:
278 result_type = &ffi_type_double;
279 break;
280 case RET_STR:
281 result_type = &ffi_type_pointer;
282 break;
283 case RET_PTR:
284 result_type = &ffi_type_pointer;
285 break;
286 case RET_SCM:
287 result_type = &ffi_type_pointer;
288 break;
289 }
290
291 result = uim_malloc(1024); /* huge? */
292
293 for (i = 0; i < args; i++) {
294 uim_lisp arg_ = CAR(argv_);
295
296 switch (select_object_type(CAR(arg_))) {
297 case RET_UNKNOWN:
298 break;
299 case RET_VOID:
300 input_void = 1;
301 break;
302 case RET_UCHAR:
303 p = uim_malloc(sizeof(unsigned char));
304 *((unsigned char *)p) = C_CHAR(CDR(arg_));
305 arg_types[i] = &ffi_type_uchar;
306 arg_values[i] = p;
307 break;
308 case RET_SCHAR:
309 p = uim_malloc(sizeof(signed char));
310 *((signed char *)p) = C_CHAR(CDR(arg_));
311 arg_types[i] = &ffi_type_schar;
312 arg_values[i] = p;
313 break;
314 case RET_USHORT:
315 p = uim_malloc(sizeof(unsigned short));
316 *((unsigned short *)p) = C_INT(CDR(arg_));
317 arg_types[i] = &ffi_type_ushort;
318 arg_values[i] = p;
319 break;
320 case RET_SSHORT:
321 p = uim_malloc(sizeof(unsigned short));
322 *((signed short *)p) = C_INT(CDR(arg_));
323 arg_types[i] = &ffi_type_sshort;
324 arg_values[i] = p;
325 break;
326 case RET_UINT:
327 p = uim_malloc(sizeof(unsigned int));
328 *((unsigned int *)p) = C_INT(CDR(arg_));
329 arg_types[i] = &ffi_type_uint;
330 arg_values[i] = p;
331 break;
332 case RET_SINT:
333 p = uim_malloc(sizeof(signed int));
334 *((signed int *)p) = C_INT(CDR(arg_));
335 arg_types[i] = &ffi_type_sint;
336 arg_values[i] = p;
337 break;
338 case RET_ULONG:
339 p = uim_malloc(sizeof(unsigned long));
340 *((unsigned long *)p) = C_INT(CDR(arg_));
341 arg_types[i] = &ffi_type_ulong;
342 arg_values[i] = p;
343 break;
344 case RET_SLONG:
345 p = uim_malloc(sizeof(signed long));
346 *((signed long *)p) = C_INT(CDR(arg_));
347 arg_types[i] = &ffi_type_slong;
348 arg_values[i] = p;
349 break;
350 case RET_FLOAT:
351 {
352 char *endptr;
353 p = uim_malloc(sizeof(float));
354 *((double *)p) = strtof(REFER_C_STR(CDR(arg_)), &endptr);
355 arg_types[i] = &ffi_type_float;
356 arg_values[i] = p;
357 }
358 break;
359 case RET_DOUBLE:
360 {
361 char *endptr;
362 p = uim_malloc(sizeof(double));
363 *((double *)p) = strtod(REFER_C_STR(CDR(arg_)), &endptr);
364 arg_types[i] = &ffi_type_double;
365 arg_values[i] = p;
366 }
367 break;
368 case RET_STR:
369 p = uim_malloc(sizeof(void *));
370 *((void **)p) = (void *)REFER_C_STR(CDR(arg_));
371 arg_types[i] = &ffi_type_pointer;
372 arg_values[i] = p;
373 break;
374 case RET_PTR:
375 p = uim_malloc(sizeof(void *));
376 if (NULLP(CDR(arg_)))
377 *((void **)p) = NULL;
378 else
379 *((void **)p) = C_PTR(CDR(arg_));
380 arg_types[i] = &ffi_type_pointer;
381 arg_values[i] = p;
382 break;
383 case RET_SCM:
384 p = uim_malloc(sizeof(void *));
385 *((void **)p) = CDR(arg_);
386 arg_types[i] = &ffi_type_pointer;
387 arg_values[i] = p;
388 }
389 argv_ = CDR(argv_);
390 }
391
392 if (input_void)
393 args = 0;
394 status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, args, result_type, arg_types);
395 switch (status) {
396 case FFI_OK:
397 break;
398 case FFI_BAD_TYPEDEF:
399 ffi_strerr_ = ffi_strerr_messages[FFI_STRERR_BAD_TYPEDEF];
400 break;
401 case FFI_BAD_ABI:
402 ffi_strerr_ = ffi_strerr_messages[FFI_STRERR_BAD_ABI];
403 break;
404 default:
405 ffi_strerr_ = ffi_strerr_messages[FFI_STRERR_UNKOWN];
406 }
407
408 if (status == FFI_OK)
409 ffi_call(&cif, (void (*)(void))C_PTR(fun_), result, arg_values);
410
411 for (i = 0; i < args; i++)
412 free(arg_values[i]);
413 free(arg_types);
414 free(arg_values);
415
416 if (status != FFI_OK) {
417 free(result);
418 return uim_scm_f();
419 }
420 ret_ = uim_scm_f();
421
422 switch (return_object_type) {
423 case RET_UNKNOWN:
424 case RET_VOID:
425 break;
426 case RET_UCHAR:
427 ret_ = MAKE_CHAR(*(unsigned char *)result);
428 break;
429 case RET_SCHAR:
430 ret_ = MAKE_CHAR(*(signed char *)result);
431 break;
432 case RET_USHORT:
433 ret_ = MAKE_INT(*(unsigned short *)result);
434 break;
435 case RET_SSHORT:
436 ret_ = MAKE_INT(*(signed short *)result);
437 break;
438 case RET_UINT:
439 ret_ = MAKE_INT(*(unsigned int *)result);
440 break;
441 case RET_SINT:
442 ret_ = MAKE_INT(*(signed int *)result);
443 break;
444 case RET_ULONG:
445 ret_ = MAKE_INT(*(unsigned long *)result);
446 break;
447 case RET_SLONG:
448 ret_ = MAKE_INT(*(signed long *)result);
449 break;
450 case RET_FLOAT:
451 {
452 char str[1024];
453 snprintf(str, sizeof(str), "%f", *((float *)result));
454 ret_ = MAKE_STR(str);
455 }
456 break;
457 case RET_DOUBLE:
458 {
459 char str[1024];
460 snprintf(str, sizeof(str), "%f", *((double *)result));
461 ret_ = MAKE_STR(str);
462 }
463 break;
464 case RET_STR:
465 ret_ = MAKE_STR(*((char **)result));
466 break;
467 case RET_PTR:
468 ret_ = MAKE_PTR(*((void **)result));
469 break;
470 case RET_SCM:
471 ret_ = *(uim_lisp *)result;
472 break;
473 }
474
475 free(result);
476
477 ffi_strerr_ = NULL;
478
479 return ret_;
480 }
481
482 static uim_lisp
c_ffi_function(uim_lisp handler_,uim_lisp result_,uim_lisp funstr_,uim_lisp argv_)483 c_ffi_function(uim_lisp handler_, uim_lisp result_, uim_lisp funstr_, uim_lisp argv_)
484 {
485 uim_lisp fun_ = c_dlsym(handler_, funstr_);
486 if (PTRP(fun_))
487 return c_ffi_call(result_, fun_, argv_);
488 return uim_scm_f();
489 }
490
491 void
uim_plugin_instance_init(void)492 uim_plugin_instance_init(void)
493 {
494 uim_scm_init_proc0("dlstrerr", c_dlstrerr);
495
496 uim_lisp_dlopen_mode_ = make_arg_list(dlopen_mode);
497 uim_scm_gc_protect(&uim_lisp_dlopen_mode_);
498 uim_scm_init_proc0("dlopen-mode", c_uim_lisp_dlopen_mode);
499 uim_scm_init_proc2("dlopen", c_dlopen);
500
501 uim_scm_init_proc1("dlclose", c_dlclose);
502 uim_scm_init_proc2("dlsym", c_dlsym);
503
504 uim_scm_init_proc3("ffi-call", c_ffi_call);
505 uim_scm_init_proc4("ffi-function", c_ffi_function);
506 }
507
508 void
uim_plugin_instance_quit(void)509 uim_plugin_instance_quit(void)
510 {
511 uim_scm_gc_unprotect(&uim_lisp_dlopen_mode_);
512 }
513