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(); 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_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; }
VALUE rb_str_format(int argc, const VALUE *argv, VALUE fmt) { enum {default_float_precision = 6}; rb_encoding *enc; const char *p, *end; char *buf; long blen, bsiz; VALUE result; long scanned = 0; int coderange = ENC_CODERANGE_7BIT; int width, prec, flags = FNONE; int nextarg = 1; int posarg = 0; int tainted = 0; VALUE nextvalue; VALUE tmp; VALUE str; volatile VALUE hash = Qundef; #define CHECK_FOR_WIDTH(f) \ if ((f) & FWIDTH) { \ rb_raise(rb_eArgError, "width given twice"); \ } \ if ((f) & FPREC0) { \ rb_raise(rb_eArgError, "width after precision"); \ } #define CHECK_FOR_FLAGS(f) \ if ((f) & FWIDTH) { \ rb_raise(rb_eArgError, "flag after width"); \ } \ if ((f) & FPREC0) { \ rb_raise(rb_eArgError, "flag after precision"); \ } ++argc; --argv; if (OBJ_TAINTED(fmt)) tainted = 1; StringValue(fmt); enc = rb_enc_get(fmt); fmt = rb_str_new4(fmt); p = RSTRING_PTR(fmt); end = p + RSTRING_LEN(fmt); blen = 0; bsiz = 120; result = rb_str_buf_new(bsiz); rb_enc_copy(result, fmt); buf = RSTRING_PTR(result); memset(buf, 0, bsiz); ENC_CODERANGE_SET(result, coderange); for (; p < end; p++) { const char *t; int n; VALUE sym = Qnil; for (t = p; t < end && *t != '%'; t++) ; PUSH(p, t - p); if (coderange != ENC_CODERANGE_BROKEN && scanned < blen) { scanned += rb_str_coderange_scan_restartable(buf+scanned, buf+blen, enc, &coderange); ENC_CODERANGE_SET(result, coderange); } if (t >= end) { /* end of fmt string */ goto sprint_exit; } p = t + 1; /* skip `%' */ width = prec = -1; nextvalue = Qundef; retry: switch (*p) { default: if (rb_enc_isprint(*p, enc)) rb_raise(rb_eArgError, "malformed format string - %%%c", *p); else rb_raise(rb_eArgError, "malformed format string"); break; case ' ': CHECK_FOR_FLAGS(flags); flags |= FSPACE; p++; goto retry; case '#': CHECK_FOR_FLAGS(flags); flags |= FSHARP; p++; goto retry; case '+': CHECK_FOR_FLAGS(flags); flags |= FPLUS; p++; goto retry; case '-': CHECK_FOR_FLAGS(flags); flags |= FMINUS; p++; goto retry; case '0': CHECK_FOR_FLAGS(flags); flags |= FZERO; p++; goto retry; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = 0; GETNUM(n, width); if (*p == '$') { if (nextvalue != Qundef) { rb_raise(rb_eArgError, "value given twice - %d$", n); } nextvalue = GETPOSARG(n); p++; goto retry; } CHECK_FOR_WIDTH(flags); width = n; flags |= FWIDTH; goto retry; case '<': case '{': { const char *start = p; char term = (*p == '<') ? '>' : '}'; int len; for (; p < end && *p != term; ) { p += rb_enc_mbclen(p, end, enc); } if (p >= end) { rb_raise(rb_eArgError, "malformed name - unmatched parenthesis"); } #if SIZEOF_INT < SIZEOF_SIZE_T if ((size_t)(p - start) >= INT_MAX) { const int message_limit = 20; len = (int)(rb_enc_right_char_head(start, start + message_limit, p, enc) - start); rb_enc_raise(enc, rb_eArgError, "too long name (%"PRIdSIZE" bytes) - %.*s...%c", (size_t)(p - start - 2), len, start, term); } #endif len = (int)(p - start + 1); /* including parenthesis */ if (sym != Qnil) { rb_enc_raise(enc, rb_eArgError, "named%.*s after <%"PRIsVALUE">", len, start, rb_sym2str(sym)); } CHECKNAMEARG(start, len, enc); get_hash(&hash, argc, argv); sym = rb_check_symbol_cstr(start + 1, len - 2 /* without parenthesis */, enc); if (!NIL_P(sym)) nextvalue = rb_hash_lookup2(hash, sym, Qundef); if (nextvalue == Qundef) { if (NIL_P(sym)) { sym = rb_sym_intern(start + 1, len - 2 /* without parenthesis */, enc); } nextvalue = rb_hash_default_value(hash, sym); if (NIL_P(nextvalue)) { rb_enc_raise(enc, rb_eKeyError, "key%.*s not found", len, start); } } if (term == '}') goto format_s; p++; goto retry; } case '*': CHECK_FOR_WIDTH(flags); flags |= FWIDTH; GETASTER(width); if (width < 0) { flags |= FMINUS; width = -width; } p++; goto retry; case '.': if (flags & FPREC0) { rb_raise(rb_eArgError, "precision given twice"); } flags |= FPREC|FPREC0; prec = 0; p++; if (*p == '*') { GETASTER(prec); if (prec < 0) { /* ignore negative precision */ flags &= ~FPREC; } p++; goto retry; } GETNUM(prec, precision); goto retry; case '\n': case '\0': p--; case '%': if (flags != FNONE) { rb_raise(rb_eArgError, "invalid format character - %%"); } PUSH("%", 1); break; case 'c': { VALUE val = GETARG(); VALUE tmp; unsigned int c; int n; tmp = rb_check_string_type(val); if (!NIL_P(tmp)) { if (rb_enc_strlen(RSTRING_PTR(tmp),RSTRING_END(tmp),enc) != 1) { rb_raise(rb_eArgError, "%%c requires a character"); } c = rb_enc_codepoint_len(RSTRING_PTR(tmp), RSTRING_END(tmp), &n, enc); RB_GC_GUARD(tmp); } else { c = NUM2INT(val); n = rb_enc_codelen(c, enc); } if (n <= 0) { rb_raise(rb_eArgError, "invalid character"); } if (!(flags & FWIDTH)) { CHECK(n); rb_enc_mbcput(c, &buf[blen], enc); blen += n; } else if ((flags & FMINUS)) { CHECK(n); rb_enc_mbcput(c, &buf[blen], enc); blen += n; if (width > 1) FILL(' ', width-1); } else { if (width > 1) FILL(' ', width-1); CHECK(n); rb_enc_mbcput(c, &buf[blen], enc); blen += n; } } break; case 's': case 'p': format_s: { VALUE arg = GETARG(); long len, slen; if (*p == 'p') { str = rb_inspect(arg); } else { str = rb_obj_as_string(arg); } if (OBJ_TAINTED(str)) tainted = 1; len = RSTRING_LEN(str); rb_str_set_len(result, blen); if (coderange != ENC_CODERANGE_BROKEN && scanned < blen) { int cr = coderange; scanned += rb_str_coderange_scan_restartable(buf+scanned, buf+blen, enc, &cr); ENC_CODERANGE_SET(result, (cr == ENC_CODERANGE_UNKNOWN ? ENC_CODERANGE_BROKEN : (coderange = cr))); } enc = rb_enc_check(result, str); if (flags&(FPREC|FWIDTH)) { slen = rb_enc_strlen(RSTRING_PTR(str),RSTRING_END(str),enc); if (slen < 0) { rb_raise(rb_eArgError, "invalid mbstring sequence"); } if ((flags&FPREC) && (prec < slen)) { char *p = rb_enc_nth(RSTRING_PTR(str), RSTRING_END(str), prec, enc); slen = prec; len = p - RSTRING_PTR(str); } /* need to adjust multi-byte string pos */ if ((flags&FWIDTH) && (width > slen)) { width -= (int)slen; if (!(flags&FMINUS)) { CHECK(width); while (width--) { buf[blen++] = ' '; } } CHECK(len); memcpy(&buf[blen], RSTRING_PTR(str), len); RB_GC_GUARD(str); blen += len; if (flags&FMINUS) { CHECK(width); while (width--) { buf[blen++] = ' '; } } rb_enc_associate(result, enc); break; } } PUSH(RSTRING_PTR(str), len); RB_GC_GUARD(str); rb_enc_associate(result, enc); } break; case 'd': case 'i': case 'o': case 'x': case 'X': case 'b': case 'B': case 'u': { volatile VALUE val = GETARG(); int valsign; char nbuf[64], *s; const char *prefix = 0; int sign = 0, dots = 0; char sc = 0; long v = 0; int base, bignum = 0; int len; switch (*p) { case 'd': case 'i': case 'u': sign = 1; break; case 'o': case 'x': case 'X': case 'b': case 'B': if (flags&(FPLUS|FSPACE)) sign = 1; break; } if (flags & FSHARP) { switch (*p) { case 'o': prefix = "0"; break; case 'x': prefix = "0x"; break; case 'X': prefix = "0X"; break; case 'b': prefix = "0b"; break; case 'B': prefix = "0B"; break; } } bin_retry: switch (TYPE(val)) { case T_FLOAT: if (FIXABLE(RFLOAT_VALUE(val))) { val = LONG2FIX((long)RFLOAT_VALUE(val)); goto bin_retry; } val = rb_dbl2big(RFLOAT_VALUE(val)); if (FIXNUM_P(val)) goto bin_retry; bignum = 1; break; case T_STRING: val = rb_str_to_inum(val, 0, TRUE); goto bin_retry; case T_BIGNUM: bignum = 1; break; case T_FIXNUM: v = FIX2LONG(val); break; default: val = rb_Integer(val); goto bin_retry; } switch (*p) { case 'o': base = 8; break; case 'x': case 'X': base = 16; break; case 'b': case 'B': base = 2; break; case 'u': case 'd': case 'i': default: base = 10; break; } if (base != 10) { int numbits = ffs(base)-1; size_t abs_nlz_bits; size_t numdigits = rb_absint_numwords(val, numbits, &abs_nlz_bits); long i; if (INT_MAX-1 < numdigits) /* INT_MAX is used because rb_long2int is used later. */ rb_raise(rb_eArgError, "size too big"); if (sign) { if (numdigits == 0) numdigits = 1; tmp = rb_str_new(NULL, numdigits); valsign = rb_integer_pack(val, RSTRING_PTR(tmp), RSTRING_LEN(tmp), 1, CHAR_BIT-numbits, INTEGER_PACK_BIG_ENDIAN); for (i = 0; i < RSTRING_LEN(tmp); i++) RSTRING_PTR(tmp)[i] = ruby_digitmap[((unsigned char *)RSTRING_PTR(tmp))[i]]; s = RSTRING_PTR(tmp); if (valsign < 0) { sc = '-'; width--; } else if (flags & FPLUS) { sc = '+'; width--; } else if (flags & FSPACE) { sc = ' '; width--; } } else { /* Following conditional "numdigits++" guarantees the * most significant digit as * - '1'(bin), '7'(oct) or 'f'(hex) for negative numbers * - '0' for zero * - not '0' for positive numbers. * * It also guarantees the most significant two * digits will not be '11'(bin), '77'(oct), 'ff'(hex) * or '00'. */ if (numdigits == 0 || ((abs_nlz_bits != (size_t)(numbits-1) || !rb_absint_singlebit_p(val)) && (!bignum ? v < 0 : BIGNUM_NEGATIVE_P(val)))) numdigits++; tmp = rb_str_new(NULL, numdigits); valsign = rb_integer_pack(val, RSTRING_PTR(tmp), RSTRING_LEN(tmp), 1, CHAR_BIT-numbits, INTEGER_PACK_2COMP | INTEGER_PACK_BIG_ENDIAN); for (i = 0; i < RSTRING_LEN(tmp); i++) RSTRING_PTR(tmp)[i] = ruby_digitmap[((unsigned char *)RSTRING_PTR(tmp))[i]]; s = RSTRING_PTR(tmp); dots = valsign < 0; } len = rb_long2int(RSTRING_END(tmp) - s); } else if (!bignum) { valsign = 1; if (v < 0) { v = -v; sc = '-'; width--; valsign = -1; } else if (flags & FPLUS) { sc = '+'; width--; } else if (flags & FSPACE) { sc = ' '; width--; } snprintf(nbuf, sizeof(nbuf), "%ld", v); s = nbuf; len = (int)strlen(s); } else { tmp = rb_big2str(val, 10); s = RSTRING_PTR(tmp); valsign = 1; if (s[0] == '-') { s++; sc = '-'; width--; valsign = -1; } else if (flags & FPLUS) { sc = '+'; width--; } else if (flags & FSPACE) { sc = ' '; width--; } len = rb_long2int(RSTRING_END(tmp) - s); } if (dots) { prec -= 2; width -= 2; } if (*p == 'X') { char *pp = s; int c; while ((c = (int)(unsigned char)*pp) != 0) { *pp = rb_enc_toupper(c, enc); pp++; } } if (prefix && !prefix[1]) { /* octal */ if (dots) { prefix = 0; } else if (len == 1 && *s == '0') { len = 0; if (flags & FPREC) prec--; } else if ((flags & FPREC) && (prec > len)) { prefix = 0; } } else if (len == 1 && *s == '0') { prefix = 0; } if (prefix) { width -= (int)strlen(prefix); } if ((flags & (FZERO|FMINUS|FPREC)) == FZERO) { prec = width; width = 0; } else { if (prec < len) { if (!prefix && prec == 0 && len == 1 && *s == '0') len = 0; prec = len; } width -= prec; } if (!(flags&FMINUS)) { CHECK(width); while (width-- > 0) { buf[blen++] = ' '; } } if (sc) PUSH(&sc, 1); if (prefix) { int plen = (int)strlen(prefix); PUSH(prefix, plen); } CHECK(prec - len); if (dots) PUSH("..", 2); if (!sign && valsign < 0) { char c = sign_bits(base, p); while (len < prec--) { buf[blen++] = c; } } else if ((flags & (FMINUS|FPREC)) != FMINUS) { while (len < prec--) { buf[blen++] = '0'; } } PUSH(s, len); RB_GC_GUARD(tmp); CHECK(width); while (width-- > 0) { buf[blen++] = ' '; } } break; case 'f': { VALUE val = GETARG(), num, den; int sign = (flags&FPLUS) ? 1 : 0, zero = 0; long len, done = 0; int prefix = 0; if (FIXNUM_P(val) || RB_TYPE_P(val, T_BIGNUM)) { den = INT2FIX(1); num = val; } else if (RB_TYPE_P(val, T_RATIONAL)) { den = rb_rational_den(val); num = rb_rational_num(val); } else { nextvalue = val; goto float_value; } if (!(flags&FPREC)) prec = default_float_precision; if (FIXNUM_P(num)) { if ((SIGNED_VALUE)num < 0) { long n = -FIX2LONG(num); num = LONG2FIX(n); sign = -1; } } else if (rb_num_negative_p(num)) { sign = -1; num = rb_funcallv(num, idUMinus, 0, 0); } if (den != INT2FIX(1) || prec > 1) { const ID idDiv = rb_intern("div"); VALUE p10 = rb_int_positive_pow(10, prec); VALUE den_2 = rb_funcall(den, idDiv, 1, INT2FIX(2)); num = rb_funcallv(num, '*', 1, &p10); num = rb_funcallv(num, '+', 1, &den_2); num = rb_funcallv(num, idDiv, 1, &den); } else if (prec >= 0) { zero = prec; } val = rb_obj_as_string(num); len = RSTRING_LEN(val) + zero; if (prec >= len) len = prec + 1; /* integer part 0 */ if (sign || (flags&FSPACE)) ++len; if (prec > 0) ++len; /* period */ CHECK(len > width ? len : width); if (sign || (flags&FSPACE)) { buf[blen++] = sign > 0 ? '+' : sign < 0 ? '-' : ' '; prefix++; done++; } len = RSTRING_LEN(val) + zero; t = RSTRING_PTR(val); if (len > prec) { memcpy(&buf[blen], t, len - prec); blen += len - prec; done += len - prec; } else { buf[blen++] = '0'; done++; } if (prec > 0) { buf[blen++] = '.'; done++; } if (zero) { FILL('0', zero); done += zero; } else if (prec > len) { FILL('0', prec - len); memcpy(&buf[blen], t, len); blen += len; done += prec; } else if (prec > 0) { memcpy(&buf[blen], t + len - prec, prec); blen += prec; done += prec; } if ((flags & FWIDTH) && width > done) { int fill = ' '; long shifting = 0; if (!(flags&FMINUS)) { shifting = done; if (flags&FZERO) { shifting -= prefix; fill = '0'; } blen -= shifting; memmove(&buf[blen + width - done], &buf[blen], shifting); } FILL(fill, width - done); blen += shifting; } RB_GC_GUARD(val); break; } case 'g': case 'G': case 'e': case 'E': /* TODO: rational support */ case 'a': case 'A': float_value: { VALUE val = GETARG(); double fval; int i, need; char fbuf[32]; fval = RFLOAT_VALUE(rb_Float(val)); if (isnan(fval) || isinf(fval)) { const char *expr; if (isnan(fval)) { expr = "NaN"; } else { expr = "Inf"; } need = (int)strlen(expr); if ((!isnan(fval) && fval < 0.0) || (flags & FPLUS)) need++; if ((flags & FWIDTH) && need < width) need = width; CHECK(need + 1); snprintf(&buf[blen], need + 1, "%*s", need, ""); if (flags & FMINUS) { if (!isnan(fval) && fval < 0.0) buf[blen++] = '-'; else if (flags & FPLUS) buf[blen++] = '+'; else if (flags & FSPACE) blen++; memcpy(&buf[blen], expr, strlen(expr)); } else { if (!isnan(fval) && fval < 0.0) buf[blen + need - strlen(expr) - 1] = '-'; else if (flags & FPLUS) buf[blen + need - strlen(expr) - 1] = '+'; else if ((flags & FSPACE) && need > width) blen++; memcpy(&buf[blen + need - strlen(expr)], expr, strlen(expr)); } blen += strlen(&buf[blen]); break; } fmt_setup(fbuf, sizeof(fbuf), *p, flags, width, prec); need = 0; if (*p != 'e' && *p != 'E') { i = INT_MIN; frexp(fval, &i); if (i > 0) need = BIT_DIGITS(i); } need += (flags&FPREC) ? prec : default_float_precision; if ((flags&FWIDTH) && need < width) need = width; need += 20; CHECK(need); snprintf(&buf[blen], need, fbuf, fval); blen += strlen(&buf[blen]); } break; } flags = FNONE; } sprint_exit: RB_GC_GUARD(fmt); /* XXX - We cannot validate the number of arguments if (digit)$ style used. */ if (posarg >= 0 && nextarg < argc) { const char *mesg = "too many arguments for format string"; if (RTEST(ruby_debug)) rb_raise(rb_eArgError, "%s", mesg); if (RTEST(ruby_verbose)) rb_warn("%s", mesg); } rb_str_resize(result, blen); if (tainted) OBJ_TAINT(result); return result; }
VALUE rb_str_format(int argc, const VALUE *argv, VALUE fmt) { rb_encoding *enc; const char *p, *end; char *buf; long blen, bsiz; VALUE result; long scanned = 0; int coderange = ENC_CODERANGE_7BIT; int width, prec, flags = FNONE; int nextarg = 1; int posarg = 0; int tainted = 0; VALUE nextvalue; VALUE tmp; VALUE str; volatile VALUE hash = Qundef; #define CHECK_FOR_WIDTH(f) \ if ((f) & FWIDTH) { \ rb_raise(rb_eArgError, "width given twice"); \ } \ if ((f) & FPREC0) { \ rb_raise(rb_eArgError, "width after precision"); \ } #define CHECK_FOR_FLAGS(f) \ if ((f) & FWIDTH) { \ rb_raise(rb_eArgError, "flag after width"); \ } \ if ((f) & FPREC0) { \ rb_raise(rb_eArgError, "flag after precision"); \ } ++argc; --argv; if (OBJ_TAINTED(fmt)) tainted = 1; StringValue(fmt); enc = rb_enc_get(fmt); fmt = rb_str_new4(fmt); p = RSTRING_PTR(fmt); end = p + RSTRING_LEN(fmt); blen = 0; bsiz = 120; result = rb_str_buf_new(bsiz); rb_enc_copy(result, fmt); buf = RSTRING_PTR(result); memset(buf, 0, bsiz); ENC_CODERANGE_SET(result, coderange); for (; p < end; p++) { const char *t; int n; ID id = 0; for (t = p; t < end && *t != '%'; t++) ; PUSH(p, t - p); if (coderange != ENC_CODERANGE_BROKEN && scanned < blen) { scanned += rb_str_coderange_scan_restartable(buf+scanned, buf+blen, enc, &coderange); ENC_CODERANGE_SET(result, coderange); } if (t >= end) { /* end of fmt string */ goto sprint_exit; } p = t + 1; /* skip `%' */ width = prec = -1; nextvalue = Qundef; retry: switch (*p) { default: if (rb_enc_isprint(*p, enc)) rb_raise(rb_eArgError, "malformed format string - %%%c", *p); else rb_raise(rb_eArgError, "malformed format string"); break; case ' ': CHECK_FOR_FLAGS(flags); flags |= FSPACE; p++; goto retry; case '#': CHECK_FOR_FLAGS(flags); flags |= FSHARP; p++; goto retry; case '+': CHECK_FOR_FLAGS(flags); flags |= FPLUS; p++; goto retry; case '-': CHECK_FOR_FLAGS(flags); flags |= FMINUS; p++; goto retry; case '0': CHECK_FOR_FLAGS(flags); flags |= FZERO; p++; goto retry; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = 0; GETNUM(n, width); if (*p == '$') { if (nextvalue != Qundef) { rb_raise(rb_eArgError, "value given twice - %d$", n); } nextvalue = GETPOSARG(n); p++; goto retry; } CHECK_FOR_WIDTH(flags); width = n; flags |= FWIDTH; goto retry; case '<': case '{': { const char *start = p; char term = (*p == '<') ? '>' : '}'; int len; for (; p < end && *p != term; ) { p += rb_enc_mbclen(p, end, enc); } if (p >= end) { rb_raise(rb_eArgError, "malformed name - unmatched parenthesis"); } #if SIZEOF_INT < SIZEOF_SIZE_T if ((size_t)(p - start) >= INT_MAX) { const int message_limit = 20; len = (int)(rb_enc_right_char_head(start, start + message_limit, p, enc) - start); rb_enc_raise(enc, rb_eArgError, "too long name (%"PRIdSIZE" bytes) - %.*s...%c", (size_t)(p - start - 2), len, start, term); } #endif len = (int)(p - start + 1); /* including parenthesis */ if (id) { rb_enc_raise(enc, rb_eArgError, "named%.*s after <%s>", len, start, rb_id2name(id)); } nextvalue = GETNAMEARG((id = rb_check_id_cstr(start + 1, len - 2 /* without parenthesis */, enc), ID2SYM(id)), start, len, enc); if (nextvalue == Qundef) { rb_enc_raise(enc, rb_eKeyError, "key%.*s not found", len, start); } if (term == '}') goto format_s; p++; goto retry; } case '*': CHECK_FOR_WIDTH(flags); flags |= FWIDTH; GETASTER(width); if (width < 0) { flags |= FMINUS; width = -width; } p++; goto retry; case '.': if (flags & FPREC0) { rb_raise(rb_eArgError, "precision given twice"); } flags |= FPREC|FPREC0; prec = 0; p++; if (*p == '*') { GETASTER(prec); if (prec < 0) { /* ignore negative precision */ flags &= ~FPREC; } p++; goto retry; } GETNUM(prec, precision); goto retry; case '\n': case '\0': p--; case '%': if (flags != FNONE) { rb_raise(rb_eArgError, "invalid format character - %%"); } PUSH("%", 1); break; case 'c': { VALUE val = GETARG(); VALUE tmp; unsigned int c; int n; tmp = rb_check_string_type(val); if (!NIL_P(tmp)) { if (rb_enc_strlen(RSTRING_PTR(tmp),RSTRING_END(tmp),enc) != 1) { rb_raise(rb_eArgError, "%%c requires a character"); } c = rb_enc_codepoint_len(RSTRING_PTR(tmp), RSTRING_END(tmp), &n, enc); RB_GC_GUARD(tmp); } else { c = NUM2INT(val); n = rb_enc_codelen(c, enc); } if (n <= 0) { rb_raise(rb_eArgError, "invalid character"); } if (!(flags & FWIDTH)) { CHECK(n); rb_enc_mbcput(c, &buf[blen], enc); blen += n; } else if ((flags & FMINUS)) { CHECK(n); rb_enc_mbcput(c, &buf[blen], enc); blen += n; FILL(' ', width-1); } else { FILL(' ', width-1); CHECK(n); rb_enc_mbcput(c, &buf[blen], enc); blen += n; } } break; case 's': case 'p': format_s: { VALUE arg = GETARG(); long len, slen; if (*p == 'p') arg = rb_inspect(arg); str = rb_obj_as_string(arg); if (OBJ_TAINTED(str)) tainted = 1; len = RSTRING_LEN(str); rb_str_set_len(result, blen); if (coderange != ENC_CODERANGE_BROKEN && scanned < blen) { int cr = coderange; scanned += rb_str_coderange_scan_restartable(buf+scanned, buf+blen, enc, &cr); ENC_CODERANGE_SET(result, (cr == ENC_CODERANGE_UNKNOWN ? ENC_CODERANGE_BROKEN : (coderange = cr))); } enc = rb_enc_check(result, str); if (flags&(FPREC|FWIDTH)) { slen = rb_enc_strlen(RSTRING_PTR(str),RSTRING_END(str),enc); if (slen < 0) { rb_raise(rb_eArgError, "invalid mbstring sequence"); } if ((flags&FPREC) && (prec < slen)) { char *p = rb_enc_nth(RSTRING_PTR(str), RSTRING_END(str), prec, enc); slen = prec; len = p - RSTRING_PTR(str); } /* need to adjust multi-byte string pos */ if ((flags&FWIDTH) && (width > slen)) { width -= (int)slen; if (!(flags&FMINUS)) { CHECK(width); while (width--) { buf[blen++] = ' '; } } CHECK(len); memcpy(&buf[blen], RSTRING_PTR(str), len); RB_GC_GUARD(str); blen += len; if (flags&FMINUS) { CHECK(width); while (width--) { buf[blen++] = ' '; } } rb_enc_associate(result, enc); break; } } PUSH(RSTRING_PTR(str), len); RB_GC_GUARD(str); rb_enc_associate(result, enc); } break; case 'd': case 'i': case 'o': case 'x': case 'X': case 'b': case 'B': case 'u': { volatile VALUE val = GETARG(); char fbuf[32], nbuf[64], *s; const char *prefix = 0; int sign = 0, dots = 0; char sc = 0; long v = 0; int base, bignum = 0; int len; switch (*p) { case 'd': case 'i': case 'u': sign = 1; break; case 'o': case 'x': case 'X': case 'b': case 'B': if (flags&(FPLUS|FSPACE)) sign = 1; break; } if (flags & FSHARP) { switch (*p) { case 'o': prefix = "0"; break; case 'x': prefix = "0x"; break; case 'X': prefix = "0X"; break; case 'b': prefix = "0b"; break; case 'B': prefix = "0B"; break; } } bin_retry: switch (TYPE(val)) { case T_FLOAT: if (FIXABLE(RFLOAT_VALUE(val))) { val = LONG2FIX((long)RFLOAT_VALUE(val)); goto bin_retry; } val = rb_dbl2big(RFLOAT_VALUE(val)); if (FIXNUM_P(val)) goto bin_retry; bignum = 1; break; case T_STRING: val = rb_str_to_inum(val, 0, TRUE); goto bin_retry; case T_BIGNUM: bignum = 1; break; case T_FIXNUM: v = FIX2LONG(val); break; default: val = rb_Integer(val); goto bin_retry; } switch (*p) { case 'o': base = 8; break; case 'x': case 'X': base = 16; break; case 'b': case 'B': base = 2; break; case 'u': case 'd': case 'i': default: base = 10; break; } if (!bignum) { if (base == 2) { val = rb_int2big(v); goto bin_retry; } if (sign) { char c = *p; if (c == 'i') c = 'd'; /* %d and %i are identical */ if (v < 0) { v = -v; sc = '-'; width--; } else if (flags & FPLUS) { sc = '+'; width--; } else if (flags & FSPACE) { sc = ' '; width--; } snprintf(fbuf, sizeof(fbuf), "%%l%c", c); snprintf(nbuf, sizeof(nbuf), fbuf, v); s = nbuf; } else { s = nbuf; if (v < 0) { dots = 1; } snprintf(fbuf, sizeof(fbuf), "%%l%c", *p == 'X' ? 'x' : *p); snprintf(++s, sizeof(nbuf) - 1, fbuf, v); if (v < 0) { char d = 0; s = remove_sign_bits(s, base); switch (base) { case 16: d = 'f'; break; case 8: d = '7'; break; } if (d && *s != d) { *--s = d; } } } len = (int)strlen(s); } else { if (sign) { tmp = rb_big2str(val, base); s = RSTRING_PTR(tmp); if (s[0] == '-') { s++; sc = '-'; width--; } else if (flags & FPLUS) { sc = '+'; width--; } else if (flags & FSPACE) { sc = ' '; width--; } } else { if (!RBIGNUM_SIGN(val)) { val = rb_big_clone(val); rb_big_2comp(val); } tmp = rb_big2str0(val, base, RBIGNUM_SIGN(val)); s = RSTRING_PTR(tmp); if (*s == '-') { dots = 1; if (base == 10) { rb_warning("negative number for %%u specifier"); } s = remove_sign_bits(++s, base); switch (base) { case 16: if (s[0] != 'f') *--s = 'f'; break; case 8: if (s[0] != '7') *--s = '7'; break; case 2: if (s[0] != '1') *--s = '1'; break; } } } len = rb_long2int(RSTRING_END(tmp) - s); } if (dots) { prec -= 2; width -= 2; } if (*p == 'X') { char *pp = s; int c; while ((c = (int)(unsigned char)*pp) != 0) { *pp = rb_enc_toupper(c, enc); pp++; } } if (prefix && !prefix[1]) { /* octal */ if (dots) { prefix = 0; } else if (len == 1 && *s == '0') { len = 0; if (flags & FPREC) prec--; } else if ((flags & FPREC) && (prec > len)) { prefix = 0; } } else if (len == 1 && *s == '0') { prefix = 0; } if (prefix) { width -= (int)strlen(prefix); } if ((flags & (FZERO|FMINUS|FPREC)) == FZERO) { prec = width; width = 0; } else { if (prec < len) { if (!prefix && prec == 0 && len == 1 && *s == '0') len = 0; prec = len; } width -= prec; } if (!(flags&FMINUS)) { CHECK(width); while (width-- > 0) { buf[blen++] = ' '; } } if (sc) PUSH(&sc, 1); if (prefix) { int plen = (int)strlen(prefix); PUSH(prefix, plen); } CHECK(prec - len); if (dots) PUSH("..", 2); if (!bignum && v < 0) { char c = sign_bits(base, p); while (len < prec--) { buf[blen++] = c; } } else if ((flags & (FMINUS|FPREC)) != FMINUS) { char c; if (!sign && bignum && !RBIGNUM_SIGN(val)) c = sign_bits(base, p); else c = '0'; while (len < prec--) { buf[blen++] = c; } } PUSH(s, len); RB_GC_GUARD(tmp); CHECK(width); while (width-- > 0) { buf[blen++] = ' '; } } break; case 'f': case 'g': case 'G': case 'e': case 'E': case 'a': case 'A': { VALUE val = GETARG(); double fval; int i, need = 6; char fbuf[32]; fval = RFLOAT_VALUE(rb_Float(val)); if (isnan(fval) || isinf(fval)) { const char *expr; if (isnan(fval)) { expr = "NaN"; } else { expr = "Inf"; } need = (int)strlen(expr); if ((!isnan(fval) && fval < 0.0) || (flags & FPLUS)) need++; if ((flags & FWIDTH) && need < width) need = width; CHECK(need + 1); snprintf(&buf[blen], need + 1, "%*s", need, ""); if (flags & FMINUS) { if (!isnan(fval) && fval < 0.0) buf[blen++] = '-'; else if (flags & FPLUS) buf[blen++] = '+'; else if (flags & FSPACE) blen++; memcpy(&buf[blen], expr, strlen(expr)); } else { if (!isnan(fval) && fval < 0.0) buf[blen + need - strlen(expr) - 1] = '-'; else if (flags & FPLUS) buf[blen + need - strlen(expr) - 1] = '+'; else if ((flags & FSPACE) && need > width) blen++; memcpy(&buf[blen + need - strlen(expr)], expr, strlen(expr)); } blen += strlen(&buf[blen]); break; } fmt_setup(fbuf, sizeof(fbuf), *p, flags, width, prec); need = 0; if (*p != 'e' && *p != 'E') { i = INT_MIN; frexp(fval, &i); if (i > 0) need = BIT_DIGITS(i); } need += (flags&FPREC) ? prec : 6; if ((flags&FWIDTH) && need < width) need = width; need += 20; CHECK(need); snprintf(&buf[blen], need, fbuf, fval); blen += strlen(&buf[blen]); } break; } flags = FNONE; } sprint_exit: RB_GC_GUARD(fmt); /* XXX - We cannot validate the number of arguments if (digit)$ style used. */ if (posarg >= 0 && nextarg < argc) { const char *mesg = "too many arguments for format string"; if (RTEST(ruby_debug)) rb_raise(rb_eArgError, "%s", mesg); if (RTEST(ruby_verbose)) rb_warn("%s", mesg); } rb_str_resize(result, blen); if (tainted) OBJ_TAINT(result); return result; }
void ruby_init_loadpath_safe(int safe_level) { VALUE load_path; extern const char ruby_initial_load_paths[]; const char *paths = ruby_initial_load_paths; #if defined LOAD_RELATIVE # if defined HAVE_DLADDR || (defined __CYGWIN__ && defined CCP_WIN_A_TO_POSIX) # define VARIABLE_LIBPATH 1 # else # define VARIABLE_LIBPATH 0 # endif # if VARIABLE_LIBPATH char *libpath; VALUE sopath; # else char libpath[MAXPATHLEN + 1]; size_t baselen; # endif char *p; #if defined _WIN32 || defined __CYGWIN__ # if VARIABLE_LIBPATH sopath = rb_str_tmp_new(MAXPATHLEN); libpath = RSTRING_PTR(sopath); GetModuleFileName(libruby, libpath, MAXPATHLEN); # else GetModuleFileName(libruby, libpath, sizeof libpath); # endif #elif defined(__EMX__) _execname(libpath, sizeof(libpath) - 1); #elif defined(HAVE_DLADDR) Dl_info dli; if (dladdr(expand_include_path, &dli)) { VALUE fname = rb_str_new_cstr(dli.dli_fname); sopath = rb_file_absolute_path(fname, Qnil); rb_str_resize(fname, 0); } else { sopath = rb_str_new(0, 0); } libpath = RSTRING_PTR(sopath); #endif #if !VARIABLE_LIBPATH libpath[sizeof(libpath) - 1] = '\0'; #endif #if defined DOSISH translit_char(libpath, '\\', '/'); #elif defined __CYGWIN__ { # if VARIABLE_LIBPATH const int win_to_posix = CCP_WIN_A_TO_POSIX | CCP_RELATIVE; size_t newsize = cygwin_conv_path(win_to_posix, libpath, 0, 0); if (newsize > 0) { VALUE rubylib = rb_str_tmp_new(newsize); p = RSTRING_PTR(rubylib); if (cygwin_conv_path(win_to_posix, libpath, p, newsize) == 0) { rb_str_resize(sopath, 0); sopath = rubylib; libpath = p; } } # else char rubylib[FILENAME_MAX]; cygwin_conv_to_posix_path(libpath, rubylib); strncpy(libpath, rubylib, sizeof(libpath)); # endif } #endif p = strrchr(libpath, '/'); if (p) { *p = 0; if (p - libpath > 3 && !(STRCASECMP(p - 4, "/bin") && strcmp(p - 4, "/lib"))) { p -= 4; *p = 0; } } #if !VARIABLE_LIBPATH else { strlcpy(libpath, ".", sizeof(libpath)); p = libpath + 1; } baselen = p - libpath; #define BASEPATH() rb_str_buf_cat(rb_str_buf_new(baselen+len), libpath, baselen) #else rb_str_set_len(sopath, p - libpath); #define BASEPATH() rb_str_dup(sopath) #endif #define RUBY_RELATIVE(path, len) rb_str_buf_cat(BASEPATH(), path, len) #else #define RUBY_RELATIVE(path, len) rubylib_mangled_path(path, len) #endif #define incpush(path) rb_ary_push(load_path, (path)) load_path = GET_VM()->load_path; if (safe_level == 0) { ruby_push_include(getenv("RUBYLIB"), identical_path); } while (*paths) { size_t len = strlen(paths); incpush(RUBY_RELATIVE(paths, len)); paths += len + 1; } }
VALUE string_spec_rb_str_resize_RSTRING_LEN(VALUE self, VALUE str, VALUE size) { VALUE modified = rb_str_resize(str, FIX2INT(size)); return INT2FIX(RSTRING_LEN(modified)); }
VALUE string_spec_rb_str_resize(VALUE self, VALUE str, VALUE size) { return rb_str_resize(str, FIX2INT(size)); }
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; }
void ruby_init_loadpath_safe(int safe_level) { VALUE load_path; ID id_initial_load_path_mark; extern const char ruby_initial_load_paths[]; const char *paths = ruby_initial_load_paths; #if defined LOAD_RELATIVE # if defined HAVE_DLADDR || (defined __CYGWIN__ && defined CCP_WIN_A_TO_POSIX) # define VARIABLE_LIBPATH 1 # else # define VARIABLE_LIBPATH 0 # endif # if VARIABLE_LIBPATH char *libpath; VALUE sopath; # else char libpath[MAXPATHLEN + 1]; # endif size_t baselen; char *p; #if defined _WIN32 || defined __CYGWIN__ # if VARIABLE_LIBPATH sopath = rb_str_new(0, MAXPATHLEN); libpath = RSTRING_PTR(sopath); GetModuleFileName(libruby, libpath, MAXPATHLEN); # else GetModuleFileName(libruby, libpath, sizeof libpath); # endif #elif defined(__EMX__) _execname(libpath, sizeof(libpath) - 1); #elif defined(HAVE_DLADDR) Dl_info dli; if (dladdr((void *)(VALUE)expand_include_path, &dli)) { VALUE rb_realpath_internal(VALUE basedir, VALUE path, int strict); char fbuf[MAXPATHLEN]; char *f = dln_find_file_r(dli.dli_fname, getenv(PATH_ENV), fbuf, sizeof(fbuf)); VALUE fname = rb_str_new_cstr(f); rb_str_freeze(fname); sopath = rb_realpath_internal(Qnil, fname, 1); } else { sopath = rb_str_new(0, 0); } libpath = RSTRING_PTR(sopath); #endif #if !VARIABLE_LIBPATH libpath[sizeof(libpath) - 1] = '\0'; #endif #if defined DOSISH translit_char(libpath, '\\', '/'); #elif defined __CYGWIN__ { # if VARIABLE_LIBPATH const int win_to_posix = CCP_WIN_A_TO_POSIX | CCP_RELATIVE; size_t newsize = cygwin_conv_path(win_to_posix, libpath, 0, 0); if (newsize > 0) { VALUE rubylib = rb_str_new(0, newsize); p = RSTRING_PTR(rubylib); if (cygwin_conv_path(win_to_posix, libpath, p, newsize) == 0) { rb_str_resize(sopath, 0); sopath = rubylib; libpath = p; } } # else char rubylib[FILENAME_MAX]; cygwin_conv_to_posix_path(libpath, rubylib); strncpy(libpath, rubylib, sizeof(libpath)); # endif } #endif p = strrchr(libpath, '/'); if (p) { *p = 0; if (p - libpath > 3 && !(STRCASECMP(p - 4, "/bin") && strcmp(p - 4, "/lib"))) { p -= 4; *p = 0; } } #if !VARIABLE_LIBPATH else { strlcpy(libpath, ".", sizeof(libpath)); p = libpath + 1; } baselen = p - libpath; #define PREFIX_PATH() rb_str_new(libpath, baselen) #else baselen = p - libpath; rb_str_resize(sopath, baselen); libpath = RSTRING_PTR(sopath); #define PREFIX_PATH() sopath #endif #define BASEPATH() rb_str_buf_cat(rb_str_buf_new(baselen+len), libpath, baselen) #define RUBY_RELATIVE(path, len) rb_str_buf_cat(BASEPATH(), path, len) #else static const char exec_prefix[] = RUBY_EXEC_PREFIX; #define RUBY_RELATIVE(path, len) rubylib_mangled_path(path, len) #define PREFIX_PATH() RUBY_RELATIVE(exec_prefix, sizeof(exec_prefix)-1) #endif load_path = GET_VM()->load_path; if (safe_level == 0) { ruby_push_include(getenv("RUBYLIB"), identical_path); } id_initial_load_path_mark = rb_intern_const("@gem_prelude_index"); while (*paths) { size_t len = strlen(paths); VALUE path = RUBY_RELATIVE(paths, len); rb_ivar_set(path, id_initial_load_path_mark, path); rb_ary_push(load_path, path); paths += len + 1; } rb_const_set(rb_cObject, rb_intern_const("TMP_RUBY_PREFIX"), rb_obj_freeze(PREFIX_PATH())); }