Ticket #850: loadlib_dots.patch

File loadlib_dots.patch, 13.5 KB (added by jkeenan, 13 years ago)

Final (?) version of patch submitted by Ross McFarland in Sept 2005

  • src/call_list.txt

     
    319319 
    320320# crypt 
    321321t       tt 
     322 
     323v       ip 
     324v       pt 
     325i       ptpPPi 
  • src/dynext.c

     
    122122 
    123123/* 
    124124 
    125 =item C<static STRING * 
    126 get_path(Interp *interpreter, STRING *lib, void **handle, char **lib_name)> 
     125=item C<static char * 
     126filestem(char * full_name)> 
    127127 
    128 Return path and handle of a dynamic lib, setting lib_name to just the filestem 
    129 (i.e. without path or extension) as a freshly-allocated C string. 
     128Returns the file stem, no path and no ext of the parameter full_name. stem is a 
     129freshly allocated c string. 
    130130 
    131131=cut 
    132132 
    133133*/ 
    134134 
    135 static STRING * 
    136 get_path(Interp *interpreter, STRING *lib, void **handle, char **lib_name) 
     135static char * 
     136filestem(char * full_name) 
    137137{ 
    138     STRING *path; 
    139     char *full_name, *file_name, *file_w_ext = NULL; 
    140     char *tmp_lib_name, *path_end, *ext_start; 
    141     const char *err; 
     138    char * stem; 
     139    char * path_end; 
     140    char * extension_loc; 
    142141 
    143     /* Find the pure library name, without path or extension.  */ 
    144     file_name = string_to_cstring(interpreter, lib); 
    145     tmp_lib_name = file_name; 
    146     path_end = strrchr(tmp_lib_name, '/'); 
     142    /* if there's path, remove it */ 
     143    path_end = strrchr(full_name, '/'); 
    147144    if (! path_end) 
    148         path_end = strrchr(tmp_lib_name, '\\'); 
    149     if (path_end) 
    150         tmp_lib_name = path_end+1; 
    151     *lib_name = malloc(strlen(tmp_lib_name)+1); 
    152     strcpy(*lib_name, tmp_lib_name); 
    153     ext_start = strrchr(*lib_name, '.'); 
    154     if (ext_start) 
    155         *ext_start = '\0'; 
     145        path_end = strrchr(full_name, '\\'); 
     146    if (! path_end) 
     147        path_end = full_name; 
     148    else 
     149        path_end++; 
     150    /* path_end now points at the first part of full_name that isn't path */ 
    156151 
    157     /* 
    158      * first, try to add an extension to the file if it has none. 
    159      */ 
    160     if (! ext_start) { 
    161         file_w_ext = malloc(strlen(file_name) + 
    162                 strlen(PARROT_LOAD_EXT) + 1); 
    163         strcpy(file_w_ext, file_name); 
    164         strcat(file_w_ext, PARROT_LOAD_EXT); 
    165         full_name = Parrot_locate_runtime_file(interpreter, file_w_ext, 
     152    stem = strdup(path_end); 
     153 
     154    /* remove extension if there is one */ 
     155    extension_loc = strrchr(stem, '.'); 
     156    if (extension_loc) 
     157        *extension_loc = '\0'; 
     158     
     159    return stem; 
     160} 
     161 
     162/* 
     163 
     164=item C<static const char * 
     165try_load(Interp *interpreter, char *clib, void **handle, STRING **path, char **lib_name)> 
     166 
     167Try loading a the library in clib out of library path or Parrot's runtime 
     168paths. Returns a pointer to err on failure, except not found and path is passed 
     169by reference and used to return path.  Otherwise behavior is the same as 
     170get_path 
     171 
     172=cut 
     173 
     174*/ 
     175 
     176static const char * 
     177try_load(Interp *interpreter, char *clib, void **handle, STRING ** path, char **lib_name) 
     178{ 
     179    char *runtime; 
     180 
     181    *handle = Parrot_dlopen(clib); 
     182    if (*handle) { 
     183        *path = string_from_cstring(interpreter, clib, 0); 
     184        *lib_name = filestem(clib); 
     185    } 
     186    else 
     187    { 
     188        const char *err = Parrot_dlerror(); 
     189        /* FIXME rm: need better way to ignore not found errors */ 
     190        if (err && !strstr(err, "No such file or directory")) 
     191            return err; 
     192 
     193        runtime = Parrot_locate_runtime_file(interpreter, clib, 
    166194                PARROT_RUNTIME_FT_DYNEXT); 
    167         if (full_name) { 
    168             *handle = Parrot_dlopen(full_name); 
     195        if (runtime) 
     196        { 
     197            *handle = Parrot_dlopen(runtime); 
    169198            if (*handle) { 
    170                 path = string_from_cstring(interpreter, full_name, 0); 
    171                 string_cstring_free(file_name); 
    172                 string_cstring_free(full_name); 
    173                 string_cstring_free(file_w_ext); 
    174                 return path; 
     199                *path = string_from_cstring(interpreter, runtime, 0); 
     200                *lib_name = filestem(runtime); 
    175201            } 
    176             err = Parrot_dlerror(); 
    177             fprintf(stderr, "Couldn't load '%s': %s\n", 
    178                     full_name, err ? err : "unknown reason"); 
    179             string_cstring_free(file_name); 
    180             string_cstring_free(full_name); 
    181             string_cstring_free(file_w_ext); 
    182             return NULL; 
    183         } 
    184         /* 
    185          * then file.extension w/o prefix 
    186          */ 
    187         *handle = Parrot_dlopen(file_w_ext); 
    188         if (*handle) { 
    189             path = string_from_cstring(interpreter, file_w_ext, 0); 
    190             string_cstring_free(file_name); 
    191             string_cstring_free(file_w_ext); 
    192             return path; 
    193         } 
    194         string_cstring_free(file_w_ext); 
    195         if (strcmp(PARROT_LOAD_EXT, PARROT_SHARE_EXT)) { 
    196             file_w_ext = malloc(strlen(file_name) + 
    197                     strlen(PARROT_SHARE_EXT) + 1); 
    198             strcpy(file_w_ext, file_name); 
    199             strcat(file_w_ext, PARROT_SHARE_EXT); 
    200             full_name = Parrot_locate_runtime_file(interpreter, file_w_ext, 
    201                     PARROT_RUNTIME_FT_DYNEXT); 
    202             if (full_name) { 
    203                 *handle = Parrot_dlopen(full_name); 
    204                 if (*handle) { 
    205                     path = string_from_cstring(interpreter, full_name, 0); 
    206                     string_cstring_free(file_name); 
    207                     string_cstring_free(file_w_ext); 
    208                     return path; 
    209                 } 
     202            else 
     203            { 
    210204                err = Parrot_dlerror(); 
    211                 fprintf(stderr, "Couldn't load '%s': %s\n", 
    212                         full_name, err ? err : "unknown reason"); 
    213                 string_cstring_free(file_name); 
    214                 string_cstring_free(file_w_ext); 
    215                 return NULL; 
     205                /* FIXME rm: need better way to ignore not found errors */ 
     206                if (err && !strstr(err, "No such file or directory")) 
     207                    return err; 
    216208            } 
    217             /* 
    218              * then file.extension w/o prefix 
    219              */ 
    220             *handle = Parrot_dlopen(file_w_ext); 
    221             if (*handle) { 
    222                 path = string_from_cstring(interpreter, file_w_ext, 0); 
    223                 string_cstring_free(file_name); 
    224                 string_cstring_free(file_w_ext); 
    225                 return path; 
    226             } 
    227             string_cstring_free(file_w_ext); 
    228209        } 
     210        string_cstring_free(runtime); 
    229211    } 
    230     /* 
    231      * finally, try the given file name as is.  we still use 
    232      * Parrot_locate_runtime_file so that (a) relative pathnames are searched in 
    233      * the standard locations, and (b) the angle of the slashes are adjusted as 
    234      * required for non-Unix systems. 
    235      */ 
    236     full_name = Parrot_locate_runtime_file(interpreter, file_name, 
    237                                            PARROT_RUNTIME_FT_DYNEXT); 
    238     if (full_name) { 
    239         *handle = Parrot_dlopen(full_name); 
    240         if (*handle) { 
    241             path = string_from_cstring(interpreter, full_name, 0); 
    242             string_cstring_free(file_name); 
    243             string_cstring_free(full_name); 
    244             return path; 
     212 
     213    return NULL; 
     214} 
     215 
     216/* 
     217 
     218=item C<static STRING * 
     219get_path(Interp *interpreter, STRING *lib, void **handle, char **lib_name)> 
     220 
     221Return path and handle of a dynamic lib, setting lib_name to just the filestem 
     222(i.e. without path or extension) as a freshly-allocated C string. 
     223 
     224=cut 
     225 
     226*/ 
     227 
     228static STRING * 
     229get_path(Interp *interpreter, STRING *lib, void **handle, char **lib_name) 
     230{ 
     231    STRING *path = NULL; 
     232    char *clib = NULL; 
     233    char *clib_mod = NULL; 
     234    char *clib_no_ext = NULL; 
     235    unsigned int i; 
     236    const char * err; 
     237 
     238    clib = string_to_cstring(interpreter, lib); 
     239 
     240    /* clear out dlerror in case it hasn't been */ 
     241    Parrot_dlerror(); 
     242     
     243    /* swap slashes if they're backwards */ 
     244    for (i = 0; i < strlen(clib); i++) 
     245#ifdef WIN32 
     246        if (clib[i] == '/') 
     247            clib[i] = '\\'; 
     248#else 
     249        if (clib[i] == '\\') 
     250            clib[i] = '/'; 
     251#endif 
     252 
     253    /* try to load unmodified */ 
     254    err = try_load(interpreter, clib, handle, &path, lib_name); 
     255    if (err) 
     256    { 
     257        fprintf(stderr, "Couldn't load '%s': %s\n", clib, err); 
     258        goto done; 
     259    } 
     260    if (path) 
     261        goto done; 
     262 
     263    /* FIXME rm: check for mem_sys_allocate failures and handle them the parrot 
     264     * way */ 
     265     
     266    /* try to add extension */ 
     267    clib_mod = mem_sys_allocate(strlen(clib) + strlen(PARROT_LOAD_EXT) + 1); 
     268    strcpy(clib_mod, clib); 
     269    strcat(clib_mod, PARROT_LOAD_EXT); 
     270    err = try_load(interpreter, clib_mod, handle, &path, lib_name); 
     271    if (err) 
     272    { 
     273        fprintf(stderr, "Couldn't load '%s': %s\n", clib_mod, err); 
     274        goto done; 
     275    } 
     276    if (path) 
     277        goto done; 
     278 
     279    /* if ext, remove it (since it's apparently wrong) and put right one on */ 
     280    string_cstring_free(clib_mod); 
     281    clib_mod = NULL; 
     282    clib_no_ext = strdup(clib); 
     283    char *ext_loc = strrchr(clib_no_ext, '.'); 
     284    if (ext_loc) { 
     285        *ext_loc = '\0'; 
     286        clib_mod = mem_sys_allocate(strlen(clib_no_ext) + 
     287                strlen(PARROT_LOAD_EXT) + 1); 
     288        strcpy(clib_mod, clib_no_ext); 
     289        strcat(clib_mod, PARROT_LOAD_EXT); 
     290        err = try_load(interpreter, clib_mod, handle, &path, lib_name); 
     291        if (err) 
     292        { 
     293            fprintf(stderr, "Couldn't load '%s': %s\n", clib_mod, err); 
     294            goto done; 
    245295        } 
     296        if (path) 
     297            goto done; 
    246298    } 
    247     /* 
    248      * and on windows strip a leading "lib" 
    249      * [shouldn't this happen in Parrot_locate_runtime_file instead?] 
    250      */ 
    251 #ifdef WIN32 
    252     if (memcmp(file_name, "lib", 3) == 0) { 
    253         *handle = Parrot_dlopen(file_name + 3); 
    254         if (*handle) { 
    255             path = string_from_cstring(interpreter, file_name + 3, 0); 
    256             string_cstring_free(file_name); 
    257             return path; 
     299 
     300    /* this doesn't work for lib's with path info or the wrong ext, but it's 
     301     * just duplicating old behavior. it used to be wrapped in win32 ifdef's 
     302     * but it doesn't hurt to try it */ 
     303    if (memcmp(clib, "lib", 3) == 0) { 
     304        err = try_load(interpreter, clib + 3, handle, &path, lib_name); 
     305        if (err) 
     306        { 
     307            fprintf(stderr, "Couldn't load '%s': %s\n", clib + 3, err); 
     308            goto done; 
    258309        } 
     310        if (path) 
     311            goto done; 
    259312    } 
    260 #endif 
    261     err = Parrot_dlerror(); 
    262     fprintf(stderr, "Couldn't load '%s': %s\n", 
    263             file_name, err ? err : "unknown reason"); 
    264     string_cstring_free(file_name); 
    265     return NULL; 
     313 
     314    fprintf(stderr, "Couldn't load '%s': No such file or directory\n", clib); 
     315 
     316done: 
     317    string_cstring_free(clib); 
     318    string_cstring_free(clib_mod); 
     319    string_cstring_free(clib_no_ext); 
     320    return path; 
    266321} 
    267322 
    268323/* 
  • t/pmc/nci_loadlib.t

     
     1#! perl -w 
     2# Copyright: 2005 The Perl Foundation.  All Rights Reserved. 
     3# $Id: foo.t 8254 2005-06-02 14:20:39Z leo $ 
     4 
     5=head1 NAME 
     6 
     7t/pmc/nci_loadlib.t - Test loadlib ability to load a library specified in 
     8various maners (name, file, w/extension, w/wrong extension, ...) 
     9 
     10=head1 SYNOPSIS 
     11 
     12        % perl -Ilib t/pmc/nci_loadlib.t 
     13 
     14=head1 DESCRIPTION 
     15 
     16Tests loadlib library loading 
     17 
     18=cut 
     19 
     20use Parrot::Test; 
     21use Parrot::Config; 
     22 
     23# known exts 
     24my @exts = qw/.so .dll .dynlib .nothing/; 
     25# no ext 
     26push @exts, ''; 
     27 
     28my @perms = (); 
     29 
     30# foo out of runtime and with rel win/unix paths (always runs) 
     31foreach (@exts) 
     32{ 
     33    push @perms, 'foo'.$_, 'runtime/parrot/dynext/foo'.$_, 
     34        "runtime\\parrot\\dynext\\foo".$_; 
     35}; 
     36 
     37# if libm is around use it from path and with full win/unix paths 
     38if (-e '/usr/lib/libm.so') 
     39{ 
     40    foreach (@exts) 
     41    { 
     42        push @perms, 'libm'.$_, '/usr/lib/libm'.$_, "\\usr\\lib\\libm".$_; 
     43    }; 
     44} 
     45 
     46# if gtk's lib happens to be around go ahead and test with it for the '.' tests 
     47# path and full win/unix 
     48if (-e '/usr/lib/libgtk-x11-2.0.so') 
     49{ 
     50    foreach (@exts) 
     51    { 
     52        push @perms, 'libgtk-x11-2.0'.$_, '/usr/lib/libgtk-x11-2.0'.$_, 
     53            "\\usr\\lib\\libgtk-x11-2.0".$_; 
     54    }; 
     55} 
     56 
     57# +2 for the lib removal test, below 
     58plan tests => scalar (@perms) + 2; 
     59 
     60# run each of the above permutations constrcuted above 
     61foreach (@perms) 
     62{ 
     63pir_output_is(" 
     64 
     65.sub main 
     66    .local pmc dynlib 
     67    dynlib = loadlib '$_' 
     68    unless dynlib goto FAILED 
     69    print '42' 
     70    goto DONE 
     71FAILED: 
     72    print 'failed to load $_' 
     73DONE: 
     74    end 
     75.end 
     76 
     77", '42', 'load '.$_); 
     78} 
     79 
     80# test out leading lib removal, special b/c we have to have the correct ext 
     81pir_output_is(" 
     82 
     83.sub main 
     84    .local pmc dynlib 
     85    .local pmc interp 
     86    .local pmc conf 
     87    .local string ext 
     88    .local string libname 
     89 
     90    .include 'iglobals.pasm' 
     91    interp = getinterp 
     92    conf = interp[.IGLOBALS_CONFIG_HASH] 
     93    ext = conf['load_ext'] 
     94 
     95    libname = concat 'libfoo', ext 
     96    dynlib = loadlib libname 
     97    unless dynlib goto FAILED 
     98    print '42' 
     99    goto DONE 
     100FAILED: 
     101    print 'failed to load ' 
     102    print libname 
     103DONE: 
     104    end 
     105.end 
     106 
     107", '42', 'load libfoo, lib removal'); 
     108 
     109pir_output_is(" 
     110 
     111.sub main 
     112    .local pmc dynlib 
     113    dynlib = loadlib 'something_that_should_never_exist' 
     114    unless dynlib goto FAILED 
     115    print '42' 
     116    goto DONE 
     117FAILED: 
     118    print 'failed to load something_that_should_never_exist' 
     119DONE: 
     120    end 
     121.end 
     122 
     123", "Couldn't load 'something_that_should_never_exist': No such file or directory 
     124failed to load something_that_should_never_exist", 
     125'fail to load non-existent');