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
20typedef void (*gen_fn)(void);
21typedef const char *(*str_str_fn)(const char *str);
22typedef int64_t (*str_int_fn)(const char *str);
23typedef const char *(*int_str_fn)(int64_t i);
24typedef 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
41bool 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