int rb_feature_provided(const char *feature, const char **loading) { const char *ext = strrchr(feature, '.'); VALUE fullpath = 0; if (*feature == '.' && (feature[1] == '/' || strncmp(feature+1, "./", 2) == 0)) { fullpath = rb_file_expand_path_fast(rb_get_path(rb_str_new2(feature)), Qnil); feature = RSTRING_PTR(fullpath); } if (ext && !strchr(ext, '/')) { if (IS_RBEXT(ext)) { if (rb_feature_p(feature, ext, TRUE, FALSE, loading)) return TRUE; return FALSE; } else if (IS_SOEXT(ext) || IS_DLEXT(ext)) { if (rb_feature_p(feature, ext, FALSE, FALSE, loading)) return TRUE; return FALSE; } } if (rb_feature_p(feature, 0, TRUE, FALSE, loading)) return TRUE; RB_GC_GUARD(fullpath); return FALSE; }
static VALUE loaded_feature_path(const char *name, long vlen, const char *feature, long len, int type, VALUE load_path) { long i; for (i = 0; i < RARRAY_LEN(load_path); ++i) { VALUE p = RARRAY_PTR(load_path)[i]; const char *s = StringValuePtr(p); long n = RSTRING_LEN(p); if (vlen < n + len + 1) continue; if (n && (strncmp(name, s, n) || name[n] != '/')) continue; if (strncmp(name + n + 1, feature, len)) continue; if (name[n+len+1] && name[n+len+1] != '.') continue; switch (type) { case 's': if (IS_DLEXT(&name[n+len+1])) return p; break; case 'r': if (IS_RBEXT(&name[n+len+1])) return p; break; default: return p; } } return 0; }
/* This searches `load_path` for a value such that name == "#{load_path[i]}/#{feature}" if `feature` is a suffix of `name`, or otherwise name == "#{load_path[i]}/#{feature}#{ext}" for an acceptable string `ext`. It returns `load_path[i].to_str` if found, else 0. If type is 's', then `ext` is acceptable only if IS_DLEXT(ext); if 'r', then only if IS_RBEXT(ext); otherwise `ext` may be absent or have any value matching `%r{^\.[^./]*$}`. */ static VALUE loaded_feature_path(const char *name, long vlen, const char *feature, long len, int type, VALUE load_path) { long i; long plen; const char *e; if (vlen < len+1) return 0; if (strchr(feature, '.') && !strncmp(name+(vlen-len), feature, len)) { plen = vlen - len; } else { for (e = name + vlen; name != e && *e != '.' && *e != '/'; --e); if (*e != '.' || e-name < len || strncmp(e-len, feature, len)) return 0; plen = e - name - len; } if (plen > 0 && name[plen-1] != '/') { return 0; } if (type == 's' ? !IS_DLEXT(&name[plen+len]) : type == 'r' ? !IS_RBEXT(&name[plen+len]) : 0) { return 0; } /* Now name == "#{prefix}/#{feature}#{ext}" where ext is acceptable (possibly empty) and prefix is some string of length plen. */ if (plen > 0) --plen; /* exclude '.' */ for (i = 0; i < RARRAY_LEN(load_path); ++i) { VALUE p = RARRAY_AREF(load_path, i); const char *s = StringValuePtr(p); long n = RSTRING_LEN(p); if (n != plen) continue; if (n && strncmp(name, s, n)) continue; return p; } return 0; }
static VALUE loaded_feature_path(const char *name, long vlen, const char *feature, long len, int type, VALUE load_path) { long i; long plen; const char *e; if(vlen < len) return 0; if (!strncmp(name+(vlen-len),feature,len)){ plen = vlen - len - 1; } else { for (e = name + vlen; name != e && *e != '.' && *e != '/'; --e); if (*e!='.' || e-name < len || strncmp(e-len,feature,len) ) return 0; plen = e - name - len - 1; } for (i = 0; i < RARRAY_LEN(load_path); ++i) { VALUE p = RARRAY_PTR(load_path)[i]; const char *s = StringValuePtr(p); long n = RSTRING_LEN(p); if (n != plen ) continue; if (n && (strncmp(name, s, n) || name[n] != '/')) continue; switch (type) { case 's': if (IS_DLEXT(&name[n+len+1])) return p; break; case 'r': if (IS_RBEXT(&name[n+len+1])) return p; break; default: return p; } } return 0; }
static int search_required(VALUE fname, volatile VALUE *path, int safe_level) { VALUE tmp; char *ext, *ftptr; int type, ft = 0; const char *loading; *path = 0; ext = strrchr(ftptr = RSTRING_PTR(fname), '.'); if (ext && !strchr(ext, '/')) { if (IS_RBEXT(ext)) { if (rb_feature_p(ftptr, ext, TRUE, FALSE, &loading)) { if (loading) *path = rb_filesystem_str_new_cstr(loading); return 'r'; } if ((tmp = rb_find_file_safe(fname, safe_level)) != 0) { ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); if (!rb_feature_p(ftptr, ext, TRUE, TRUE, &loading) || loading) *path = tmp; return 'r'; } return 0; } else if (IS_SOEXT(ext)) { if (rb_feature_p(ftptr, ext, FALSE, FALSE, &loading)) { if (loading) *path = rb_filesystem_str_new_cstr(loading); return 's'; } tmp = rb_str_subseq(fname, 0, ext - RSTRING_PTR(fname)); #ifdef DLEXT2 OBJ_FREEZE(tmp); if (rb_find_file_ext_safe(&tmp, loadable_ext + 1, safe_level)) { ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading) *path = tmp; return 's'; } #else rb_str_cat2(tmp, DLEXT); OBJ_FREEZE(tmp); if ((tmp = rb_find_file_safe(tmp, safe_level)) != 0) { ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading) *path = tmp; return 's'; } #endif } else if (IS_DLEXT(ext)) { if (rb_feature_p(ftptr, ext, FALSE, FALSE, &loading)) { if (loading) *path = rb_filesystem_str_new_cstr(loading); return 's'; } if ((tmp = rb_find_file_safe(fname, safe_level)) != 0) { ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading) *path = tmp; return 's'; } } } else if ((ft = rb_feature_p(ftptr, 0, FALSE, FALSE, &loading)) == 'r') { if (loading) *path = rb_filesystem_str_new_cstr(loading); return 'r'; } tmp = fname; type = rb_find_file_ext_safe(&tmp, loadable_ext, safe_level); switch (type) { case 0: if (ft) goto statically_linked; ftptr = RSTRING_PTR(tmp); return rb_feature_p(ftptr, 0, FALSE, TRUE, 0); default: if (ft) { statically_linked: if (loading) *path = rb_filesystem_str_new_cstr(loading); return ft; } case 1: ext = strrchr(ftptr = RSTRING_PTR(tmp), '.'); if (rb_feature_p(ftptr, ext, !--type, TRUE, &loading) && !loading) break; *path = tmp; } return type ? 's' : 'r'; }
static int rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const char **fn) { VALUE features, this_feature_index = Qnil, v, p, load_path = 0; const char *f, *e; long i, len, elen, n; st_table *loading_tbl, *features_index; st_data_t data; int type; if (fn) *fn = 0; if (ext) { elen = strlen(ext); len = strlen(feature) - elen; type = rb ? 'r' : 's'; } else { len = strlen(feature); elen = 0; type = 0; } features = get_loaded_features(); features_index = get_loaded_features_index(); st_lookup(features_index, (st_data_t)feature, (st_data_t *)&this_feature_index); /* We search `features` for an entry such that either "#{features[i]}" == "#{load_path[j]}/#{feature}#{e}" for some j, or "#{features[i]}" == "#{feature}#{e}" Here `e` is an "allowed" extension -- either empty or one of the extensions accepted by IS_RBEXT, IS_SOEXT, or IS_DLEXT. Further, if `ext && rb` then `IS_RBEXT(e)`, and if `ext && !rb` then `IS_SOEXT(e) || IS_DLEXT(e)`. If `expanded`, then only the latter form (without load_path[j]) is accepted. Otherwise either form is accepted, *unless* `ext` is false and an otherwise-matching entry of the first form is preceded by an entry of the form "#{features[i2]}" == "#{load_path[j2]}/#{feature}#{e2}" where `e2` matches %r{^\.[^./]*$} but is not an allowed extension. After a "distractor" entry of this form, only entries of the form "#{feature}#{e}" are accepted. In `rb_provide_feature()` and `get_loaded_features_index()` we maintain an invariant that the array `this_feature_index` will point to every entry in `features` which has the form "#{prefix}#{feature}#{e}" where `e` is empty or matches %r{^\.[^./]*$}, and `prefix` is empty or ends in '/'. This includes both match forms above, as well as any distractors, so we may ignore all other entries in `features`. */ if (!NIL_P(this_feature_index)) { for (i = 0; ; i++) { VALUE entry; long index; if (RB_TYPE_P(this_feature_index, T_ARRAY)) { if (i >= RARRAY_LEN(this_feature_index)) break; entry = RARRAY_AREF(this_feature_index, i); } else { if (i > 0) break; entry = this_feature_index; } index = FIX2LONG(entry); v = RARRAY_AREF(features, index); f = StringValuePtr(v); if ((n = RSTRING_LEN(v)) < len) continue; if (strncmp(f, feature, len) != 0) { if (expanded) continue; if (!load_path) load_path = rb_get_expanded_load_path(); if (!(p = loaded_feature_path(f, n, feature, len, type, load_path))) continue; expanded = 1; f += RSTRING_LEN(p) + 1; } if (!*(e = f + len)) { if (ext) continue; return 'u'; } if (*e != '.') continue; if ((!rb || !ext) && (IS_SOEXT(e) || IS_DLEXT(e))) { return 's'; } if ((rb || !ext) && (IS_RBEXT(e))) { return 'r'; } } } loading_tbl = get_loading_table(); f = 0; if (!expanded) { struct loaded_feature_searching fs; fs.name = feature; fs.len = len; fs.type = type; fs.load_path = load_path ? load_path : rb_get_expanded_load_path(); fs.result = 0; st_foreach(loading_tbl, loaded_feature_path_i, (st_data_t)&fs); if ((f = fs.result) != 0) { if (fn) *fn = f; goto loading; } } if (st_get_key(loading_tbl, (st_data_t)feature, &data)) { if (fn) *fn = (const char*)data; loading: if (!ext) return 'u'; return !IS_RBEXT(ext) ? 's' : 'r'; } else { VALUE bufstr; char *buf; static const char so_ext[][4] = { ".so", ".o", }; if (ext && *ext) return 0; bufstr = rb_str_tmp_new(len + DLEXT_MAXLEN); buf = RSTRING_PTR(bufstr); MEMCPY(buf, feature, char, len); for (i = 0; (e = loadable_ext[i]) != 0; i++) { strlcpy(buf + len, e, DLEXT_MAXLEN + 1); if (st_get_key(loading_tbl, (st_data_t)buf, &data)) { rb_str_resize(bufstr, 0); if (fn) *fn = (const char*)data; return i ? 's' : 'r'; } } for (i = 0; i < numberof(so_ext); i++) { strlcpy(buf + len, so_ext[i], DLEXT_MAXLEN + 1); if (st_get_key(loading_tbl, (st_data_t)buf, &data)) { rb_str_resize(bufstr, 0); if (fn) *fn = (const char*)data; return 's'; } } rb_str_resize(bufstr, 0); } return 0; }
static int rb_feature_p(const char *feature, const char *ext, int rb, int expanded, const char **fn) { VALUE v, features, p, load_path = 0; const char *f, *e; long i, len, elen, n; st_table *loading_tbl; st_data_t data; int type; if (fn) *fn = 0; if (ext) { elen = strlen(ext); len = strlen(feature) - elen; type = rb ? 'r' : 's'; } else { len = strlen(feature); elen = 0; type = 0; } features = get_loaded_features(); for (i = 0; i < RARRAY_LEN(features); ++i) { v = RARRAY_PTR(features)[i]; f = StringValuePtr(v); if ((n = RSTRING_LEN(v)) < len) continue; if (strncmp(f, feature, len) != 0) { if (expanded) continue; if (!load_path) load_path = rb_get_expanded_load_path(); if (!(p = loaded_feature_path(f, n, feature, len, type, load_path))) continue; expanded = 1; f += RSTRING_LEN(p) + 1; } if (!*(e = f + len)) { if (ext) continue; return 'u'; } if (*e != '.') continue; if ((!rb || !ext) && (IS_SOEXT(e) || IS_DLEXT(e))) { return 's'; } if ((rb || !ext) && (IS_RBEXT(e))) { return 'r'; } } loading_tbl = get_loading_table(); if (loading_tbl) { f = 0; if (!expanded) { struct loaded_feature_searching fs; fs.name = feature; fs.len = len; fs.type = type; fs.load_path = load_path ? load_path : rb_get_load_path(); fs.result = 0; st_foreach(loading_tbl, loaded_feature_path_i, (st_data_t)&fs); if ((f = fs.result) != 0) { if (fn) *fn = f; goto loading; } } if (st_get_key(loading_tbl, (st_data_t)feature, &data)) { if (fn) *fn = (const char*)data; loading: if (!ext) return 'u'; return !IS_RBEXT(ext) ? 's' : 'r'; } else { VALUE bufstr; char *buf; if (ext && *ext) return 0; bufstr = rb_str_tmp_new(len + DLEXT_MAXLEN); buf = RSTRING_PTR(bufstr); MEMCPY(buf, feature, char, len); for (i = 0; (e = loadable_ext[i]) != 0; i++) { strlcpy(buf + len, e, DLEXT_MAXLEN + 1); if (st_get_key(loading_tbl, (st_data_t)buf, &data)) { rb_str_resize(bufstr, 0); if (fn) *fn = (const char*)data; return i ? 's' : 'r'; } } rb_str_resize(bufstr, 0); } } return 0; }