1 /*
2  *  gretl -- Gnu Regression, Econometrics and Time-series Library
3  *  Copyright (C) 2001 Allin Cottrell and Riccardo "Jack" Lucchetti
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #include "libgretl.h"
21 
22 #ifdef WIN32
23 # include <windows.h>
24 #else
25 # include <dlfcn.h>
26 #endif
27 
28 /**
29  * SECTION:plugins
30  * @short_description: accessing gretl plugins
31  * @title: Plugins
32  * @include: libgretl.h
33  *
34  * Some of the functionality of libgretl is provided by plugin
35  * modules that are loaded on demand. Here we have functions for
36  * opening and closing plugins, and for obtaining a pointer to
37  * a symbol from a gretl plugin. These functions should work on
38  * both unix-type systems (including Mac OS X) and MS Windows.
39  *
40  * Note that if you wish to make use of gretl plugins in your own
41  * program, you will have to build and install the plugins (of
42  * course) and you may also have to tell libgretl where to
43  * find them. This can be done via the libgretl function
44  * set_gretl_plugin_path(). For example, if the plugins are
45  * in /opt/gretl/lib/gretl-gtk2 then in a C program you could do:
46  *
47  * set_gretl_plugin_path("/opt/gretl/lib/gretl-gtk2");
48  *
49  */
50 
51 enum {
52     P_XLS_IMPORT = 1,
53     P_XLSX_IMPORT,
54     P_GNUMERIC_IMPORT,
55     P_ODS_IMPORT,
56     P_JOHANSEN,
57     P_VIF,
58     P_LEVERAGE,
59     P_MP_OLS,
60     P_PCA,
61     P_PROGRESS_BAR,
62     P_RANGE_MEAN,
63     P_STATS_TABLES,
64     P_SYSEST,
65     P_TRAMO_X12A,
66     P_NISTCHECK,
67     P_ARMA,
68     P_ARMA_X12,
69     P_GARCH,
70     P_URCDIST,
71     P_KERNEL,
72     P_FRACTAL,
73     P_POISSON,
74     P_MAILER,
75     P_EVIEWS_IMPORT,
76     P_STATA_IMPORT,
77     P_SPSS_IMPORT,
78     P_SAS_IMPORT,
79     P_JMULTI_IMPORT,
80     P_ZIPFILE,
81     P_DPANEL,
82     P_HECKIT,
83     P_ODBC,
84     P_QUANTREG,
85     P_INTREG,
86     P_ANOVA,
87     P_DURATION,
88     P_INTERPOLATE,
89     P_ISO3166,
90     P_BIPROBIT,
91     P_REPROBIT,
92     P_PANURC,
93     P_JSON_GET,
94     P_XML_GET,
95     P_STATA_EXPORT,
96     P_SVM,
97     P_REGLS,
98     P_GEOPLOT,
99     P_PUREBIN,
100     P_BDSTEST,
101     P_LPSOLVE
102 } plugin_codes;
103 
104 struct plugin_info {
105     int pnum;           /* index number of plugin */
106     const char *pname;  /* name of plugin */
107     void *handle;       /* handle obtained via dlopen or similar */
108 };
109 
110 struct plugin_function_info {
111     const char *name;   /* name of function */
112     int index;          /* index of the plugin that supplies it */
113 };
114 
115 struct plugin_info plugins[] = {
116     { 0,                 NULL,              NULL },
117     { P_XLS_IMPORT,      "excel_import",    NULL },
118     { P_XLSX_IMPORT,     "xlsx_import",     NULL },
119     { P_GNUMERIC_IMPORT, "gnumeric_import", NULL },
120     { P_ODS_IMPORT,      "ods_import",      NULL },
121     { P_JOHANSEN,        "johansen",        NULL },
122     { P_VIF,             "vif",             NULL },
123     { P_LEVERAGE,        "leverage",        NULL },
124     { P_MP_OLS,          "mp_ols",          NULL },
125     { P_PCA,             "pca",             NULL },
126     { P_PROGRESS_BAR,    "progress_bar",    NULL },
127     { P_RANGE_MEAN,      "range-mean",      NULL },
128     { P_STATS_TABLES,    "stats_tables",    NULL },
129     { P_SYSEST,          "sysest",          NULL },
130     { P_TRAMO_X12A,      "tramo-x12a",      NULL },
131     { P_NISTCHECK,       "nistcheck",       NULL },
132     { P_ARMA,            "arma",            NULL },
133     { P_ARMA_X12,        "arma_x12",        NULL },
134     { P_GARCH,           "garch",           NULL },
135     { P_URCDIST,         "urcdist",         NULL },
136     { P_KERNEL,          "kernel",          NULL },
137     { P_FRACTAL,         "fractals",        NULL },
138     { P_POISSON,         "poisson",         NULL },
139     { P_MAILER,          "mailer",          NULL },
140     { P_EVIEWS_IMPORT,   "eviews_import",   NULL },
141     { P_STATA_IMPORT,    "stata_import",    NULL },
142     { P_SPSS_IMPORT,     "spss_import",     NULL },
143     { P_SAS_IMPORT,      "sas_import",      NULL },
144     { P_JMULTI_IMPORT,   "jmulti_import",   NULL },
145     { P_ZIPFILE,         "gretlzip",        NULL },
146     { P_DPANEL,          "dpanel",          NULL },
147     { P_HECKIT,          "heckit",          NULL },
148     { P_ODBC,            "odbc_import",     NULL },
149     { P_QUANTREG,        "quantreg",        NULL },
150     { P_INTREG,          "interval",        NULL },
151     { P_ANOVA,           "anova",           NULL },
152     { P_DURATION,        "duration",        NULL },
153     { P_INTERPOLATE,     "interpolate",     NULL },
154     { P_ISO3166,         "iso3166",         NULL },
155     { P_BIPROBIT,        "biprobit",        NULL },
156     { P_REPROBIT,        "reprobit",        NULL },
157     { P_PANURC,          "panurc",          NULL },
158     { P_JSON_GET,        "json_get",        NULL },
159     { P_XML_GET,         "xml_get",         NULL },
160     { P_STATA_EXPORT,    "stata_export",    NULL },
161     { P_SVM,             "svm",             NULL },
162     { P_REGLS,           "regls",           NULL },
163     { P_GEOPLOT,         "geoplot",         NULL },
164     { P_PUREBIN,         "purebin",         NULL },
165     { P_BDSTEST,         "bdstest",         NULL },
166     { P_LPSOLVE,         "lpsolve",         NULL },
167 };
168 
169 struct plugin_function_info plugin_functions[] = {
170     /* data importers */
171     { "xls_get_data",      P_XLS_IMPORT },
172     { "xlsx_get_data",     P_XLSX_IMPORT },
173     { "gnumeric_get_data", P_GNUMERIC_IMPORT },
174     { "ods_get_data",      P_ODS_IMPORT },
175     { "wf1_get_data",      P_EVIEWS_IMPORT },
176     { "dta_get_data",      P_STATA_IMPORT },
177     { "sav_get_data",      P_SPSS_IMPORT },
178     { "xport_get_data",    P_SAS_IMPORT },
179     { "jmulti_get_data",   P_JMULTI_IMPORT },
180 
181     /* Johansen cointegration test and VECM */
182     { "johansen_coint_test",   P_JOHANSEN },
183     { "johansen_estimate",     P_JOHANSEN },
184     { "johansen_boot_round",   P_JOHANSEN },
185     { "vecm_test_restriction", P_JOHANSEN },
186     { "trace_pvalue",          P_JOHANSEN },
187 
188     /* influential observations */
189     { "model_leverage",       P_LEVERAGE },
190     { "leverage_data_dialog", P_LEVERAGE },
191 
192     /* collinearity diagnostics */
193     { "compute_vifs", P_VIF },
194     { "compute_bkw",  P_VIF },
195     { "bkw_matrix",   P_VIF },
196 
197     /* GMP (multiple precision) */
198     { "mplsq",                    P_MP_OLS },
199     { "matrix_mp_ols",            P_MP_OLS },
200     { "mp_vector_raise_to_power", P_MP_OLS },
201 #ifdef HAVE_MPFR
202     { "mp_vector_ln",             P_MP_OLS },
203 #endif
204     { "mp_bw_filter",             P_MP_OLS },
205     { "mp_midas_weights",         P_MP_OLS },
206     { "mp_midas_gradient",        P_MP_OLS },
207 
208     /* principal components analysis */
209     { "pca_from_cmatrix", P_PCA },
210 
211     /* GUI progress bar */
212     { "show_progress", P_PROGRESS_BAR },
213 
214     /* range - mean graph */
215     { "range_mean_graph", P_RANGE_MEAN },
216 
217     /* statistical tables */
218     { "dw_lookup",            P_STATS_TABLES },
219     { "rank_sum_lookup",      P_STATS_TABLES },
220     { "stock_yogo_lookup",    P_STATS_TABLES },
221     { "get_IPS_critvals",     P_STATS_TABLES },
222     { "IPS_tbar_moments",     P_STATS_TABLES },
223     { "IPS_tbar_rho_moments", P_STATS_TABLES },
224     { "qlr_asy_pvalue",       P_STATS_TABLES },
225     { "qlr_critval_15_05",    P_STATS_TABLES },
226 
227     /* SUR, 3SLS, FIML */
228     { "system_estimate", P_SYSEST },
229 
230     /* TRAMO/SEATS and X12A */
231     { "write_tx_data",    P_TRAMO_X12A },
232     { "exec_tx_script",   P_TRAMO_X12A },
233     { "adjust_series",    P_TRAMO_X12A },
234     { "linearize_series", P_TRAMO_X12A },
235 
236     /* NIST test suite */
237     { "run_nist_tests", P_NISTCHECK },
238 
239     /* modeling */
240     { "arma_model",        P_ARMA },
241     { "arma_x12_model",    P_ARMA_X12 },
242     { "garch_model",       P_GARCH },
243     { "count_data_estimate", P_POISSON },
244     { "heckit_estimate",   P_HECKIT },
245     { "interval_estimate", P_INTREG },
246     { "tobit_via_intreg",  P_INTREG },
247     { "biprobit_estimate",   P_BIPROBIT },
248     { "biprobit_adjust_vcv", P_BIPROBIT },
249     { "reprobit_estimate", P_REPROBIT },
250 
251     /* Dickey-Fuller test p-values */
252     { "mackinnon_pvalue",  P_URCDIST },
253     { "dfgls_pvalue",      P_URCDIST },
254 
255     /* kernel density estimation */
256     { "kernel_density",        P_KERNEL },
257     { "array_kernel_density",  P_KERNEL },
258     { "kernel_density_matrix", P_KERNEL },
259     { "multiple_kd_matrix",    P_KERNEL },
260 
261     /* Hurst exponent estimation */
262     { "hurst_exponent", P_FRACTAL },
263 
264     /* Send email */
265     { "email_file", P_MAILER },
266 
267     /* zip and unzip */
268     { "gretl_native_make_zipfile", P_ZIPFILE},
269     { "gretl_native_unzip",        P_ZIPFILE},
270     { "gretl_native_zip_datafile", P_ZIPFILE},
271 
272     /* Dynamic panel data estimation */
273     { "dpd_estimate", P_DPANEL},
274 
275     /* ODBC */
276     { "gretl_odbc_check_dsn", P_ODBC},
277     { "gretl_odbc_get_data",  P_ODBC},
278 
279     /* quantreg */
280     { "rq_driver",  P_QUANTREG},
281     { "lad_driver", P_QUANTREG},
282 
283     /* analysis of variance */
284     { "gretl_anova", P_ANOVA},
285 
286     /* duration models */
287     { "duration_estimate", P_DURATION},
288 
289     /* temporal aggregation */
290     { "time_disaggregate", P_INTERPOLATE},
291 
292     /* ISO 3166 country codes */
293     { "iso_country", P_ISO3166},
294     { "iso_country_array", P_ISO3166},
295     { "iso_country_series", P_ISO3166},
296 
297     /* panel unit roots/cointegration */
298     { "real_levin_lin", P_PANURC},
299 
300     /* parsing or writing JSON data */
301     { "json_get_string", P_JSON_GET},
302     { "json_get_bundle", P_JSON_GET},
303     { "json_bundle_get_terminals", P_JSON_GET},
304     { "bundle_to_json", P_JSON_GET},
305 
306     /* parsing XML data */
307     { "xml_get", P_XML_GET},
308 
309     /* exporting data in dta format */
310     { "stata_export", P_STATA_EXPORT},
311 
312     /* libsvm interface */
313     { "gretl_svm_driver", P_SVM},
314 
315     /* regularized least squares */
316     { "gretl_regls",  P_REGLS},
317     { "regls_xv_mpi", P_REGLS},
318 
319     /* shapefile handling */
320     { "map_get_data",    P_GEOPLOT},
321     { "shp_get_bundle",  P_GEOPLOT},
322     { "geoplot", P_GEOPLOT},
323 
324     /* "pure" binary data read/write */
325     { "purebin_read_data",  P_PUREBIN},
326     { "purebin_write_data", P_PUREBIN},
327     { "purebin_read_subset",   P_PUREBIN},
328     { "purebin_read_varnames", P_PUREBIN},
329 
330     /* BDS nonlinearity test */
331     { "bdstest", P_BDSTEST},
332 
333     /* interface to lpsolve library */
334     { "gretl_lpsolve", P_LPSOLVE},
335 
336     /* sentinel */
337     { NULL, 0 }
338 };
339 
gretl_plugin_hash_init(void)340 static GHashTable *gretl_plugin_hash_init (void)
341 {
342     GHashTable *ht;
343     int i;
344 
345     ht = g_hash_table_new(g_str_hash, g_str_equal);
346 
347     /* Record the plugin index of each plugin function
348        in a hash table under the key of the function
349        name, permitting quick look-up.
350     */
351     for (i=0; plugin_functions[i].name != NULL; i++) {
352 	g_hash_table_insert(ht, (gpointer) plugin_functions[i].name,
353 			    GINT_TO_POINTER(plugin_functions[i].index));
354     }
355 
356     return ht;
357 }
358 
plugin_index_lookup(const char * name)359 static int plugin_index_lookup (const char *name)
360 {
361     static GHashTable *pht;
362     gpointer ptr;
363 
364     if (name == NULL) {
365 	/* cleanup signal */
366 	if (pht != NULL) {
367 	    g_hash_table_destroy(pht);
368 	    pht = NULL;
369 	}
370 	return 0;
371     }
372 
373     if (pht == NULL) {
374 	/* construct hash table if not already done */
375 	pht = gretl_plugin_hash_init();
376     }
377 
378     ptr = g_hash_table_lookup(pht, name);
379 
380     return ptr == NULL ? 0 : GPOINTER_TO_INT(ptr);
381 }
382 
383 /**
384  * gretl_dlopen:
385  * @path: full path to the shared object to be opened.
386  * @now: on *nix, if non-zero we call dlopen with the flag
387  * RTLD_NOW, else we use RTLD_LAZY.
388  *
389  * Cross-platform wrapper for opening a shared code object
390  * on MS Windows or unix-type systems (including OS X).
391  *
392  * Returns: handle to the shared object.
393  */
394 
gretl_dlopen(const char * path,int now)395 void *gretl_dlopen (const char *path, int now)
396 {
397     void *handle = NULL;
398 
399 #ifdef WIN32
400     if (strstr(path, "R.dll")) {
401 	handle = LoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
402     } else {
403 	handle = LoadLibrary(path);
404     }
405 #else
406     handle = dlopen(path, (now)? RTLD_NOW : RTLD_LAZY);
407 #endif
408 
409     if (handle == NULL) {
410         gretl_errmsg_sprintf(_("Failed to load plugin: %s"), path);
411 #if !defined(WIN32)
412 	fprintf(stderr, "%s\n", dlerror());
413 #endif
414     }
415 
416     return handle;
417 }
418 
419 /**
420  * gretl_dlsym:
421  * @handle: handle to shared object; see gretl_dlopen().
422  * @name: name of symbol to look up.
423  *
424  * Cross-platform wrapper for obtaining a pointer to
425  * a named symbol in a shared object represented by
426  * @handle.
427  *
428  * Returns: pointer corresponding to @name, or NULL.
429  */
430 
gretl_dlsym(void * handle,const char * name)431 void *gretl_dlsym (void *handle, const char *name)
432 {
433 #ifdef WIN32
434     return GetProcAddress(handle, name);
435 #else
436     return dlsym(handle, name);
437 #endif
438 }
439 
440 #if defined(WIN32) || defined(__CYGWIN__)
441 # define PLUGIN_EXT ".dll"
442 #else
443 # define PLUGIN_EXT ".so"
444 #endif
445 
get_plugin_handle_by_index(int i)446 static void *get_plugin_handle_by_index (int i)
447 {
448     void *handle = plugins[i].handle;
449 
450     if (handle == NULL) {
451 	/* not opened yet */
452 	char pluginpath[MAXLEN];
453 
454 	strcpy(pluginpath, gretl_plugin_path());
455 	strcat(pluginpath, plugins[i].pname);
456 	strcat(pluginpath, PLUGIN_EXT);
457 
458 	handle = gretl_dlopen(pluginpath, 0);
459 	/* store the pointer we got */
460 	plugins[i].handle = handle;
461     }
462 
463     return handle;
464 }
465 
get_function_address(void * handle,const char * name)466 static void *get_function_address (void *handle,
467 				   const char *name)
468 {
469     void *funp;
470 
471 #ifdef WIN32
472     funp = GetProcAddress(handle, name);
473 #else
474     funp = dlsym(handle, name);
475     if (funp == NULL) {
476 	gchar *munged = g_strdup_printf("_%s", name);
477 
478 	funp = dlsym(handle, munged);
479 	if (funp == NULL) {
480 	    fprintf(stderr, "%s\n", dlerror());
481 	}
482 	g_free(munged);
483     }
484 #endif
485 
486     return funp;
487 }
488 
plugins_cleanup(void)489 void plugins_cleanup (void)
490 {
491     int i, n = sizeof(plugins) / sizeof(plugins[0]);
492 
493     /* note: plugins[0] is a dummy entry */
494 
495     for (i=1; i<n; i++) {
496 	if (plugins[i].handle != NULL) {
497 	    close_plugin(plugins[i].handle);
498 	    plugins[i].handle = NULL;
499 	}
500     }
501 
502     /* tear down the plugin look-up hash table */
503     plugin_index_lookup(NULL);
504 }
505 
506 /**
507  * get_plugin_function:
508  * @funcname: name of function to access.
509  *
510  * Looks up @funcname in gretl's internal plugin table and
511  * attempts to open the plugin object file that offers the
512  * given function.
513  *
514  * Returns: function pointer, or NULL on failure.
515  */
516 
get_plugin_function(const char * funcname)517 void *get_plugin_function (const char *funcname)
518 {
519     int i = plugin_index_lookup(funcname);
520     void *funp = NULL;
521 
522 #if !HAVE_GMP
523     if (i == P_MP_OLS) {
524 	gretl_errmsg_set("GMP is not supported in this build");
525 	return NULL;
526     }
527 #endif
528 
529     if (i > 0) {
530 	void *handle = get_plugin_handle_by_index(i);
531 
532 	if (handle != NULL) {
533 	    funp = get_function_address(handle, funcname);
534 	} else {
535 	    fprintf(stderr, "%s: get_function_address failed\n",
536 		    funcname);
537 	}
538     } else {
539 	fprintf(stderr, "%s: plugin_index_lookup failed\n",
540 		funcname);
541     }
542 
543     if (funp == NULL) {
544 	gretl_errmsg_set(_("Couldn't load plugin function"));
545     }
546 
547     return funp;
548 }
549 
get_plugin_handle_by_name(const char * name)550 static void *get_plugin_handle_by_name (const char *name)
551 {
552     char pluginpath[MAXLEN];
553 
554     strcpy(pluginpath, gretl_plugin_path());
555     strcat(pluginpath, name);
556     strcat(pluginpath, PLUGIN_EXT);
557 
558     return gretl_dlopen(pluginpath, 0);
559 }
560 
get_packaged_C_function(const char * pkgname,const char * funcname,void ** handle)561 void *get_packaged_C_function (const char *pkgname,
562 			       const char *funcname,
563 			       void **handle)
564 {
565     void *funp;
566 
567     *handle = get_plugin_handle_by_name(pkgname);
568     if (*handle == NULL) {
569 	return NULL;
570     }
571 
572     funp = get_function_address(*handle, funcname);
573 
574     if (funp == NULL) {
575 	gretl_errmsg_set(_("Couldn't load plugin function"));
576 	close_plugin(*handle);
577 	*handle = NULL;
578     }
579 
580     return funp;
581 }
582 
583 /* For use with valgrind: if you want to trace memory
584    leaks into plugin code you have to keep the plugins
585    open at program termination. So you can define this
586    as non-zero temporarily.
587 */
588 #define KEEP_PLUGINS_OPEN 0
589 
590 /**
591  * close_plugin:
592  * @handle: pointer obtained via the handle argument to
593  * get_plugin_function().
594  *
595  * Closes a shared plugin object.
596  */
597 
close_plugin(void * handle)598 void close_plugin (void *handle)
599 {
600 #if KEEP_PLUGINS_OPEN
601     return;
602 #endif
603     if (handle != NULL) {
604 #ifdef WIN32
605 	FreeLibrary(handle);
606 #else
607 	dlclose(handle);
608 #endif
609     }
610 }
611