static mrb_value mrb_addrinfo_getnameinfo(mrb_state *mrb, mrb_value self) { mrb_int flags; mrb_value ary, host, sastr, serv; int error; flags = 0; mrb_get_args(mrb, "|i", &flags); host = mrb_str_buf_new(mrb, NI_MAXHOST); serv = mrb_str_buf_new(mrb, NI_MAXSERV); sastr = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@sockaddr")); if (!mrb_string_p(sastr)) { mrb_raise(mrb, E_SOCKET_ERROR, "invalid sockaddr"); } error = getnameinfo((struct sockaddr *)RSTRING_PTR(sastr), (socklen_t)RSTRING_LEN(sastr), RSTRING_PTR(host), NI_MAXHOST, RSTRING_PTR(serv), NI_MAXSERV, flags); if (error != 0) { mrb_raisef(mrb, E_SOCKET_ERROR, "getnameinfo: %s", gai_strerror(error)); } ary = mrb_ary_new_capa(mrb, 2); mrb_str_resize(mrb, host, strlen(RSTRING_PTR(host))); mrb_ary_push(mrb, ary, host); mrb_str_resize(mrb, serv, strlen(RSTRING_PTR(serv))); mrb_ary_push(mrb, ary, serv); return ary; }
mrb_value mrb_io_sysread(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr; mrb_value buf = mrb_nil_value(); mrb_int maxlen; int ret; mrb_get_args(mrb, "i|S", &maxlen, &buf); if (maxlen < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative expanding string size"); } else if (maxlen == 0) { return mrb_str_new(mrb, NULL, maxlen); } if (mrb_nil_p(buf)) { buf = mrb_str_new(mrb, NULL, maxlen); } if (RSTRING_LEN(buf) != maxlen) { buf = mrb_str_resize(mrb, buf, maxlen); } else { mrb_str_modify(mrb, RSTRING(buf)); } fptr = (struct mrb_io *)io_get_open_fptr(mrb, io); if (!fptr->readable) { mrb_raise(mrb, E_IO_ERROR, "not opened for reading"); } ret = read(fptr->fd, RSTRING_PTR(buf), (fsize_t)maxlen); switch (ret) { case 0: /* EOF */ if (maxlen == 0) { buf = mrb_str_new_cstr(mrb, ""); } else { mrb_raise(mrb, E_EOF_ERROR, "sysread failed: End of File"); } break; case -1: /* Error */ mrb_sys_fail(mrb, "sysread failed"); break; default: if (RSTRING_LEN(buf) != ret) { buf = mrb_str_resize(mrb, buf, ret); } break; } return buf; }
static void get_backtrace_i(mrb_state *mrb, void *stream, int level, const char *format, ...) { va_list ap; mrb_value ary, str; int ai; if (level > 0) { return; } ai = mrb_gc_arena_save(mrb); ary = mrb_obj_value((struct RArray*)stream); va_start(ap, format); str = mrb_str_new(mrb, 0, vsnprintf(NULL, 0, format, ap) + 1); va_end(ap); va_start(ap, format); vsnprintf(RSTRING_PTR(str), RSTRING_LEN(str), format, ap); va_end(ap); mrb_str_resize(mrb, str, RSTRING_LEN(str) - 1); mrb_ary_push(mrb, ary, str); mrb_gc_arena_restore(mrb, ai); }
/* * call-seq: * str.prepend(other_str) -> str * * Prepend---Prepend the given string to <i>str</i>. * * a = "world" * a.prepend("hello ") #=> "hello world" * a #=> "hello world" */ static mrb_value mrb_str_prepend(mrb_state *mrb, mrb_value self) { struct RString *s1 = mrb_str_ptr(self), *s2, *temp_s; mrb_int len; mrb_value other, temp_str; mrb_get_args(mrb, "S", &other); mrb_str_modify(mrb, s1); if (!mrb_string_p(other)) { other = mrb_str_to_str(mrb, other); } s2 = mrb_str_ptr(other); len = RSTR_LEN(s1) + RSTR_LEN(s2); temp_str = mrb_str_new(mrb, NULL, RSTR_LEN(s1)); temp_s = mrb_str_ptr(temp_str); memcpy(RSTR_PTR(temp_s), RSTR_PTR(s1), RSTR_LEN(s1)); if (RSTRING_CAPA(self) < len) { mrb_str_resize(mrb, self, len); } memcpy(RSTR_PTR(s1), RSTR_PTR(s2), RSTR_LEN(s2)); memcpy(RSTR_PTR(s1) + RSTR_LEN(s2), RSTR_PTR(temp_s), RSTR_LEN(temp_s)); RSTR_SET_LEN(s1, len); RSTR_PTR(s1)[len] = '\0'; return self; }
static mrb_value sa2addrlist(mrb_state *mrb, const struct sockaddr *sa, socklen_t salen) { mrb_value ary, host; unsigned short port; const char *afstr; switch (sa->sa_family) { case AF_INET: afstr = "AF_INET"; port = ((struct sockaddr_in *)sa)->sin_port; break; case AF_INET6: afstr = "AF_INET6"; port = ((struct sockaddr_in6 *)sa)->sin6_port; break; default: mrb_raise(mrb, E_ARGUMENT_ERROR, "bad af"); return mrb_nil_value(); } port = ntohs(port); host = mrb_str_buf_new(mrb, NI_MAXHOST); if (getnameinfo(sa, salen, RSTRING_PTR(host), NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == -1) mrb_sys_fail(mrb, "getnameinfo"); mrb_str_resize(mrb, host, strlen(RSTRING_PTR(host))); ary = mrb_ary_new_capa(mrb, 4); mrb_ary_push(mrb, ary, mrb_str_new_cstr(mrb, afstr)); mrb_ary_push(mrb, ary, mrb_fixnum_value(port)); mrb_ary_push(mrb, ary, host); mrb_ary_push(mrb, ary, host); return ary; }
mrb_value mrb_io_sysread(mrb_state *mrb, mrb_value io) { struct mrb_io *fptr; mrb_value buf = mrb_nil_value(); int maxlen, ret; mrb_get_args(mrb, "i|S", &maxlen, &buf); if (maxlen < 0) { return mrb_nil_value(); } if (mrb_nil_p(buf)) { buf = mrb_str_new(mrb, "", maxlen); } if (RSTRING_LEN(buf) != maxlen) { buf = mrb_str_resize(mrb, buf, maxlen); } fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type); ret = read(fptr->fd, RSTRING_PTR(buf), maxlen); switch (ret) { case 0: /* EOF */ if (maxlen == 0) { buf = mrb_str_new_cstr(mrb, ""); } else { mrb_raise(mrb, E_EOF_ERROR, "sysread failed: End of File"); return mrb_nil_value(); } break; case -1: /* Error */ mrb_raise(mrb, E_IO_ERROR, "sysread failed"); return mrb_nil_value(); break; default: if (RSTRING_LEN(buf) != ret) { buf = mrb_str_resize(mrb, buf, ret); } break; } return buf; }
mrb_value mrb_file__getwd(mrb_state *mrb, mrb_value klass) { mrb_value path; path = mrb_str_buf_new(mrb, MAXPATHLEN); if (GETCWD(RSTRING_PTR(path), MAXPATHLEN) == NULL) { mrb_sys_fail(mrb, "getcwd(2)"); } mrb_str_resize(mrb, path, strlen(RSTRING_PTR(path))); return path; }
static mrb_value mrb_basicsocket_recvfrom(mrb_state *mrb, mrb_value self) { int n; mrb_int maxlen, flags = 0; mrb_value ary, buf, sa; socklen_t socklen; mrb_get_args(mrb, "i|i", &maxlen, &flags); buf = mrb_str_buf_new(mrb, maxlen); socklen = sizeof(struct sockaddr_storage); sa = mrb_str_buf_new(mrb, socklen); n = recvfrom(socket_fd(mrb, self), RSTRING_PTR(buf), maxlen, flags, (struct sockaddr *)RSTRING_PTR(sa), &socklen); if (n == -1) mrb_sys_fail(mrb, "recvfrom"); mrb_str_resize(mrb, buf, n); mrb_str_resize(mrb, sa, socklen); ary = mrb_ary_new_capa(mrb, 2); mrb_ary_push(mrb, ary, buf); mrb_ary_push(mrb, ary, sa); return ary; }
static mrb_value sym_inspect(mrb_state *mrb, mrb_value sym) { #ifdef INCLUDE_ENCODING #define STR_ENC_GET(mrb, str) mrb_enc_from_index(mrb, ENCODING_GET(mrb, str)) mrb_value str; mrb_sym id = SYM2ID(sym); mrb_encoding *enc; const char *ptr; long len; char *dest; mrb_encoding *resenc = mrb_default_internal_encoding(mrb); if (resenc == NULL) resenc = mrb_default_external_encoding(mrb); sym = mrb_str_new_cstr(mrb, mrb_sym2name(mrb, id));//mrb_id2str(id); enc = STR_ENC_GET(mrb, sym); ptr = RSTRING_PTR(sym); len = RSTRING_LEN(sym); if ((resenc != enc && !mrb_str_is_ascii_only_p(mrb, sym)) || len != (long)strlen(ptr) || !mrb_enc_symname_p(ptr, enc) || !sym_printable(mrb, ptr, ptr + len, enc)) { str = mrb_str_inspect(mrb, sym); len = RSTRING_LEN(str); mrb_str_resize(mrb, str, len + 1); dest = RSTRING_PTR(str); memmove(dest + 1, dest, len); dest[0] = ':'; } else { char *dest; str = mrb_enc_str_new(mrb, 0, len + 1, enc); dest = RSTRING_PTR(str); dest[0] = ':'; memcpy(dest + 1, ptr, len); } return str; #else mrb_value str; const char *name; mrb_sym id = SYM2ID(sym); name = mrb_sym2name(mrb, id); //mrb_id2name(id); str = mrb_str_new(mrb, 0, strlen(name)+1); RSTRING(str)->buf[0] = ':'; strcpy(RSTRING(str)->buf+1, name); if (!mrb_symname_p(name)) { str = mrb_str_dump(mrb, str); strncpy(RSTRING(str)->buf, ":\"", 2); } return str; #endif }
static mrb_value mrb_basicsocket_recv(mrb_state *mrb, mrb_value self) { int n; mrb_int maxlen, flags = 0; mrb_value buf; mrb_get_args(mrb, "i|i", &maxlen, &flags); buf = mrb_str_buf_new(mrb, maxlen); n = recv(socket_fd(mrb, self), RSTRING_PTR(buf), maxlen, flags); if (n == -1) mrb_sys_fail(mrb, "recv"); mrb_str_resize(mrb, buf, n); return buf; }
static mrb_value mrb_ssl_read(mrb_state *mrb, mrb_value self) { ssl_context *ssl; mrb_int maxlen = 0; mrb_value buf; int ret; mrb_get_args(mrb, "i", &maxlen); buf = mrb_str_buf_new(mrb, maxlen); ssl = DATA_CHECK_GET_PTR(mrb, self, &mrb_ssl_type, ssl_context); ret = ssl_read(ssl, (unsigned char *)RSTRING_PTR(buf), maxlen); if ( ret == 0 || ret == POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY) { return mrb_nil_value(); } else if (ret < 0) { mrb_raise(mrb, E_SSL_ERROR, "ssl_read() returned E_SSL_ERROR"); } else { mrb_str_resize(mrb, buf, ret); } return buf; }
static mrb_value mrb_base58_encode(mrb_state *mrb, mrb_value self) { const char *chars = default_chars; char *custom_chars; char *data; mrb_int data_len; mrb_value result; mrb_int result_len = 0; if (mrb_get_args(mrb, "s|z", &data, &data_len, &custom_chars) == 2) { if (strlen(custom_chars) != 58) mrb_raise(mrb, E_ARGUMENT_ERROR, "Base58 requires 58 symbols"); chars = custom_chars; } result = mrb_str_buf_new(mrb, base58_encoded_enough_length(data_len)); result_len = base58_encode_reverse(mrb, RSTRING_PTR(result), data, data_len, chars); result = mrb_str_resize(mrb, result, result_len); return mrb_funcall(mrb, result, "reverse", 0); }
static mrb_value mrb_base58_decode(mrb_state *mrb, mrb_value self) { const char *chars = default_chars; char *custom_chars; char *data; mrb_int data_len; mrb_value result; mrb_int result_len = 0; if (mrb_get_args(mrb, "s|z", &data, &data_len, &custom_chars) == 2) { if (strlen(custom_chars) != 58) mrb_raise(mrb, E_ARGUMENT_ERROR, "Base58 requires 58 symbols"); chars = custom_chars; } result = mrb_str_buf_new(mrb, data_len); result_len = base58_decode(mrb, RSTRING_PTR(result), data, data_len, chars); if (result_len < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid Base58 symbol"); return mrb_str_resize(mrb, result, result_len); }
static mrb_value mrb_LZ4_decompress_safe(mrb_state *mrb, mrb_value self) { char *source; mrb_int compressedSize, maxDecompressedSize; mrb_get_args(mrb, "si", &source, &compressedSize, &maxDecompressedSize); if (unlikely(maxDecompressedSize < 0)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "maxDecompressedSize mustn't be negative"); } mrb_value dest = mrb_str_buf_new(mrb, maxDecompressedSize); int decompressedSize = LZ4_decompress_safe(source, RSTRING_PTR(dest), compressedSize, RSTRING_CAPA(dest)); if (decompressedSize >= 0) { return mrb_str_resize(mrb, dest, decompressedSize); } else { mrb_raise(mrb, E_LZ4_ERROR, "cannot decompress, source may be malformed or maxDecompressedSize is too small"); } }
static mrb_value mrb_LZ4_compress_default(mrb_state *mrb, mrb_value self) { char *source; mrb_int source_size; mrb_get_args(mrb, "s", &source, &source_size); int maxDestSize = LZ4_COMPRESSBOUND(source_size); if (likely(maxDestSize)) { mrb_value dest = mrb_str_buf_new(mrb, maxDestSize); int destSize = LZ4_compress_default(source, RSTRING_PTR(dest), source_size, RSTRING_CAPA(dest)); if (likely(destSize)) { return mrb_str_resize(mrb, dest, destSize); } else { mrb_raise(mrb, E_LZ4_ERROR, "cannot compress"); } } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "source_size is too large"); } }
static mrb_value mrb_file_realpath(mrb_state *mrb, mrb_value klass) { mrb_value pathname, dir_string, s, result; int argc; char *cpath; argc = mrb_get_args(mrb, "S|S", &pathname, &dir_string); if (argc == 2) { s = mrb_str_dup(mrb, dir_string); s = mrb_str_append(mrb, s, mrb_str_new_cstr(mrb, FILE_SEPARATOR)); s = mrb_str_append(mrb, s, pathname); pathname = s; } cpath = mrb_str_to_cstr(mrb, pathname); result = mrb_str_buf_new(mrb, PATH_MAX); if (realpath(cpath, RSTRING_PTR(result)) == NULL) mrb_sys_fail(mrb, cpath); mrb_str_resize(mrb, result, strlen(RSTRING_PTR(result))); return result; }
mrb_value mrb_str_format(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value fmt) { const char *p, *end; char *buf; mrb_int blen; mrb_int bsiz; mrb_value result; mrb_int n; mrb_int width; mrb_int prec; int nextarg = 1; int posarg = 0; mrb_value nextvalue; mrb_value str; mrb_value hash = mrb_undef_value(); #define CHECK_FOR_WIDTH(f) \ if ((f) & FWIDTH) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "width given twice"); \ } \ if ((f) & FPREC0) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "width after precision"); \ } #define CHECK_FOR_FLAGS(f) \ if ((f) & FWIDTH) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after width"); \ } \ if ((f) & FPREC0) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after precision"); \ } ++argc; --argv; mrb_to_str(mrb, fmt); p = RSTRING_PTR(fmt); end = p + RSTRING_LEN(fmt); blen = 0; bsiz = 120; result = mrb_str_new_capa(mrb, bsiz); buf = RSTRING_PTR(result); memset(buf, 0, bsiz); for (; p < end; p++) { const char *t; mrb_sym id = 0; int flags = FNONE; for (t = p; t < end && *t != '%'; t++) ; if (t + 1 == end) ++t; PUSH(p, t - p); if (t >= end) goto sprint_exit; /* end of fmt string */ p = t + 1; /* skip '%' */ width = prec = -1; nextvalue = mrb_undef_value(); retry: switch (*p) { default: mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - \\%%S", mrb_str_new(mrb, p, 1)); 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 (!mrb_undef_p(nextvalue)) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "value given twice - %S$", mrb_fixnum_value(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 == '<') ? '>' : '}'; mrb_value symname; for (; p < end && *p != term; ) p++; if (id) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "name%S after <%S>", mrb_str_new(mrb, start, p - start + 1), mrb_sym2str(mrb, id)); } symname = mrb_str_new(mrb, start + 1, p - start - 1); id = mrb_intern_str(mrb, symname); nextvalue = GETNAMEARG(mrb_symbol_value(id), start, (mrb_int)(p - start + 1)); if (mrb_undef_p(nextvalue)) { mrb_raisef(mrb, E_KEY_ERROR, "key%S not found", mrb_str_new(mrb, start, p - start + 1)); } 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) { mrb_raise(mrb, E_ARGUMENT_ERROR, "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--; /* fallthrough */ case '%': if (flags != FNONE) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format character - %"); } PUSH("%", 1); break; case 'c': { mrb_value val = GETARG(); mrb_value tmp; char *c; tmp = mrb_check_string_type(mrb, val); if (!mrb_nil_p(tmp)) { if (RSTRING_LEN(tmp) != 1) { mrb_raise(mrb, E_ARGUMENT_ERROR, "%c requires a character"); } } else if (mrb_fixnum_p(val)) { mrb_int n = mrb_fixnum(val); if (n < 0x80) { char buf[1]; buf[0] = (char)n; tmp = mrb_str_new(mrb, buf, 1); } else { tmp = mrb_funcall(mrb, val, "chr", 0); mrb_check_type(mrb, tmp, MRB_TT_STRING); } } else { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid character"); } c = RSTRING_PTR(tmp); n = RSTRING_LEN(tmp); if (!(flags & FWIDTH)) { PUSH(c, n); } else if ((flags & FMINUS)) { PUSH(c, n); if (width>0) FILL(' ', width-1); } else { if (width>0) FILL(' ', width-1); PUSH(c, n); } } break; case 's': case 'p': format_s: { mrb_value arg = GETARG(); mrb_int len; mrb_int slen; if (*p == 'p') arg = mrb_inspect(mrb, arg); str = mrb_obj_as_string(mrb, arg); len = RSTRING_LEN(str); if (RSTRING(result)->flags & MRB_STR_EMBED) { mrb_int tmp_n = len; RSTRING(result)->flags &= ~MRB_STR_EMBED_LEN_MASK; RSTRING(result)->flags |= tmp_n << MRB_STR_EMBED_LEN_SHIFT; } else { RSTRING(result)->as.heap.len = blen; } if (flags&(FPREC|FWIDTH)) { slen = RSTRING_LEN(str); if (slen < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid mbstring sequence"); } if ((flags&FPREC) && (prec < slen)) { char *p = RSTRING_PTR(str) + prec; slen = prec; len = (mrb_int)(p - RSTRING_PTR(str)); } /* need to adjust multi-byte string pos */ if ((flags&FWIDTH) && (width > slen)) { width -= (int)slen; if (!(flags&FMINUS)) { FILL(' ', width); } PUSH(RSTRING_PTR(str), len); if (flags&FMINUS) { FILL(' ', width); } break; } } PUSH(RSTRING_PTR(str), len); } break; case 'd': case 'i': case 'o': case 'x': case 'X': case 'b': case 'B': case 'u': { mrb_value val = GETARG(); char nbuf[68], *s; const char *prefix = NULL; int sign = 0, dots = 0; char sc = 0; mrb_int v = 0; int base; mrb_int len; 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; default: break; } } bin_retry: switch (mrb_type(val)) { #ifndef MRB_WITHOUT_FLOAT case MRB_TT_FLOAT: val = mrb_flo_to_fixnum(mrb, val); if (mrb_fixnum_p(val)) goto bin_retry; break; #endif case MRB_TT_STRING: val = mrb_str_to_inum(mrb, val, 0, TRUE); goto bin_retry; case MRB_TT_FIXNUM: v = mrb_fixnum(val); break; default: val = mrb_Integer(mrb, 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': sign = 1; default: base = 10; break; } if (sign) { if (v >= 0) { if (flags & FPLUS) { sc = '+'; width--; } else if (flags & FSPACE) { sc = ' '; width--; } } else { sc = '-'; width--; } mrb_assert(base == 10); snprintf(nbuf, sizeof(nbuf), "%" MRB_PRId, v); s = nbuf; if (v < 0) s++; /* skip minus sign */ } else { s = nbuf; if (v < 0) { dots = 1; } switch (base) { case 2: if (v < 0) { val = mrb_fix2binstr(mrb, mrb_fixnum_value(v), base); } else { val = mrb_fixnum_to_str(mrb, mrb_fixnum_value(v), base); } strncpy(++s, RSTRING_PTR(val), sizeof(nbuf)-1); break; case 8: snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIo, v); break; case 16: snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIx, v); break; } if (v < 0) { char d; s = remove_sign_bits(s, base); switch (base) { case 16: d = 'f'; break; case 8: d = '7'; break; case 2: d = '1'; break; default: d = 0; break; } if (d && *s != d) { *--s = d; } } } { size_t size; size = strlen(s); /* PARANOID: assert(size <= MRB_INT_MAX) */ len = (mrb_int)size; } if (*p == 'X') { char *pp = s; int c; while ((c = (int)(unsigned char)*pp) != 0) { *pp = toupper(c); pp++; } } if (prefix && !prefix[1]) { /* octal */ if (dots) { prefix = NULL; } else if (len == 1 && *s == '0') { len = 0; if (flags & FPREC) prec--; } else if ((flags & FPREC) && (prec > len)) { prefix = NULL; } } else if (len == 1 && *s == '0') { prefix = NULL; } if (prefix) { size_t size; size = strlen(prefix); /* PARANOID: assert(size <= MRB_INT_MAX). * this check is absolutely paranoid. */ width -= (mrb_int)size; } 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) && width > 0) { FILL(' ', width); width = 0; } if (sc) PUSH(&sc, 1); if (prefix) { int plen = (int)strlen(prefix); PUSH(prefix, plen); } if (dots) { prec -= 2; width -= 2; PUSH("..", 2); } if (prec > len) { CHECK(prec - len); if ((flags & (FMINUS|FPREC)) != FMINUS) { char c = '0'; FILL(c, prec - len); } else if (v < 0) { char c = sign_bits(base, p); FILL(c, prec - len); } } PUSH(s, len); if (width > 0) { FILL(' ', width); } } break; #ifndef MRB_WITHOUT_FLOAT case 'f': case 'g': case 'G': case 'e': case 'E': case 'a': case 'A': { mrb_value val = GETARG(); double fval; mrb_int i; mrb_int need = 6; char fbuf[32]; int frexp_result; fval = mrb_float(mrb_Float(mrb, val)); if (!isfinite(fval)) { const char *expr; const mrb_int elen = 3; char sign = '\0'; if (isnan(fval)) { expr = "NaN"; } else { expr = "Inf"; } need = elen; if (!isnan(fval) && fval < 0.0) sign = '-'; else if (flags & (FPLUS|FSPACE)) sign = (flags & FPLUS) ? '+' : ' '; if (sign) ++need; if ((flags & FWIDTH) && need < width) need = width; if (need < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "width too big"); } FILL(' ', need); if (flags & FMINUS) { if (sign) buf[blen - need--] = sign; memcpy(&buf[blen - need], expr, elen); } else { if (sign) buf[blen - elen - 1] = sign; memcpy(&buf[blen - elen], expr, elen); } break; } fmt_setup(fbuf, sizeof(fbuf), *p, flags, width, prec); need = 0; if (*p != 'e' && *p != 'E') { i = INT_MIN; frexp(fval, &frexp_result); i = (mrb_int)frexp_result; if (i > 0) need = BIT_DIGITS(i); } if (need > MRB_INT_MAX - ((flags&FPREC) ? prec : 6)) { too_big_width: mrb_raise(mrb, E_ARGUMENT_ERROR, (width > prec ? "width too big" : "prec too big")); } need += (flags&FPREC) ? prec : 6; if ((flags&FWIDTH) && need < width) need = width; if (need > MRB_INT_MAX - 20) { goto too_big_width; } need += 20; CHECK(need); n = snprintf(&buf[blen], need, fbuf, fval); if (n < 0 || n >= need) { mrb_raise(mrb, E_RUNTIME_ERROR, "formatting error"); } blen += n; } break; #endif } flags = FNONE; } sprint_exit: #if 0 /* 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 (mrb_test(ruby_debug)) mrb_raise(mrb, E_ARGUMENT_ERROR, mesg); if (mrb_test(ruby_verbose)) mrb_warn(mrb, "%S", mrb_str_new_cstr(mrb, mesg)); } #endif mrb_str_resize(mrb, result, blen); return result; }
mrb_value mrb_str_format(mrb_state *mrb, int argc, const mrb_value *argv, mrb_value fmt) { const char *p, *end; char *buf; mrb_int blen; mrb_int bsiz; mrb_value result; mrb_int n; mrb_int width; mrb_int prec; int flags = FNONE; int nextarg = 1; int posarg = 0; mrb_value nextvalue; mrb_value tmp; mrb_value str; mrb_value hash = mrb_undef_value(); #define CHECK_FOR_WIDTH(f) \ if ((f) & FWIDTH) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "width given twice"); \ } \ if ((f) & FPREC0) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "width after precision"); \ } #define CHECK_FOR_FLAGS(f) \ if ((f) & FWIDTH) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after width"); \ } \ if ((f) & FPREC0) { \ mrb_raise(mrb, E_ARGUMENT_ERROR, "flag after precision"); \ } ++argc; --argv; mrb_string_value(mrb, &fmt); p = RSTRING_PTR(fmt); end = p + RSTRING_LEN(fmt); blen = 0; bsiz = 120; result = mrb_str_buf_new(mrb, bsiz); buf = RSTRING_PTR(result); memset(buf, 0, bsiz); for (; p < end; p++) { const char *t; mrb_sym id = 0; for (t = p; t < end && *t != '%'; t++) ; PUSH(p, t - p); if (t >= end) goto sprint_exit; /* end of fmt string */ p = t + 1; /* skip `%' */ width = prec = -1; nextvalue = mrb_undef_value(); retry: switch (*p) { default: mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - \\%%S", mrb_str_new(mrb, p, 1)); 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 (!mrb_undef_p(nextvalue)) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "value given twice - %S$", mrb_fixnum_value(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 == '<') ? '>' : '}'; mrb_value symname; for (; p < end && *p != term; ) p++; if (id) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "name%S after <%S>", mrb_str_new(mrb, start, p - start + 1), mrb_sym2str(mrb, id)); } symname = mrb_str_new(mrb, start + 1, p - start - 1); id = mrb_intern_str(mrb, symname); nextvalue = GETNAMEARG(mrb_symbol_value(id), start, (int)(p - start + 1)); if (mrb_undef_p(nextvalue)) { mrb_raisef(mrb, E_KEY_ERROR, "key%S not found", mrb_str_new(mrb, start, p - start + 1)); } 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) { mrb_raise(mrb, E_ARGUMENT_ERROR, "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) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format character - %"); } PUSH("%", 1); break; case 'c': { mrb_value val = GETARG(); mrb_value tmp; unsigned int c; tmp = mrb_check_string_type(mrb, val); if (!mrb_nil_p(tmp)) { if (RSTRING_LEN(tmp) != 1 ) { mrb_raise(mrb, E_ARGUMENT_ERROR, "%c requires a character"); } c = RSTRING_PTR(tmp)[0]; n = 1; } else { c = mrb_fixnum(val); n = 1; } if (n <= 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid character"); } if (!(flags & FWIDTH)) { CHECK(n); buf[blen] = c; blen += n; } else if ((flags & FMINUS)) { CHECK(n); buf[blen] = c; blen += n; FILL(' ', width-1); } else { FILL(' ', width-1); CHECK(n); buf[blen] = c; blen += n; } } break; case 's': case 'p': format_s: { mrb_value arg = GETARG(); mrb_int len; mrb_int slen; if (*p == 'p') arg = mrb_inspect(mrb, arg); str = mrb_obj_as_string(mrb, arg); len = RSTRING_LEN(str); RSTRING_LEN(result) = blen; if (flags&(FPREC|FWIDTH)) { slen = RSTRING_LEN(str); if (slen < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid mbstring sequence"); } if ((flags&FPREC) && (prec < slen)) { char *p = RSTRING_PTR(str) + prec; 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); blen += len; if (flags&FMINUS) { CHECK(width); while (width--) { buf[blen++] = ' '; } } break; } } PUSH(RSTRING_PTR(str), len); } break; case 'd': case 'i': case 'o': case 'x': case 'X': case 'b': case 'B': case 'u': { mrb_value val = GETARG(); char fbuf[32], nbuf[64], *s; const char *prefix = NULL; int sign = 0, dots = 0; char sc = 0; mrb_int v = 0, org_v = 0; int base; mrb_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; default: 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; default: break; } } bin_retry: switch (mrb_type(val)) { case MRB_TT_FLOAT: if (FIXABLE(mrb_float(val))) { val = mrb_fixnum_value((mrb_int)mrb_float(val)); goto bin_retry; } val = mrb_flt2big(mrb, mrb_float(val)); if (mrb_fixnum_p(val)) goto bin_retry; break; case MRB_TT_STRING: val = mrb_str_to_inum(mrb, val, 0, TRUE); goto bin_retry; case MRB_TT_FIXNUM: v = mrb_fixnum(val); break; default: val = mrb_Integer(mrb, 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 == 2) { org_v = v; if ( v < 0 && !sign ) { val = mrb_fix2binstr(mrb, mrb_fixnum_value(v), base); dots = 1; } else { val = mrb_fix2str(mrb, mrb_fixnum_value(v), base); } v = mrb_fixnum(mrb_str_to_inum(mrb, val, 10, 0/*Qfalse*/)); } if (sign) { char c = *p; if (c == 'i') c = 'd'; /* %d and %i are identical */ if (base == 2) c = 'd'; 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 { char c = *p; if (c == 'X') c = 'x'; if (base == 2) c = 'd'; s = nbuf; if (v < 0) { dots = 1; } snprintf(fbuf, sizeof(fbuf), "%%l%c", c); snprintf(++s, sizeof(nbuf) - 1, fbuf, v); if (v < 0) { char d; s = remove_sign_bits(s, base); switch (base) { case 16: d = 'f'; break; case 8: d = '7'; break; case 2: d = '1'; break; default: d = 0; break; } if (d && *s != d) { *--s = d; } } } { size_t size; size = strlen(s); /* PARANOID: assert(size <= MRB_INT_MAX) */ len = (mrb_int)size; } if (dots) { prec -= 2; width -= 2; } if (*p == 'X') { char *pp = s; int c; while ((c = (int)(unsigned char)*pp) != 0) { *pp = toupper(c); pp++; } } if (prefix && !prefix[1]) { /* octal */ if (dots) { prefix = NULL; } else if (len == 1 && *s == '0') { len = 0; if (flags & FPREC) prec--; } else if ((flags & FPREC) && (prec > len)) { prefix = NULL; } } else if (len == 1 && *s == '0') { prefix = NULL; } if (prefix) { size_t size; size = strlen(prefix); /* PARANOID: assert(size <= MRB_INT_MAX). * this check is absolutely paranoid. */ width -= (mrb_int)size; } 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 (v < 0 || (base == 2 && org_v < 0)) { char c = sign_bits(base, p); while (len < prec--) { buf[blen++] = c; } } else if ((flags & (FMINUS|FPREC)) != FMINUS) { char c = '0'; while (len < prec--) { buf[blen++] = c; } } PUSH(s, len); CHECK(width); while (width-- > 0) { buf[blen++] = ' '; } } break; case 'f': case 'g': case 'G': case 'e': case 'E': case 'a': case 'A': { mrb_value val = GETARG(); double fval; int i, need = 6; char fbuf[32]; fval = mrb_float(mrb_Float(mrb, val)); if (isnan(fval) || isinf(fval)) { const char *expr; const int elen = 3; if (isnan(fval)) { expr = "NaN"; } else { expr = "Inf"; } need = elen; if ((!isnan(fval) && fval < 0.0) || (flags & FPLUS)) need++; if ((flags & FWIDTH) && need < width) need = width; CHECK(need + 1); n = 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, elen); } else { if (!isnan(fval) && fval < 0.0) buf[blen + need - elen - 1] = '-'; else if (flags & FPLUS) buf[blen + need - elen - 1] = '+'; else if ((flags & FSPACE) && need > width) blen++; memcpy(&buf[blen + need - elen], expr, elen); } 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); n = snprintf(&buf[blen], need, fbuf, fval); blen += n; } break; } flags = FNONE; } sprint_exit: #if 0 /* 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 (mrb_test(ruby_debug)) mrb_raise(mrb, E_ARGUMENT_ERROR, mesg); if (mrb_test(ruby_verbose)) mrb_warn("%s", mesg); } #endif mrb_str_resize(mrb, result, blen); return result; }
/* * call-seq: * string.succ -> string * * Returns next sequence of the string; * * a = "abc" * a.succ #=> "abd" */ static mrb_value mrb_str_succ_bang(mrb_state *mrb, mrb_value self) { mrb_value result; unsigned char *p, *e, *b, *t; const char *prepend; struct RString *s = mrb_str_ptr(self); size_t l; if (RSTRING_LEN(self) == 0) return self; mrb_str_modify(mrb, s); l = RSTRING_LEN(self); b = p = (unsigned char*) RSTRING_PTR(self); t = e = p + l; *(e--) = 0; // find trailing ascii/number while (e >= b) { if (ISALNUM(*e)) break; e--; } if (e < b) { e = p + l - 1; result = mrb_str_new_lit(mrb, ""); } else { // find leading letter of the ascii/number b = e; while (b > p) { if (!ISALNUM(*b) || (ISALNUM(*b) && *b != '9' && *b != 'z' && *b != 'Z')) break; b--; } if (!ISALNUM(*b)) b++; result = mrb_str_new(mrb, (char*) p, b - p); } while (e >= b) { if (!ISALNUM(*e)) { if (*e == 0xff) { mrb_str_cat_lit(mrb, result, "\x01"); (*e) = 0; } else (*e)++; break; } prepend = NULL; if (*e == '9') { if (e == b) prepend = "1"; *e = '0'; } else if (*e == 'z') { if (e == b) prepend = "a"; *e = 'a'; } else if (*e == 'Z') { if (e == b) prepend = "A"; *e = 'A'; } else { (*e)++; break; } if (prepend) mrb_str_cat_cstr(mrb, result, prepend); e--; } result = mrb_str_cat(mrb, result, (char*) b, t - b); l = RSTRING_LEN(result); mrb_str_resize(mrb, self, l); memcpy(RSTRING_PTR(self), RSTRING_PTR(result), l); return self; }