The problem is that if you do

        loadlib "/home/rogers/foo/bar/baz"

Parrot_load_lib finds and loads "/home/rogers/foo/bar/baz.so" just fine,
but then the library isn't initialized properly because it attempts to
look up the address of "Parrot_lib_/home/rogers/foo/bar/baz_load" via
dlsym, and similarly if you add an explicit ".so" to the arg.  This
patch does the following:

   1.  Computes a trimmed "lib_name" as the part between the last "/" or
"\" and the last ".", exclusive, and uses that for constructing entry
names for dlsym.

   2.  Attempts to fix an apparent memory leak, via the addition of line
172 to src/dynext.c.  (C heavies, please scrutinize.)

   3.  Calls Parrot_locate_runtime_file on files with explicit
extensions, for completeness.

   4.  Rewrites the test for a missing extension slightly, so that it
won't get confused by a "." in a directory name.  (Not specifically
tested.)

   5.  Includes four new test cases for loadlib, with absolute/partial
pathnames with/without an extension.

   There is also this "#ifdef WIN32" business about dropping a "lib"
prefix from the name if all else fails; I am guessing that this no
longer works, but I have no way of testing it myself, so I haven't tried
to resuscitate it.  But I'm not sure that it ever worked; seems to me
that this would have to be folded into the Parrot_locate_runtime_file
library directory search in order to do the right thing.

                                        -- Bob Rogers
                                           http://rgrjr.dyndns.org/

------------------------------------------------------------------------
Index: src/dynext.c
===================================================================
--- src/dynext.c        (revision 8203)
+++ src/dynext.c        (working copy)
@@ -123,26 +123,41 @@
 /*
 
 =item C<static STRING *
-get_path(Interp *interpreter, STRING *lib, void **handle)>
+get_path(Interp *interpreter, STRING *lib, void **handle, char **lib_name)>
 
-Return path and handle of a dynamic lib.
+Return path and handle of a dynamic lib, setting lib_name to just the filestem
+(i.e. without path or extension) as a freshly-allocated C string.
 
 =cut
 
 */
 
 static STRING *
