1 | // This is an open source non-commercial project. Dear PVS-Studio, please check |
2 | // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com |
3 | |
4 | /// Functions for using external native libraries |
5 | |
6 | #include <stdbool.h> |
7 | #include <stdint.h> |
8 | #include <uv.h> |
9 | |
10 | #include "nvim/os/dl.h" |
11 | #include "nvim/os/os.h" |
12 | #include "nvim/memory.h" |
13 | #include "nvim/message.h" |
14 | |
15 | /// possible function prototypes that can be called by os_libcall() |
16 | /// int -> int |
17 | /// int -> string |
18 | /// string -> string |
19 | /// string -> int |
20 | typedef void (*gen_fn)(void); |
21 | typedef const char *(*str_str_fn)(const char *str); |
22 | typedef int64_t (*str_int_fn)(const char *str); |
23 | typedef const char *(*int_str_fn)(int64_t i); |
24 | typedef int64_t (*int_int_fn)(int64_t i); |
25 | |
26 | /// os_libcall - call a function in a dynamic loadable library |
27 | /// |
28 | /// an example of calling a function that takes a string and returns an int: |
29 | /// |
30 | /// int64_t int_out = 0; |
31 | /// os_libcall("mylib.so", "somefn", "string-argument", 0, NULL, &int_out); |
32 | /// |
33 | /// @param libname the name of the library to load (e.g.: libsomething.so) |
34 | /// @param funcname the name of the library function (e.g.: myfunc) |
35 | /// @param argv the input string, NULL when using `argi` |
36 | /// @param argi the input integer, not used when using `argv` != NULL |
37 | /// @param[out] str_out an allocated output string, caller must free if |
38 | /// not NULL. NULL when using `int_out`. |
39 | /// @param[out] int_out the output integer param |
40 | /// @return true on success, false on failure |
41 | bool os_libcall(const char *libname, |
42 | const char *funcname, |
43 | const char *argv, |
44 | int64_t argi, |
45 | char **str_out, |
46 | int64_t *int_out) |
47 | { |
48 | if (!libname || !funcname) { |
49 | return false; |
50 | } |
51 | |
52 | uv_lib_t lib; |
53 | |
54 | // open the dynamic loadable library |
55 | if (uv_dlopen(libname, &lib)) { |
56 | EMSG2(_("dlerror = \"%s\"" ), uv_dlerror(&lib)); |
57 | uv_dlclose(&lib); |
58 | return false; |
59 | } |
60 | |
61 | // find and load the requested function in the library |
62 | gen_fn fn; |
63 | if (uv_dlsym(&lib, funcname, (void **) &fn)) { |
64 | EMSG2(_("dlerror = \"%s\"" ), uv_dlerror(&lib)); |
65 | uv_dlclose(&lib); |
66 | return false; |
67 | } |
68 | |
69 | // call the library and save the result |
70 | // TODO(aktau): catch signals and use jmp (if available) to handle |
71 | // exceptions. jmp's on Unix seem to interact trickily with signals as |
72 | // well. So for now we only support those libraries that are well-behaved. |
73 | if (str_out) { |
74 | str_str_fn sfn = (str_str_fn) fn; |
75 | int_str_fn ifn = (int_str_fn) fn; |
76 | |
77 | const char *res = argv ? sfn(argv) : ifn(argi); |
78 | |
79 | // assume that ptr values of NULL, 1 or -1 are illegal |
80 | *str_out = (res && (intptr_t) res != 1 && (intptr_t) res != -1) |
81 | ? xstrdup(res) : NULL; |
82 | } else { |
83 | str_int_fn sfn = (str_int_fn) fn; |
84 | int_int_fn ifn = (int_int_fn) fn; |
85 | *int_out = argv ? sfn(argv) : ifn(argi); |
86 | } |
87 | |
88 | // free the library |
89 | uv_dlclose(&lib); |
90 | |
91 | return true; |
92 | } |
93 | |