xstrncpy saga
[sxemacs] / src / emodules-ng.c
1 /*** emodules-ng.c -- Dynamic Loader routines (via ltdl)
2  *
3  * Copyright (C) 2007 Sebastian Freundt
4  *
5  * Author:  Sebastian Freundt <hroptatyr@sxemacs.org>
6  *
7  * This file is part of SXEmacs.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * 3. Neither the name of the author nor the names of any contributors
21  *    may be used to endorse or promote products derived from this
22  *    software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27  * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
31  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
32  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
33  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
34  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  *
36  ***/
37
38 #include "config.h"
39 #include <stdbool.h>
40 #include "lisp.h"
41 #include "sysdep.h"
42 #include "emodules-ng.h"
43 #include "dynacat.h"
44
45 #if defined HAVE_SYS_STAT_H
46 # include <sys/stat.h>
47 #endif
48
49 #if defined HAVE_LTDL_H  /* go ltdl */
50 # include <ltdl.h>
51 #else
52 # error "Bugger! No code to dlopen() things"
53 #endif
54
55 #define EMODNG_INIT             "init"
56 #define EMODNG_REINIT           "reinit"
57 #define EMODNG_DEINIT           "deinit"
58
59 /* this is internal administration */
60 static lt_dlhandle meself = NULL;
61 static emodng_t emods = NULL;
62
63 typedef struct emod_epcell_s /* endpoint cell */ *emod_epcell_t;
64 static emod_epcell_t emod_endpoints = NULL;
65
66 struct emod_epcell_s {
67         emodng_t emod;
68         emod_epcell_t next;
69 };
70
71 /* Do we do our work quietly? */
72 int load_modules_quietly;
73
74 /* Load path */
75 Lisp_Object Vmodule_load_path, Vmodule_extensions;
76
77 /* lt_dlsym() violates ISO C, so confide the breakage into this function to
78  * avoid warnings. */
79 typedef void(*emodng_f)(void);
80
81 struct emodng_lby_s {
82         lt_dlhandle loaded_by;
83         struct emodng_lby_s *next;
84 };
85
86 struct emodng_s {
87         const char *name;
88         const char *truename;
89         const char *filename;
90         lt_dlhandle dl;
91
92         struct emodng_lby_s *lby;
93
94         emodng_init_f initf;
95         emodng_deinit_f deinitf;
96         emodng_reinit_f reinitf;
97         emodng_docs_f docsf;
98
99         emodng_t *deps;
100         size_t ndeps;
101
102         bool openedp;
103         bool initialisedp;
104         bool docsloadedp;
105
106         /* navigation info */
107         emodng_t next;
108 };
109
110 static Lisp_Object emodng_load_unwind(Lisp_Object unwind_ptr);
111 static Lisp_Object emodng_unload_unwind(Lisp_Object unwind_ptr);
112 static emodng_t emodng_load(const char *name, lt_dlhandle);
113 static bool emodng_unload(const char *name);
114 static void _emodng_open(emodng_t emod);
115 static void _emodng_close(emodng_t emod);
116 static void _emodng_init(emodng_t emod);
117 static void _emodng_deinit(emodng_t emod);
118 static void _emodng_docs(emodng_t emod);
119 static inline emodng_t _emodng_open_dep(emodng_t emod, const char *dep)
120         __attribute__((always_inline));
121 static inline void _emodng_close_dep(emodng_t emod, emodng_t dep)
122         __attribute__((always_inline));
123
124 \f
125 /* some inlines and helper funs */
126 static inline emodng_f
127 lt_dlfun(lt_dlhandle handle, const char *symbol)
128         __attribute__((always_inline));
129 static inline emodng_f
130 lt_dlfun(lt_dlhandle handle, const char *symbol)
131 {
132         void *tmp = lt_dlsym(handle, symbol);
133         return (emodng_f)(long int)tmp;
134 }
135
136 #if defined HAVE_STAT || 1
137 static inline bool
138 __file_exists_p(const char *filename)
139 {
140         struct stat _stbuf;
141         return stat(filename, &_stbuf) == 0;
142 }
143 #endif  /* HAVE_STAT */
144
145 static lt_dlhandle
146 __emodng_open_append_exts(const char *filename)
147 {
148         /* append all specified extensions */
149         size_t nname = strlen(filename);
150         char name[nname + /*length of longest extension:*/24];
151         /* note to myself: better check that length! */
152         char *p = xstpncpy(name, filename, nname+1);
153
154         for (Lisp_Object ext = Vmodule_extensions;
155              CONSP(ext); ext = XCDR(ext)) {
156                 if (UNLIKELY(!STRINGP(XCAR(ext)))) {
157                         continue;
158                 }
159
160                 xstrncpy(p, (const char*)XSTRING_DATA(XCAR(ext)),
161                          name + sizeof(name) - p);
162
163                 if (__file_exists_p(name)) {
164                         EMOD_DEBUG_LOADER("trying %s\n", name);
165                         return lt_dlopen(name);
166                 }
167         }
168         return NULL;
169 }
170
171 static lt_dlhandle
172 __emodng_open_prepend_paths(const char *filename)
173 {
174         /* append all specified extensions */
175         size_t nfilename = strlen(filename);
176
177         /* we used to use Vmodule_load_path here */
178         for (Lisp_Object path = Vload_path;
179              CONSP(path); path = XCDR(path)) {
180                 if (LIKELY((STRINGP(XCAR(path))))) {
181                         char name[(size_t)XSTRING_LENGTH(XCAR(path))
182                                   + 1 /*for the slash*/
183                                   + nfilename /*for the module's actual name*/
184                                   + 1 /*for the terminating \0 char*/];
185                         /* note that we assume here that filename already
186                          * carries the correct extension
187                          * see __emodng_open_prepend_paths_append_exts for
188                          * cope with all the combinations */
189                         char *p;
190                         p = xstpncpy(name,
191                                      (const char*)XSTRING_DATA(XCAR(path)),
192                                      (size_t)XSTRING_LENGTH(XCAR(path)-2));
193                         *p++ = '/';
194                         *p = '\0';
195                         xstrncpy(p, filename, name + sizeof(name) - p);
196
197                         if (__file_exists_p(name)) {
198                                 EMOD_DEBUG_LOADER("trying %s\n", name);
199                                 return lt_dlopen(name);
200                         }
201                 }
202         }
203         return NULL;
204 }
205
206 static lt_dlhandle
207 __emodng_open_prepend_paths_append_exts(const char *filename)
208 {
209         /* append all specified extensions */
210         size_t nfilename = strlen(filename);
211
212         /* we used to use Vmodule_load_path here */
213         for (Lisp_Object path = Vload_path;
214              CONSP(path); path = XCDR(path)) {
215                 if (LIKELY((STRINGP(XCAR(path))))) {
216                         char name[(size_t)XSTRING_LENGTH(XCAR(path))
217                                   + 1 /*for the slash*/
218                                   + nfilename /*for the module's actual name*/
219                                   + 24 /* for the length of the extension
220                                         * BETTER CHECK THAT */
221                                   + 1 /*for the terminating \0 char*/];
222                         /* note that we assume here that filename already
223                          * carries the correct extension
224                          * see __emodng_open_prepend_paths_append_exts for
225                          * cope with all the combinations */
226                         char *p = xstpncpy(
227                                 name,
228                                 (const char*)XSTRING_DATA(XCAR(path)),
229                                 (size_t)XSTRING_LENGTH(XCAR(path))-2);
230                         if (*(p-1) != '/') {
231                                 *p++ = '/';
232                                 *p = '\0';
233                         }
234                         p = xstpncpy(p, filename, name + sizeof(name) - p);
235
236                         /* append all extensions now */
237                         for (Lisp_Object ext = Vmodule_extensions;
238                              CONSP(ext); ext = XCDR(ext)) {
239                                 if (UNLIKELY(!STRINGP(XCAR(ext)))) {
240                                         continue;
241                                 }
242
243                                 xstrncpy(p,(const char*)XSTRING_DATA(XCAR(ext)),
244                                          name + sizeof(name) - p);
245
246                                 if (__file_exists_p(name)) {
247                                         EMOD_DEBUG_LOADER("trying \"%s\"\n",
248                                                           name);
249                                         return lt_dlopen(name);
250                                 }
251                         }
252                 }
253         }
254         return NULL;
255 }
256
257 static void
258 __emodng_open(emodng_t emod)
259 {
260         /* obtain some info to massage the name of the emod */
261         /* determine if emod->name is an absolute filename */
262         bool abso_path_p = emod->name[0] == '/';
263         /* determine if emod->name is a relative filename */
264         bool rel_path_p =
265                 strncmp(emod->name, "./", 2) == 0 ||
266                 strncmp(emod->name, "../", 3) == 0;
267         /* determine if emod->name carries an extension already
268          * we search backward to an occurrence of '.' and forward for a '/'
269          * to make sure it really belongs to the last path component */
270         char *ext_compo = strrchr(emod->name, '.');
271         bool has_ext_p = ext_compo != NULL && strchr(ext_compo, '/') == NULL;
272
273         if (LIKELY(!abso_path_p && !rel_path_p && !has_ext_p)) {
274                 /* append both extension and paths */
275                 emod->dl = __emodng_open_prepend_paths_append_exts(emod->name);
276         } else if (!abso_path_p && !rel_path_p) {
277                 /* just append paths */
278                 emod->dl = __emodng_open_prepend_paths(emod->name);
279         } else if (has_ext_p) {
280                 /* fully featured file name, just dlopen() it */
281                 emod->dl = lt_dlopen(emod->name);
282         } else if (!has_ext_p) {
283                 /* file name with path but sans extension */
284                 emod->dl = __emodng_open_append_exts(emod->name);
285         }
286         /* actually we should cater with those relative file names
287          * more thoroughly, I'm not sure what './' means in libltdl speak */
288         return;
289 }
290
291 static emodng_t
292 __emodng_find_or_create(emodng_t in)
293 {
294 /* try to find the emodule specified by in or create one
295  * if it cannot be found */
296         const lt_dlinfo *info;
297         emodng_t new;
298
299         /* get module info */
300         info = lt_dlgetinfo(in->dl);
301
302         EMOD_DEBUG_LOADER("modinfo %s %s %d\n",
303                           info->filename, info->name, info->ref_count);
304
305         for (emodng_t e = emods; e; e = e->next) {
306                 if (info->name &&
307                     strcmp(info->name, e->truename) == 0) {
308                         /* bingo */
309                         return e;
310                 }
311         }
312
313         /* otherwise we have to create this */
314         new = xnew(struct emodng_s);
315
316         new->truename = xstrdup(info->name);
317         new->filename = xstrdup(info->filename);
318         new->name = xstrdup(in->name);
319         new->dl = in->dl;
320         new->openedp =  new->initialisedp = new->docsloadedp = false;
321
322         new->deps = in->deps;
323         new->ndeps = in->ndeps;
324
325         new->next = emods;
326         return emods = new;
327 }
328
329 static emodng_t
330 __emodng_find(const char *name)
331 {
332 /* try to find the emodule named `name', return its pointer or NULL */
333         emodng_t e;
334
335         for (e = emods; e; e = e->next) {
336                 if (strcmp(name, e->truename) == 0) {
337                         /* bingo */
338                         break;
339                 }
340         }
341
342         if (e == NULL) {
343                 return NULL;
344         }
345
346         /* get module info */
347 #ifdef EMOD_DEBUG_FLAG
348         {
349                 const lt_dlinfo *info = lt_dlgetinfo(e->dl);
350
351                 EMOD_DEBUG_LOADER("modinfo %s %s %d\n",
352                                   info->filename, info->name, info->ref_count);
353         }
354 #endif
355         return e;
356 }
357
358 static void
359 __emodng_remove(emodng_t emod)
360 {
361         /* dehorstify this ... */
362         if (UNLIKELY(emod == NULL)) {
363                 return;
364         }
365
366         /* if emod was the top of the list, just kick it */
367         if (emods && emod == emods) {
368                 emods = emod->next;
369                 return;
370         }
371
372         for (emodng_t e = emods; e; e = e->next) {
373                 if (e->next && e->next->dl == emod->dl) {
374                         e->next = emod->next;
375                         return;
376                 }
377         }
378         return;
379 }
380
381 static inline bool
382 emod_endpoint_p(emodng_t emod)
383 {
384         /* a VERY sloppy approach */
385         for (emod_epcell_t ee = emod_endpoints; ee; ee = ee->next) {
386                 if (ee->emod == emod) {
387                         return true;
388                 }
389         }
390         return false;
391 }
392
393 static inline void
394 _register_endpoint(emodng_t emod)
395 {
396         emod_epcell_t new = xnew(struct emod_epcell_s);
397
398         new->emod = emod;
399         new->next = emod_endpoints;
400         emod_endpoints = new;
401         return;
402 }
403
404 static inline void
405 _unregister_endpoint(emodng_t emod)
406 {
407         /* very unlikely, but dehorstify anyway */
408         if (UNLIKELY(emod == NULL)) {
409                 return;
410         }
411
412         /* maybe we're lucky today and deal with the topmost entry ... */
413         if (emod_endpoints && emod_endpoints->emod == emod) {
414                 emod_epcell_t free_me = emod_endpoints;
415                 emod_endpoints = emod_endpoints->next;
416                 xfree(free_me);
417                 return;
418         }
419
420         /* ... gah, what a fooking mess, traverse the list */
421         for (emod_epcell_t e = emod_endpoints; e && e->next; e = e->next) {
422                 if (e->next->emod == emod) {
423                         emod_epcell_t free_me = e->next;
424                         e->next = e->next->next;
425                         xfree(free_me);
426                         return;
427                 }
428         }
429         return;
430 }
431
432 static inline void
433 free_emodng(emodng_t emod)
434 {
435         xfree(emod->truename);
436         xfree(emod->filename);
437         xfree(emod->name);
438         if (emod->deps) {
439                 xfree(emod->deps);
440         }
441         xfree(emod);
442 }
443
444 \f
445 /* we divide the load phase into two actual phases, open and init
446  * similarly unload is actually deinit and close (yes, in that order, horst)
447  * it is just because we make sure about dependencies and things */
448 static emodng_t
449 emodng_load(const char *name, lt_dlhandle caller)
450 {
451 /* this is the loader chain which opens->inits->imports docs */
452         struct emodng_s _emod = {.name = name};
453         emodng_t emod;
454
455         lt_dlinit();
456         _emodng_open(&_emod);
457         if (UNLIKELY(_emod.dl == NULL)) {
458                 return NULL;
459         }
460
461         /* set up a real emodng_t object and put it into our global emod list */
462         emod = __emodng_find_or_create(&_emod);
463         emod->openedp = true;
464
465         _emodng_init(emod);
466         _emodng_docs(emod);
467
468         return emod;
469 }
470
471 static void
472 _emodng_open(emodng_t emod)
473 {
474         const char **emoddeps;
475
476         EMOD_DEBUG_LOADER("loading %s\n", emod->name);
477         __emodng_open(emod);
478
479         /* check that we're really using a valid handle */
480         if (UNLIKELY(emod->dl == NULL)) {
481                 error("Opening dynamic module \"%s\": %s",
482                       emod->name, lt_dlerror());
483         }
484
485         emod->deps = NULL;
486         emod->ndeps = 0;
487
488         /* checking dependencies */
489         if (LIKELY((emoddeps =
490                     (const char**)lt_dlsym(emod->dl, "dependencies")) ||
491                    (emoddeps =
492                     (const char**)lt_dlsym(emod->dl, "deps")))) {
493                 emodng_t *ed;
494                 size_t ndeps = 0;
495
496                 /* count the deps manually */
497                 for (const char **deps = emoddeps, *dep = *deps;
498                      dep; dep = *++deps, ndeps++);
499
500                 if (ndeps == 0) {
501                         return;
502                 }
503
504                 /* create a big array of deps */
505                 EMOD_DEBUG_LOADER("found %lu deps\n",
506                                   (long unsigned int)ndeps);
507                 ed = emod->deps = xnew_array(emodng_t, emod->ndeps = ndeps);
508
509                 /* load the other modules (deps) first */
510                 for (const char **deps = emoddeps, *dep = *deps;
511                      dep; dep = *++deps) {
512                         *ed++ = _emodng_open_dep(emod, dep);
513                 }
514         } else {
515                 EMOD_DEBUG_LOADER("no deps found ... "
516                                   "hope this is right\n");
517         }
518         return;
519 }
520
521 static void
522 _emodng_init(emodng_t emod)
523 {
524 /* calls initialiser code in the module */
525         if (emod->initialisedp) {
526                 EMOD_DEBUG_LOADER("already initialised\n");
527                 return;
528         }
529
530         EMOD_DEBUG_LOADER("initialising %s\n", emod->truename);
531         if (LIKELY((emod->initf =
532                     (emodng_init_f)lt_dlfun(emod->dl, "init")) != NULL)) {
533                 EMOD_DEBUG_LOADER("found init() ... calling now\n");
534                 (*emod->initf)(emod);
535         } else {
536                 EMOD_DEBUG_LOADER("no init() function found ... "
537                                   "hope this is right\n");
538         }
539         emod->initialisedp = true;
540         return;
541 }
542
543 static void
544 _emodng_docs(emodng_t emod)
545 {
546 /* because we're lisp, we have to care for documentation strings of our loaded
547    stuff, thus call a function docs() if present */
548         if (emod->docsloadedp) {
549                 EMOD_DEBUG_LOADER("already loaded doc strings\n");
550                 return;
551         }
552
553         EMOD_DEBUG_LOADER("loading docs from %s\n", emod->name);
554         if (LIKELY((emod->docsf =
555                     (emodng_docs_f)lt_dlfun(emod->dl, "docs")) != NULL)) {
556                 EMOD_DEBUG_LOADER("found docs() ... calling now\n");
557                 (*emod->docsf)(emod);
558         } else {
559                 EMOD_DEBUG_LOADER("no docs() function found ... "
560                                   "hope this is right\n");
561         }
562         emod->docsloadedp = true;
563         return;
564 }
565
566 static inline emodng_t
567 _emodng_open_dep(emodng_t emod, const char *dep)
568 {
569         EMOD_DEBUG_LOADER("module \"%s\" depends upon \"%s\", "
570                           "hence loading \"%s\"\n",
571                           emod->name, dep, dep);
572         return emodng_load(dep, emod->dl);
573 }
574
575 /* unloader code */
576 static bool
577 emodng_unload(const char *name)
578 {
579         emodng_t e = __emodng_find(name);
580
581         if (e == NULL) {
582                 return false;
583         } else if (!emod_endpoint_p(e)) {
584                 error("Module %s is referenced by other modules\n",
585                       e->truename);
586                 return false;
587         }
588
589         _unregister_endpoint(e);
590         _emodng_deinit(e);
591         _emodng_close(e);
592         lt_dlexit();
593         return true;
594 }
595
596 static void
597 _emodng_close(emodng_t emod)
598 {
599         const lt_dlinfo *info;
600
601         /* get module info */
602         info = lt_dlgetinfo(emod->dl);
603
604         EMOD_DEBUG_LOADER("trying to unload %s (%d)\n",
605                           emod->name, info->ref_count);
606
607         lt_dlclose(emod->dl);
608
609         /* close all the dep modules too */
610         for (size_t i = 0; i < emod->ndeps; i++) {
611                 _emodng_close_dep(emod, emod->deps[i]);
612         }
613
614         if (info->ref_count == 0) {
615                 EMOD_DEBUG_LOADER("kicking %s from the list of loaded mods\n",
616                                   emod->name);
617                 __emodng_remove(emod);
618                 free_emodng(emod);
619         }
620         return;
621 }
622
623 static void
624 _emodng_deinit(emodng_t emod)
625 {
626 /* calls deinitialiser code in the module */
627         EMOD_DEBUG_LOADER("deinitialising %s\n", emod->truename);
628         if (LIKELY((emod->deinitf =
629                     (emodng_init_f)lt_dlfun(emod->dl, "deinit")) != NULL)) {
630                 EMOD_DEBUG_LOADER("found deinit() ... calling now\n");
631                 (*emod->deinitf)(emod);
632         } else {
633                 EMOD_DEBUG_LOADER("no deinit() function found ... "
634                                   "hope this is right\n");
635         }
636         emod->initialisedp = false;
637         return;
638 }
639
640 static inline void
641 _emodng_close_dep(emodng_t emod, emodng_t dep)
642 {
643         if (UNLIKELY(dep == NULL)) {
644                 return;
645         }
646
647         EMOD_DEBUG_LOADER("module \"%s\" depended upon \"%s\", "
648                           "hence unloading \"%s\"\n",
649                           emod->name, dep->name, dep->truename);
650         _emodng_close(dep);
651         return;
652 }
653
654
655 /* unwind code in case something's wrong */
656 static Lisp_Object
657 emodng_load_unwind(Lisp_Object unw)
658 {
659         lt_dlhandle mod_handle = get_dynacat(unw);
660
661         /* if the handle's still vivid, drown him */
662         if (mod_handle) {
663                 lt_dlclose(mod_handle);
664         }
665         return Qnil;
666 }
667
668 static Lisp_Object
669 emodng_unload_unwind(Lisp_Object unw)
670 {
671         lt_dlhandle SXE_UNUSED(mod_handle) = get_dynacat(unw);
672
673         /* if the handle's still vivid, drown him */
674         return Qnil;
675 }
676
677 \f
678 DEFUN("load-module-file", Fload_module_file, 1, 3, "FLoad dynamic module: ", /*
679 Load in a C Emacs Extension module named FILE.
680 The optional NAME and VERSION are used to identify specific modules.
681
682 This function is similar in intent to `load-file' except that it loads
683 in pre-compiled C or C++ code, using dynamic shared objects.  If NAME
684 is specified, then the module is only loaded if its internal name
685 matches the NAME specified.  If VERSION is specified, then the module
686 is only loaded if it matches that VERSION.  This function will check
687 to make sure that the same module is not loaded twice.  Modules are
688 searched for in the same way as Lisp files, except for the file
689 extension.  For a list of valid extensions, see: `module-extensions'
690
691 All symbols in the shared module must be completely resolved in order
692 for this function to be successful.  Any modules which the specified
693 FILE depends on will be automatically loaded.  You can determine which
694 modules have been loaded as dynamic shared objects by examining the
695 return value of the function `list-modules'.
696
697 It is possible, although unwise, to unload modules using `unload-module'.
698 The preferred mechanism for unloading or reloading modules is to quit
699 SXEmacs, and then reload those new or changed modules that are required.
700
701 Messages informing you of the progress of the load are displayed unless
702 the variable `load-modules-quietly' is non-NIL.
703 */
704       (file, unused1, unused2))
705 {
706 /* not mt-safe */
707         const char *mod_name;
708         int speccount = specpdl_depth();
709         struct dynacat_s unw = {.ptr = NULL};
710         Lisp_Object lunw = (Lisp_Object)(long)&unw;
711         emodng_t result;
712
713         CHECK_STRING(file);
714         set_lheader_implementation(&unw.lheader, &lrecord_dynacat);
715         file = Fexpand_file_name(file, Qnil);
716         mod_name = (const char*)XSTRING_DATA(file);
717
718         record_unwind_protect(emodng_load_unwind, lunw);
719         result = emodng_load(mod_name, meself);
720         unbind_to(speccount, Qnil);
721
722         if (result) {
723                 _register_endpoint(result);
724                 return Qt;
725         }
726         return Qnil;
727 }
728
729 DEFUN("unload-module", Funload_module, 1, 1, 0, /*
730 BAD JU-JU! Attempt to unload the emodule named FOO.
731
732 This is a dangerous operation and you should think twice before doing
733 it.  Unloading an emodule can often result in a non-responsive,
734 non-working, or just plain dead SXEmacs session.  It is simply not
735 worth the risk.  Save your buffers and restart SXEmacs, it really is
736 the safest way.
737
738 One last little gotcha... FOO is the _internal_ name of the emodule.
739 The internal name is listed in `list-modules'.
740 */
741       (foo))
742 {
743 /* not mt-safe */
744         const char *mod_name;
745         int speccount = specpdl_depth();
746         struct dynacat_s unw = {.ptr = NULL};
747         Lisp_Object lunw = (Lisp_Object)(long)&unw;
748         bool result;
749
750         CHECK_STRING(foo);
751         set_lheader_implementation(&unw.lheader, &lrecord_dynacat);
752         mod_name = (const char*)XSTRING_DATA(foo);
753
754         record_unwind_protect(emodng_unload_unwind, lunw);
755         result = emodng_unload(mod_name);
756         unbind_to(speccount, Qnil);
757
758         return result ? Qt : Qnil;
759 }
760
761 DEFUN("list-loaded-modules", Flist_loaded_modules, 0, 0, 0,     /*
762 Return a list of loaded modules.
763 */
764       ())
765 {
766         Lisp_Object mlist = Qnil;
767
768         for (emodng_t e = emods; e; e = e->next) {
769                 mlist = Fcons(build_string(e->truename), mlist);
770         }
771         return mlist;
772 }
773
774 void
775 emodng_doc_subr(const char *symname, const char *doc)
776 {
777         Bytecount len = xstrlen(symname);
778         Lisp_Object sym = oblookup(Vobarray, (const Bufbyte *)symname, len);
779         Lisp_Subr *subr;
780
781         if (SYMBOLP(sym) && SUBRP(XSYMBOL(sym)->function)) {
782                 EMOD_DEBUG_LOADER("trying to install docs for #'%s\n", symname);
783                 subr = XSUBR(XSYMBOL(sym)->function);
784 #if 1
785                 subr->doc = xstrdup(doc);
786 #else
787                 subr->doc = doc;
788 #endif
789         } else if (!SUBRP(sym)) {
790                 EMOD_CRITICAL("Bollocks! #'%s is not a subr\n", symname);
791         } else if (!SYMBOLP(sym)) {
792                 EMOD_CRITICAL("Bloody 'ell, #'%s is not even a symbol\n",
793                               symname);
794         }
795         /*
796          * FIXME: I wish there was some way to avoid the xstrdup(). Is it
797          * possible to just set a pointer to the string, or somehow create a
798          * symbol whose value we can point to the constant string? Can someone
799          * look into this?
800          */
801         return;
802 }
803
804 void
805 emodng_doc_sym(const char *symname, const char *doc)
806 {
807         Bytecount len = xstrlen(symname);
808         Lisp_Object sym = oblookup(Vobarray, (const Bufbyte *)symname, len);
809         Lisp_Object docstr;
810         struct gcpro gcpro1;
811
812         EMOD_DEBUG_LOADER("trying to install docs for '%s\n", symname);
813
814         if (SYMBOLP(sym)) {
815                 docstr = build_string(doc);
816                 GCPRO1(docstr);
817                 Fput(sym, Qvariable_documentation, docstr);
818                 UNGCPRO;
819         } else if (!SYMBOLP(sym)) {
820                 EMOD_CRITICAL("wrong doc specification\n");
821         }
822         return;
823 }
824
825 #if defined USE_LTDL_SEARCHPATH
826 static int
827 _adapt_load_path(Lisp_Object sym, Lisp_Object *val,
828                  Lisp_Object in_object, int flags)
829 {
830         Lisp_Object ls;
831         size_t len = 0;
832
833         /* traverse once to obtain the overall length */
834         for (len = 0, ls = *val; CONSP(ls); ls = XCDR(ls)) {
835                 if (LIKELY((STRINGP(XCAR(ls))))) {
836                         len += XSTRING_LENGTH(XCAR(ls)) + /*for colon*/1;
837                 }
838         }
839         /* C99 we need ya! traverse to fill the searchpath */
840         if (LIKELY(len > 0)) {
841                 char sp[len], *p = sp;
842
843                 for (ls = *val; CONSP(ls); ls = XCDR(ls)) {
844                         Lisp_Object lse = XCAR(ls);
845                         if (LIKELY((STRINGP(lse)))) {
846                                 p = xstpncpy(p,
847                                              (const char*)XSTRING_DATA(lse),
848                                              XSTRING_LENGTH(lse));
849                                 *p++ = ':';
850                         }
851                 }
852                 *--p = '\0';
853                 lt_dlsetsearchpath(sp);
854         } else {
855                 lt_dlsetsearchpath(NULL);
856         }
857         EMOD_DEBUG_LOADER("load path %s\n", lt_dlgetsearchpath());
858         return 0;
859 }
860 #endif
861 \f
862 void
863 syms_of_emodng(void)
864 {
865         DEFSUBR(Fload_module_file);
866         DEFSUBR(Funload_module);
867         DEFSUBR(Flist_loaded_modules);
868 }
869
870 void
871 reinit_vars_of_emodng(void)
872 {
873 #if 0
874         meself = lt_dlopen(NULL);
875
876         EMOD_DEBUG_LOADER("load path %s\n", lt_dlgetsearchpath());
877 #ifdef LTDL_SYSSEARCHPATH
878         EMOD_DEBUG_LOADER("sys path %s\n", LTDL_SYSSEARCHPATH);
879 #endif
880 #endif
881 }
882
883 void
884 vars_of_emodng(void)
885 {
886         DEFVAR_BOOL("load-modules-quietly", &load_modules_quietly       /*
887 *Set to t if module loading is to be silent.
888
889 Normally, when loading dynamic modules, Emacs will inform you of its
890 progress, and will display the module name and version if the module
891 is loaded correctly.  Setting this variable to `t' will suppress these
892 messages.  This would normally only be done if `load-module' was being
893 called by a Lisp function.
894                                                                          */ );
895         load_modules_quietly = 0;
896
897 #if defined USE_LTDL_SEARCHPATH
898         DEFVAR_LISP_MAGIC("module-load-path", &Vmodule_load_path        /*
899 *List of directories to search for dynamic modules to load.
900 Each element is a string (directory name) or nil (try default directory).
901
902 Note that elements of this list *may not* begin with "~", so you must
903 call `expand-file-name' on them before adding them to this list.
904
905 Initialized based on EMACSMODULEPATH environment variable, if any, otherwise
906 to default specified the file `paths.h' when SXEmacs was built.  If there
907 were no paths specified in `paths.h', then SXEmacs chooses a default
908 value for this variable by looking around in the file-system near the
909 directory in which the SXEmacs executable resides.
910
911 Due to the nature of dynamic modules, the path names should almost always
912 refer to architecture-dependent directories.  It is unwise to attempt to
913 store dynamic modules in a heterogenous environment.  Some environments
914 are similar enough to each other that SXEmacs will be unable to determine
915 the correctness of a dynamic module, which can have unpredictable results
916 when a dynamic module is loaded.
917                                                                 */,
918                 _adapt_load_path);
919 #else  /* !USE_LTDL_SEARCHPATH */
920         DEFVAR_LISP("module-load-path", &Vmodule_load_path      /*
921 *List of directories to search for dynamic modules to load.
922 Each element is a string (directory name) or nil (try default directory).
923
924 Note that elements of this list *may not* begin with "~", so you must
925 call `expand-file-name' on them before adding them to this list.
926
927 Initialized based on EMACSMODULEPATH environment variable, if any, otherwise
928 to default specified the file `paths.h' when SXEmacs was built.  If there
929 were no paths specified in `paths.h', then SXEmacs chooses a default
930 value for this variable by looking around in the file-system near the
931 directory in which the SXEmacs executable resides.
932
933 Due to the nature of dynamic modules, the path names should almost always
934 refer to architecture-dependent directories.  It is unwise to attempt to
935 store dynamic modules in a heterogenous environment.  Some environments
936 are similar enough to each other that SXEmacs will be unable to determine
937 the correctness of a dynamic module, which can have unpredictable results
938 when a dynamic module is loaded.
939                                                                 */ );
940 #endif
941         Vmodule_load_path = Qnil;
942
943         DEFVAR_LISP("module-extensions", &Vmodule_extensions /*
944 *List of filename extensions to use when searching for dynamic modules.
945 */);
946         Vmodule_extensions =
947                 list5(build_string(".la"),
948                       build_string(".so"),
949                       build_string(".ell"),
950                       build_string(".dll"),
951                       build_string(".dylib"));
952
953         Fprovide(intern("modules"));
954
955         reinit_vars_of_emodng();
956 }
957
958 /* emodules-ng.c ends here */