-get_path(Interp *interpreter, STRING *lib, void **handle)
+get_path(Interp *interpreter, STRING *lib, void **handle, char **lib_name)
 {
     STRING *path;
     char *full_name, *file_name, *file_w_ext = NULL;
+    char *tmp_lib_name, *path_end, *ext_start;
     const char *err;
 
+    /* Find the pure library name, without path or extension.  */
+    file_name = string_to_cstring(interpreter, lib);
+    tmp_lib_name = file_name;
+    path_end = strrchr(tmp_lib_name, '/');
+    if (! path_end)
+        path_end = strrchr(tmp_lib_name, '\\');
+    if (path_end)
+        tmp_lib_name = path_end+1;
+    *lib_name = malloc(strlen(tmp_lib_name)+1);
+    strcpy(*lib_name, tmp_lib_name);
+    ext_start = strrchr(*lib_name, '.');
+    if (ext_start)
+        *ext_start = '\0';
+
     /*
-     * first try file with extension if it got none
+     * first, try to add an extension to the file if it has none.
      */
-    file_name = string_to_cstring(interpreter, lib);
-    if (!strchr(file_name, '.')) {
+    if (! ext_start) {
         file_w_ext = malloc(strlen(file_name) +
                 strlen(PARROT_LOAD_EXT) + 1);
         strcpy(file_w_ext, file_name);
@@ -154,6 +169,7 @@
             if (*handle) {
                 path = string_from_cstring(interpreter, full_name, 0);
                 string_cstring_free(file_name);
+                string_cstring_free(full_name);
                 string_cstring_free(file_w_ext);
                 return path;
             }
@@ -207,16 +223,25 @@
         }
     }
     /*
-     * then the given file name as is
+     * finally, try the given file name as is.  we still use
+     * Parrot_locate_runtime_file so that (a) relative pathnames are searched 
in
+     * the standard locations, and (b) the angle of the slashes are adjusted as
+     * required for non-Unix systems.
      */
-    *handle = Parrot_dlopen(file_name);
-    if (*handle) {
-        path = string_from_cstring(interpreter, file_name, 0);
-        string_cstring_free(file_name);
-        return path;
+    full_name = Parrot_locate_runtime_file(interpreter, file_name,
+                                           PARROT_RUNTIME_FT_DYNEXT);
+    if (full_name) {
+        *handle = Parrot_dlopen(full_name);
+        if (*handle) {
+            path = string_from_cstring(interpreter, full_name, 0);
+            string_cstring_free(file_name);
+            string_cstring_free(full_name);
+            return path;
+        }
     }
     /*
      * and on windows strip a leading "lib"
+     * [shouldn't this happen in Parrot_locate_runtime_file instead?]
      */
 #ifdef WIN32
     if (memcmp(file_name, "lib", 3) == 0) {
@@ -297,9 +322,10 @@
     void (*init_func)(Interp *, PMC *);
     char *cinit_func_name, *cload_func_name;
     PMC *lib_pmc;
+    char *lib_name;    /* library stem without path or extension.  */
 
     UNUSED(initializer);
-    path = get_path(interpreter, lib, &handle);
+    path = get_path(interpreter, lib, &handle, &lib_name);
     if (!path || !handle) {
         /*
          * XXX internal_exception? return PMCNULL?
@@ -325,17 +351,20 @@
      */
     Parrot_block_DOD(interpreter);
     /* get load_func */
-    load_func_name = Parrot_sprintf_c(interpreter, "Parrot_lib_%Ss_load", lib);
+    load_func_name = Parrot_sprintf_c(interpreter, "Parrot_lib_%s_load",
+                                      lib_name);
     cload_func_name = string_to_cstring(interpreter, load_func_name);
     load_func = (PMC * (*)(Interp *))D2FPTR(Parrot_dlsym(handle,
                 cload_func_name));
     string_cstring_free(cload_func_name);
     /* get init_func */
-    init_func_name = Parrot_sprintf_c(interpreter, "Parrot_lib_%Ss_init", lib);
+    init_func_name = Parrot_sprintf_c(interpreter, "Parrot_lib_%s_init",
+                                      lib_name);
     cinit_func_name = string_to_cstring(interpreter, init_func_name);
     init_func = (void (*)(Interp *, PMC *))D2FPTR(Parrot_dlsym(handle,
                 cinit_func_name));
     string_cstring_free(cinit_func_name);
+    string_cstring_free(lib_name);
 
     lib_pmc = Parrot_init_lib(interpreter, load_func, init_func);
 
Index: t/dynclass/foo.t
===================================================================
--- t/dynclass/foo.t    (revision 8203)
+++ t/dynclass/foo.t    (working copy)
@@ -16,7 +16,7 @@
 
 =cut
 
-use Parrot::Test tests => 3;
+use Parrot::Test tests => 7;
 use Parrot::Config;
 
 pir_output_is(<< 'CODE', << 'OUTPUT', "get_integer");
@@ -34,6 +34,99 @@
 42
 OUTPUT
 
+pir_output_is(<< 'CODE', << 'OUTPUT', "loadlib with relative pathname, no 
ext");
+.sub main @MAIN
+    ## load a relative pathname without the extension.  loadlib will convert 
the
+    ## '/' characters to '\\' on windows.
+    $S0 = "runtime/parrot/dynext/foo"
+    loadlib P1, $S0
+
+    ## ensure that we can still make Foo instances.
+    find_type $I0, "Foo"
+    new $P1, $I0
+    $I1 = $P1
+    print $I1
+    print "\n"
+.end
+CODE
+42
+OUTPUT
+
+pir_output_is(<< 'CODE', << 'OUTPUT', "loadlib with absolute pathname, no 
ext");
+.sub main @MAIN
+    ## get cwd in $S0.
+    .include "iglobals.pasm"
+    $P11 = getinterp
+    $P12 = $P11[.IGLOBALS_CONFIG_HASH]
+    $S0 = $P12["prefix"]
+
+    ## convert cwd to an absolute pathname without the extension, and load it.
+    ## this should always find the version in the build directory, since that's
+    ## the only place "make test" will work.
+    $S0 = concat "/runtime/parrot/dynext/foo"
+    loadlib P1, $S0
+
+    ## ensure that we can still make Foo instances.
+    find_type $I0, "Foo"
+    new $P1, $I0
+    $I1 = $P1
+    print $I1
+    print "\n"
+.end
+CODE
+42
+OUTPUT
+
+pir_output_is(<< 'CODE', << 'OUTPUT', "loadlib with relative pathname & ext");
+.sub main @MAIN
+    ## get share_ext in $S0.
+    .include "iglobals.pasm"
+    $P11 = getinterp
+    $P12 = $P11[.IGLOBALS_CONFIG_HASH]
+    $S0 = $P12["share_ext"]
+
+    ## load a relative pathname with an extension.
+    $S0 = concat "runtime/parrot/dynext/foo", $S0
+    loadlib P1, $S0
+
+    ## ensure that we can still make Foo instances.
+    find_type $I0, "Foo"
+    new $P1, $I0
+    $I1 = $P1
+    print $I1
+    print "\n"
+.end
+CODE
+42
+OUTPUT
+
+pir_output_is(<< 'CODE', << 'OUTPUT', "loadlib with absolute pathname & ext");
+.sub main @MAIN
+    ## get cwd in $S0, share_ext in $S1.
+    .include "iglobals.pasm"
+    $P11 = getinterp
+    $P12 = $P11[.IGLOBALS_CONFIG_HASH]
+    $S0 = $P12["prefix"]
+    $S1 = $P12["share_ext"]
+
+    ## convert $S0 to an absolute pathname with extension, and load it.
+    ## this should always find the version in the build directory, since that's
+    ## the only place "make test" will work.
+    $S0 = concat $S0, "/runtime/parrot/dynext/foo"
+    $S0 = concat $S0, $S1
+    loadlib P1, $S0
+
+    ## ensure that we can still make Foo instances.
+    find_type $I0, "Foo"
+    new $P1, $I0
+    $I1 = $P1
+    print $I1
+    print "\n"
+.end
+CODE
+42
+OUTPUT
+
 SKIP: { skip("No BigInt Lib configured", 1) if !$PConfig{gmp};
 
 pir_output_is(<< 'CODE', << 'OUTPUT', "inherited add");

Reply via email to