Imported Upstream version 1.34.0 92/192692/1 upstream/1.34.0
authorSeonah Moon <seonah1.moon@samsung.com>
Thu, 8 Nov 2018 04:45:15 +0000 (13:45 +0900)
committerSeonah Moon <seonah1.moon@samsung.com>
Thu, 8 Nov 2018 04:46:20 +0000 (13:46 +0900)
Change-Id: I8cb88098c3cc96bf09f8bc660c47765ab801ef95

405 files changed:
AUTHORS
CMakeLists.txt
CMakeOptions.txt
ChangeLog
Makefile.in
README.rst
aclocal.m4
compile
config.guess
config.sub
configure
configure.ac
contrib/Makefile.in
depcomp
doc/Makefile.am
doc/Makefile.in
doc/bash_completion/nghttpx
doc/enums.rst
doc/h2load.1
doc/h2load.1.rst
doc/nghttp.1
doc/nghttp2_option_set_peer_max_concurrent_streams.rst
doc/nghttp2_submit_altsvc.rst
doc/nghttp2_submit_origin.rst [new file with mode: 0644]
doc/nghttp2_submit_request.rst
doc/nghttpd.1
doc/nghttpx.1
doc/nghttpx.1.rst
doc/programmers-guide.rst
doc/sources/contribute.rst
doc/sources/nghttpx-howto.rst
doc/types.rst
examples/Makefile.in
examples/asio-cl.cc
examples/asio-cl2.cc
examples/asio-sv2.cc
examples/client.c
examples/deflate.c
examples/libevent-client.c
examples/libevent-server.c
install-sh
integration-tests/CMakeLists.txt
integration-tests/Makefile.in
integration-tests/server_tester.go
lib/CMakeLists.txt
lib/Makefile.in
lib/Makefile.msvc
lib/includes/Makefile.in
lib/includes/nghttp2/nghttp2.h
lib/includes/nghttp2/nghttp2ver.h
lib/nghttp2_buf.h
lib/nghttp2_callbacks.h
lib/nghttp2_debug.h
lib/nghttp2_frame.c
lib/nghttp2_frame.h
lib/nghttp2_hd.c
lib/nghttp2_hd.h
lib/nghttp2_hd_huffman.h
lib/nghttp2_helper.c
lib/nghttp2_helper.h
lib/nghttp2_http.c
lib/nghttp2_http.h
lib/nghttp2_int.h
lib/nghttp2_map.h
lib/nghttp2_mem.h
lib/nghttp2_net.h
lib/nghttp2_npn.h
lib/nghttp2_option.c
lib/nghttp2_option.h
lib/nghttp2_outbound_item.c
lib/nghttp2_outbound_item.h
lib/nghttp2_pq.h
lib/nghttp2_priority_spec.h
lib/nghttp2_queue.h
lib/nghttp2_rcbuf.h
lib/nghttp2_session.c
lib/nghttp2_session.h
lib/nghttp2_stream.h
lib/nghttp2_submit.c
lib/nghttp2_submit.h
lib/nghttp2_version.c
lib/version.rc.in
ltmain.sh
m4/libtool.m4
missing
python/Makefile.in
script/Makefile.in
src/HtmlParser.h
src/HttpServer.cc
src/Makefile.in
src/allocator.h
src/app_helper.cc
src/app_helper.h
src/asio_client_session.cc
src/asio_client_session_tcp_impl.cc
src/asio_client_session_tcp_impl.h
src/asio_client_session_tls_impl.cc
src/asio_client_tls_context.cc
src/asio_common.cc
src/asio_server_tls_context.cc
src/base64.h
src/base64_test.h
src/buffer_test.h
src/comp_helper.h
src/deflatehd.cc
src/h2load.cc
src/h2load.h
src/http2.cc
src/http2.h
src/http2_test.h
src/includes/Makefile.in
src/includes/nghttp2/asio_http2_client.h
src/inflatehd.cc
src/memchunk.h
src/memchunk_test.h
src/network.h
src/nghttp.cc
src/nghttp.h
src/nghttp2_config.h
src/nghttp2_gzip.h
src/nghttp2_gzip_test.h
src/nghttpd.cc
src/shrpx-unittest.cc
src/shrpx.cc
src/shrpx.h
src/shrpx_accept_handler.cc
src/shrpx_api_downstream_connection.cc
src/shrpx_client_handler.cc
src/shrpx_client_handler.h
src/shrpx_config.cc
src/shrpx_config.h
src/shrpx_config_test.cc
src/shrpx_config_test.h
src/shrpx_connection.cc
src/shrpx_connection.h
src/shrpx_connection_handler.cc
src/shrpx_connection_handler.h
src/shrpx_downstream.cc
src/shrpx_downstream.h
src/shrpx_downstream_test.h
src/shrpx_health_monitor_downstream_connection.cc
src/shrpx_http2_downstream_connection.cc
src/shrpx_http2_session.cc
src/shrpx_http2_session.h
src/shrpx_http2_upstream.cc
src/shrpx_http_downstream_connection.cc
src/shrpx_http_downstream_connection.h
src/shrpx_http_test.cc
src/shrpx_http_test.h
src/shrpx_https_upstream.cc
src/shrpx_live_check.cc
src/shrpx_log.cc
src/shrpx_log.h
src/shrpx_log_config.cc
src/shrpx_memcached_connection.cc
src/shrpx_mruby.cc
src/shrpx_mruby_module_env.cc
src/shrpx_mruby_module_request.cc
src/shrpx_mruby_module_response.cc
src/shrpx_rate_limit.cc
src/shrpx_router_test.h
src/shrpx_tls.cc
src/shrpx_tls.h
src/shrpx_tls_test.h
src/shrpx_worker.cc
src/shrpx_worker.h
src/shrpx_worker_process.cc
src/shrpx_worker_test.cc
src/shrpx_worker_test.h
src/ssl_compat.h
src/template_test.h
src/timegm.h
src/tls.h
src/util.cc
src/util.h
src/util_test.h
src/xsi_strerror.c
src/xsi_strerror.h
test-driver
tests/Makefile.in
tests/failmalloc.c
tests/failmalloc_test.c
tests/failmalloc_test.h
tests/main.c
tests/malloc_wrapper.h
tests/nghttp2_buf_test.h
tests/nghttp2_frame_test.c
tests/nghttp2_frame_test.h
tests/nghttp2_hd_test.h
tests/nghttp2_helper_test.h
tests/nghttp2_map_test.h
tests/nghttp2_npn_test.h
tests/nghttp2_pq_test.h
tests/nghttp2_queue_test.h
tests/nghttp2_session_test.c
tests/nghttp2_session_test.h
tests/nghttp2_stream_test.h
tests/nghttp2_test_helper.c
tests/nghttp2_test_helper.h
tests/testdata/Makefile.in
third-party/Makefile.in
third-party/mruby/AUTHORS
third-party/mruby/MITL
third-party/mruby/README.md
third-party/mruby/Rakefile
third-party/mruby/appveyor.yml
third-party/mruby/appveyor_config.rb
third-party/mruby/benchmark/bm_ao_render.rb
third-party/mruby/benchmark/bm_so_lists.rb
third-party/mruby/build_config.rb
third-party/mruby/doc/guides/debugger.md
third-party/mruby/doc/guides/gc-arena-howto.md
third-party/mruby/doc/guides/mrbgems.md
third-party/mruby/doc/limitations.md
third-party/mruby/include/mrbconf.h
third-party/mruby/include/mruby.h
third-party/mruby/include/mruby/array.h
third-party/mruby/include/mruby/boxing_nan.h
third-party/mruby/include/mruby/boxing_no.h
third-party/mruby/include/mruby/boxing_word.h
third-party/mruby/include/mruby/class.h
third-party/mruby/include/mruby/common.h
third-party/mruby/include/mruby/compile.h
third-party/mruby/include/mruby/debug.h
third-party/mruby/include/mruby/error.h
third-party/mruby/include/mruby/gc.h
third-party/mruby/include/mruby/irep.h
third-party/mruby/include/mruby/numeric.h
third-party/mruby/include/mruby/object.h
third-party/mruby/include/mruby/proc.h
third-party/mruby/include/mruby/string.h
third-party/mruby/include/mruby/value.h
third-party/mruby/include/mruby/variable.h
third-party/mruby/include/mruby/version.h
third-party/mruby/lib/mruby-core-ext.rb [moved from third-party/mruby/tasks/ruby_ext.rake with 100% similarity]
third-party/mruby/lib/mruby/build.rb [moved from third-party/mruby/tasks/mruby_build.rake with 93% similarity]
third-party/mruby/lib/mruby/build/command.rb [moved from third-party/mruby/tasks/mruby_build_commands.rake with 100% similarity]
third-party/mruby/lib/mruby/build/load_gems.rb [moved from third-party/mruby/tasks/mruby_build_gem.rake with 98% similarity]
third-party/mruby/lib/mruby/gem.rb [moved from third-party/mruby/tasks/mrbgem_spec.rake with 95% similarity]
third-party/mruby/mrbgems/default.gembox
third-party/mruby/mrbgems/mruby-array-ext/mrbgem.rake
third-party/mruby/mrbgems/mruby-array-ext/mrblib/array.rb
third-party/mruby/mrbgems/mruby-array-ext/src/array.c
third-party/mruby/mrbgems/mruby-array-ext/test/array.rb
third-party/mruby/mrbgems/mruby-bin-debugger/bintest/print.rb
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c
third-party/mruby/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c
third-party/mruby/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
third-party/mruby/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c
third-party/mruby/mrbgems/mruby-class-ext/src/class.c
third-party/mruby/mrbgems/mruby-class-ext/test/module.rb
third-party/mruby/mrbgems/mruby-compar-ext/mrbgem.rake [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-compar-ext/mrblib/compar.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-compiler/core/codegen.c
third-party/mruby/mrbgems/mruby-compiler/core/lex.def
third-party/mruby/mrbgems/mruby-compiler/core/parse.y
third-party/mruby/mrbgems/mruby-enum-ext/mrblib/enum.rb
third-party/mruby/mrbgems/mruby-enum-ext/test/enum.rb
third-party/mruby/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
third-party/mruby/mrbgems/mruby-enum-lazy/test/lazy.rb
third-party/mruby/mrbgems/mruby-enumerator/mrblib/enumerator.rb
third-party/mruby/mrbgems/mruby-enumerator/test/enumerator.rb
third-party/mruby/mrbgems/mruby-eval/src/eval.c
third-party/mruby/mrbgems/mruby-exit/src/mruby-exit.c
third-party/mruby/mrbgems/mruby-fiber/src/fiber.c
third-party/mruby/mrbgems/mruby-hash-ext/mrblib/hash.rb
third-party/mruby/mrbgems/mruby-hash-ext/src/hash-ext.c
third-party/mruby/mrbgems/mruby-hash-ext/test/hash.rb
third-party/mruby/mrbgems/mruby-io/.gitignore [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-io/.travis.yml [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-io/README.md [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-io/include/mruby/ext/io.h [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-io/mrbgem.rake [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-io/mrblib/file.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-io/mrblib/file_constants.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-io/mrblib/io.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-io/mrblib/kernel.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-io/run_test.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-io/src/file.c [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-io/src/file_test.c [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-io/src/io.c [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-io/src/mruby_io_gem.c [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-io/test/file.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-io/test/file_test.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-io/test/gc_filedes.sh [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-io/test/io.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-io/test/mruby_io_test.c [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-kernel-ext/mrblib/kernel.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-kernel-ext/src/kernel.c
third-party/mruby/mrbgems/mruby-kernel-ext/test/kernel.rb
third-party/mruby/mrbgems/mruby-math/src/math.c
third-party/mruby/mrbgems/mruby-method/README.md [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-method/mrbgem.rake [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-method/mrblib/kernel.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-method/mrblib/method.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-method/mrblib/unbound_method.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-method/src/method.c [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-method/test/method.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb
third-party/mruby/mrbgems/mruby-numeric-ext/src/numeric_ext.c
third-party/mruby/mrbgems/mruby-numeric-ext/test/numeric.rb
third-party/mruby/mrbgems/mruby-object-ext/src/object.c
third-party/mruby/mrbgems/mruby-object-ext/test/nil.rb
third-party/mruby/mrbgems/mruby-objectspace/test/objectspace.rb
third-party/mruby/mrbgems/mruby-pack/.gitignore [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-pack/.travis.yml [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-pack/README.md [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-pack/mrbgem.rake [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-pack/packtest.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-pack/run_test.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-pack/src/pack.c [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-pack/test/pack.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-print/src/print.c
third-party/mruby/mrbgems/mruby-proc-ext/test/proc.c
third-party/mruby/mrbgems/mruby-proc-ext/test/proc.rb
third-party/mruby/mrbgems/mruby-random/src/mt19937ar.c
third-party/mruby/mrbgems/mruby-range-ext/src/range.c
third-party/mruby/mrbgems/mruby-socket/.travis.yml [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-socket/README.md [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-socket/mrbgem.rake [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-socket/mrblib/socket.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-socket/run_test.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-socket/src/const.cstub [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-socket/src/const.def [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-socket/src/gen.rb [new file with mode: 0755]
third-party/mruby/mrbgems/mruby-socket/src/socket.c [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-socket/test/addrinfo.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-socket/test/basicsocket.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-socket/test/ipsocket.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-socket/test/socket.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-socket/test/sockettest.c [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-socket/test/tcpsocket.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-socket/test/udpsocket.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-socket/test/unix.rb [new file with mode: 0644]
third-party/mruby/mrbgems/mruby-sprintf/src/sprintf.c
third-party/mruby/mrbgems/mruby-sprintf/test/sprintf.rb
third-party/mruby/mrbgems/mruby-string-ext/mrblib/string.rb
third-party/mruby/mrbgems/mruby-string-ext/src/string.c
third-party/mruby/mrbgems/mruby-string-ext/test/string.rb
third-party/mruby/mrbgems/mruby-struct/src/struct.c
third-party/mruby/mrbgems/mruby-struct/test/struct.rb
third-party/mruby/mrbgems/mruby-symbol-ext/mrblib/symbol.rb
third-party/mruby/mrbgems/mruby-test/driver.c
third-party/mruby/mrbgems/mruby-test/init_mrbtest.c
third-party/mruby/mrbgems/mruby-test/mrbgem.rake
third-party/mruby/mrbgems/mruby-time/src/time.c
third-party/mruby/mrbgems/mruby-time/test/time.rb
third-party/mruby/mrblib/00class.rb
third-party/mruby/mrblib/10error.rb
third-party/mruby/mrblib/array.rb
third-party/mruby/mrblib/enum.rb
third-party/mruby/mrblib/float.rb [new file with mode: 0644]
third-party/mruby/mrblib/hash.rb
third-party/mruby/mrblib/mrblib.rake
third-party/mruby/mrblib/numeric.rb
third-party/mruby/mrblib/range.rb
third-party/mruby/mrblib/string.rb
third-party/mruby/src/array.c
third-party/mruby/src/backtrace.c
third-party/mruby/src/class.c
third-party/mruby/src/codedump.c
third-party/mruby/src/debug.c
third-party/mruby/src/dump.c
third-party/mruby/src/error.c
third-party/mruby/src/etc.c
third-party/mruby/src/fmt_fp.c
third-party/mruby/src/gc.c
third-party/mruby/src/hash.c
third-party/mruby/src/kernel.c
third-party/mruby/src/load.c
third-party/mruby/src/mruby_core.rake
third-party/mruby/src/numeric.c
third-party/mruby/src/object.c
third-party/mruby/src/pool.c
third-party/mruby/src/proc.c
third-party/mruby/src/range.c
third-party/mruby/src/state.c
third-party/mruby/src/string.c
third-party/mruby/src/symbol.c
third-party/mruby/src/variable.c
third-party/mruby/src/vm.c
third-party/mruby/tasks/mrbgems.rake
third-party/mruby/tasks/toolchains/gcc.rake
third-party/mruby/tasks/toolchains/visualcpp.rake
third-party/mruby/test/assert.rb
third-party/mruby/test/t/array.rb
third-party/mruby/test/t/bs_block.rb
third-party/mruby/test/t/class.rb
third-party/mruby/test/t/codegen.rb
third-party/mruby/test/t/float.rb
third-party/mruby/test/t/hash.rb
third-party/mruby/test/t/integer.rb
third-party/mruby/test/t/kernel.rb
third-party/mruby/test/t/lang.rb
third-party/mruby/test/t/module.rb
third-party/mruby/test/t/numeric.rb
third-party/mruby/test/t/proc.rb
third-party/mruby/test/t/range.rb
third-party/mruby/test/t/string.rb
third-party/mruby/travis_config.rb
third-party/neverbleed/neverbleed.c

diff --git a/AUTHORS b/AUTHORS
index abcced7..85ac644 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -16,6 +16,7 @@ github issues [2].
 187j3x1
 Alek Storm
 Alex Nalivko
+Alexandros Konstantinakis-Karmis
 Alexis La Goutte
 Amir Pakdel
 Anders Bakken
@@ -92,6 +93,7 @@ Tomasz Buchert
 Tomasz Torcz
 Vernon Tang
 Viacheslav Biriukov
+Viktor Szakats
 Viktor Szépe
 Wenfeng Liu
 Xiaoguang Sun
@@ -103,6 +105,7 @@ clemahieu
 dalf
 es
 fangdingjun
+jwchoi
 kumagi
 lstefani
 makovich
index 9ab152e..fccc1ce 100644 (file)
 
 cmake_minimum_required(VERSION 3.0)
 # XXX using 1.8.90 instead of 1.9.0-DEV
-project(nghttp2 VERSION 1.31.1)
+project(nghttp2 VERSION 1.34.0)
 
 # See versioning rule:
 #  http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
-set(LT_CURRENT  30)
+set(LT_CURRENT  31)
 set(LT_REVISION 1)
-set(LT_AGE      16)
+set(LT_AGE      17)
 
 set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
 include(Version)
index 1cfde80..a12479b 100644 (file)
@@ -14,6 +14,7 @@ option(ENABLE_PYTHON_BINDINGS "Build Python bindings"
   ${ENABLE_PYTHON_BINDINGS_DEFAULT})
 option(ENABLE_FAILMALLOC "Build failmalloc test program" ON)
 option(ENABLE_LIB_ONLY  "Build libnghttp2 only.  This is a short hand for -DENABLE_APP=0 -DENABLE_EXAMPLES=0 -DENABLE_HPACK_TOOLS=0 -DENABLE_PYTHON_BINDINGS=0")
+option(ENABLE_STATIC_LIB "Build libnghttp2 in static mode also")
 
 option(WITH_LIBXML2     "Use libxml2"
   ${WITH_LIBXML2_DEFAULT})
index a76e6b9..d973b9b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
-commit 1e22b36c61d52bb0446a63f5994b1fbe8c7ce0db (HEAD, tag: v1.31.1, origin/v1.31.x, origin/HEAD, v1.31.x)
+commit 2b085815b787270b6942fc86a414edace12d40c5 (HEAD, tag: v1.34.0, origin/master, origin/HEAD, master)
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-04-07
+AuthorDate: 2018-10-04
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-04-07
+CommitDate: 2018-10-04
 
     Update manual pages
 
-commit 0f818baf61c5762093d23520f7ee513d6e9e942e
+commit 986fa3026479174c766417e19834adea6f70233c
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-04-07
+AuthorDate: 2018-10-04
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-04-07
+CommitDate: 2018-10-04
 
-    Bump up version number to 1.31.1
+    Bump up version number to 1.34.0, LT revision to 31:1:17
 
-commit c411d16945d658a181d92ca36bfea30853edab37
+commit 7c8cb3a0ce08b3a2244e339654cca98033328acc
 Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-AuthorDate: 2018-04-07
+AuthorDate: 2018-10-04
 Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
-CommitDate: 2018-04-07
+CommitDate: 2018-10-04
 
-    Fix frame handling
+    nghttpx: Improve CONNECT response status handling
+
+commit 334c439ce05b721963be56341348c1d58289051a
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-10-04
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-10-04
+
+    Fix bug that regular CONNECT does not work
+
+commit 6700626c304d435c06c2aa8a65ef8ca09076ad9f
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-10-03
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-10-03
+
+    Rule out content-length in the successful response to CONNECT
+
+commit 15162addc4bd6c55798300c007647a43ab197683
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-10-02
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-10-02
+
+    Update manual pages
+
+commit 9327077741eab5271d29233bc80d6090348264fb
+Merge: fc7489e0 aeb92bbb
+Author:     Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com>
+AuthorDate: 2018-09-30
+Commit:     GitHub <noreply@github.com>
+CommitDate: 2018-09-30
+
+    Merge pull request #1235 from nghttp2/backend-conn-timeout
+    
+    nghttpx: Add read/write-timeout parameters to backend option
+
+commit aeb92bbbe29a552d2c68236c64a8176a0dcdbd8b
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-30
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-30
+
+    nghttpx: Add read/write-timeout parameters to backend option
+
+commit fc7489e044720818c041fb05dcde35c4abd2108b
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-30
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-30
+
+    nghttpx: Fix mruby parameter validation
+
+commit 87ac872fdcf828d7205355435c671f3cfb53fcdd
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-30
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-30
+
+    nghttpx: Update doc
+
+commit c278adde7a2ae9871b10a0d572edaa5be29b20dc
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-30
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-30
+
+    nghttpx: Log error when mruby file cannot be opened
+
+commit f94d72090996e0971c1b7bd3f1fbc367c1cb32e4
+Merge: 9b9baa6b d2a594a7
+Author:     Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com>
+AuthorDate: 2018-09-29
+Commit:     GitHub <noreply@github.com>
+CommitDate: 2018-09-29
+
+    Merge pull request #1234 from nghttp2/nghttpx-rfc8441
+    
+    nghttpx: Implement RFC 8441 Bootstrapping WebSocket with HTTP/2
+
+commit 9b9baa6bd9f4fd4a98c3747d5b17e5976c635869
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-29
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-29
+
+    Update doc
+
+commit 02566ee383fee0ccd628e6199519f0f2203a02e8
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-29
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-29
+
+    nghttpx: Update doc
+
+commit 3002f31b1fafc7d07cc3dd8d34b2ccb6f7768a0d
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-29
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-29
+
+    src: Add debug output for SETTINGS_ENABLE_CONNECT_PROTOCOL
+
+commit d2a594a75340a02c312b70199a2532956968c594
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-03-11
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-29
+
+    nghttpx: Implement RFC 8441 Bootstrapping WebSocket with HTTP/2
+
+commit 651e14771182e2292833d84ab513e5f018df51c2
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-28
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-28
+
+    Allow client sending :protocol optimistically
+
+commit a42faf1cc21ca64218fc468442e02d439249c25f
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-23
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-23
+
+    nghttpx: Write TLS alert during handshake
+
+commit 4aac05e1936ad14d66b994b789ab0abe0354a28c
+Merge: 8753b6da b80dfaa8
+Author:     Tatsuhiro Tsujikawa <404610+tatsuhiro-t@users.noreply.github.com>
+AuthorDate: 2018-09-23
+Commit:     GitHub <noreply@github.com>
+CommitDate: 2018-09-23
+
+    Merge pull request #1231 from nghttp2/ws-lib-only
+    
+    Implement RFC 8441
+
+commit b80dfaa8a0e61b375b5f0f878ee1a56b7a3fbd64
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-23
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-23
+
+    Adjustment for RFC 8441
+
+commit a19d8f5d31047944a29a6b865fd7fca07a71eba6
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-03-11
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-23
+
+    Deal with :protocol pseudo header
+
+commit 33f6e90a56da48d9af9e635bed39510f2e2705ff
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-03-10
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-23
+
+    Add NGHTTP2_TOKEN__PROTOCOL
+
+commit ed7fabcbc24b376bbf4b168aebcdb6f1a32cf688
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-03-10
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-23
+
+    Add SETTINGS_ENABLE_CONNECT_PROTOCOL
+
+commit 8753b6da1419a267e10b1a47a37825e0b748128a
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-17
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-17
+
+    Update doc
+
+commit f2de733bdf47c5dc16169fe45bbca870234efb98
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-16
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-16
+
+    Update neverbleed to fix OpenSSL 1.1.1 issues
+
+commit 88ff8c69a0621b335794271ade7003aad65bd5ef
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-16
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-16
+
+    Update mruby 1.4.1
+
+commit a63558a1ebe3d50f4b8ab339e0d3ecdc858803c3
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-16
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-16
+
+    nghttpx: Call OCSP_response_get1_basic only when OCSP status is successful
+
+commit 3575a1325e8664bf7a55c5e4c590a9a0f9d27b79
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-15
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-15
+
+    nghttpx: Fix crash with plain text HTTP
+
+commit e2de2fee69b691013e444a23254ff8cd13d01d30
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-15
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-15
+
+    Update bash_completion
+
+commit 9f415979fbd0f2a9257ec1d86ddfe849ebad8c50
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-15
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-15
+
+    Update manual pages
+
+commit 4bfc0cd196ae6ca459b847698f6756ba732c79aa
+Merge: a1ea1696 9c824b87
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-14
+Commit:     GitHub <noreply@github.com>
+CommitDate: 2018-09-14
+
+    Merge pull request #1230 from nghttp2/nghttpx-faster-logging
+    
+    nghttpx: Get rid of std::stringstream from Log
+
+commit 9c824b87fe647c4d587fe365d9bd63704ec826fe
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-08-31
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-14
+
+    nghttpx: Get rid of std::stringstream from Log
+
+commit a1ea1696becea1d3c3c2720d44acbf5dae759560
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-13
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-13
+
+    Make VALID_HD_NAME_CHARS and VALID_HD_VALUE_CHARS const qualified
+
+commit dfc0f248c60140194626c526221e45b6ee0b3d45
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-13
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-13
+
+    Make static_table const qualified
+
+commit ed7c9db2a657005fa80eb44593f99dbbf57e6dec
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-09
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-09
+
+    nghttpx: Add mruby env.tls_handshake_finished
+
+commit 5b42815afb6214ff22e9228ade7021b5c0bf26c4
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-09
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-09
+
+    nghttpx: Strip incoming Early-Data header field by default
+
+commit cfe7fa9a754c93e807fe2bc9e9046e3872969fa9
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-09
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-09
+
+    nghttpx: Add --tls13-ciphers and --tls-client-ciphers options
+
+commit cb8a9d58fdb3bff492d27977d5d5616220150ac0
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-09
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-09
+
+    src: Remove TLSv1.3 ciphers from DEFAULT_CIPHER_LIST
+    
+    TLSv1.3 ciphers are treated differently from the ciphers for TLSv1.2
+    or earlier.
+
+commit 023b94480bc365afbd3b2740323f3f02787b2901
+Merge: f79a5812 9b03c64f
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-09
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-09
+
+    Merge branch 'tls13-early-data'
+
+commit 9b03c64f68077fb54a68b4cae9fe35ca1d0a00ed
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-08
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-08
+
+    nghttpx: Should postpone early data by default
+
+commit b8eccec62dc7662d2c69aa129cfe5423c36ade5b
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-08
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-08
+
+    nghttpx: Disable OpenSSL anti-replay
+
+commit 9f212587205aebd7dbaabbe3472527140d377d20
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-05-20
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-08
+
+    Specify SSL_CTX_set_max_early_data and add an option to change max value
+
+commit 47f6012407fda0a49e467344bfe35b73279c9654
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2017-11-26
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-08
+
+    nghttpx: Add an option to postpone early data processing
+
+commit 770e44de4d31bfa3c3989306d087f14ce2f765ad
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2017-11-26
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-08
+
+    Implement draft-ietf-httpbis-replay-02
+    
+    nghttpx sends early-data header field when forwarding requests which
+    are received in TLSv1.3 early data, and the TLS handshake is still in
+    progress.
+
+commit 2ab319c1375bd3a8a7d07faffa28fc4e9da00041
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2017-11-26
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-08
+
+    Don't hide error code from openssl
+
+commit 3992302432acfb4f300bb2497cdd4355080a824c
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2017-04-29
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-08
+
+    Remove SSL_ERROR_WANT_WRITE handling
+
+commit b30f312a70971a3fc570a86f963be0bfe0412232
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2017-04-01
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-08
+
+    Honor SSL_read semantics
+
+commit c5cdb78a952162aec9d0f2c9341742bb0b208631
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2017-04-01
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-08
+
+    nghttpx: Add TLSv1.3 0-RTT early data support
+
+commit f79a58120e82e7e1382e60717b14d744daf803ab
+Author:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+AuthorDate: 2018-09-02
+Commit:     Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
+CommitDate: 2018-09-02
+
+    Bump up version number to 1.34.0
index 0db5ede..4724a2a 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -189,7 +189,7 @@ am__recursive_targets = \
   $(RECURSIVE_CLEAN_TARGETS) \
   $(am__extra_recursive_targets)
 AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
-       cscope distdir dist dist-all distcheck
+       cscope distdir distdir-am dist dist-all distcheck
 am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \
        $(LISP)config.h.in
 # Read a list of newline-separated strings from the standard input,
@@ -497,8 +497,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
            echo ' $(SHELL) ./config.status'; \
            $(SHELL) ./config.status;; \
          *) \
-           echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
-           cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \
        esac;
 
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -663,7 +663,10 @@ distclean-tags:
        -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
        -rm -f cscope.out cscope.in.out cscope.po.out cscope.files
 
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
        $(am__remove_distdir)
        test -d "$(distdir)" || mkdir "$(distdir)"
        @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
index 4e48058..0ed29e0 100644 (file)
@@ -951,7 +951,7 @@ output_length
     The length of the compressed header block.
 
 percentage_of_original_size
-    ``input_length`` / ``output_length`` * 100
+    ``output_length`` / ``input_length`` * 100
 
 wire
     The compressed header block as a hex string.
index decfb76..8ceb9ad 100644 (file)
@@ -1,6 +1,6 @@
-# generated automatically by aclocal 1.15.1 -*- Autoconf -*-
+# generated automatically by aclocal 1.16.1 -*- Autoconf -*-
 
-# Copyright (C) 1996-2017 Free Software Foundation, Inc.
+# Copyright (C) 1996-2018 Free Software Foundation, Inc.
 
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -296,7 +296,7 @@ AS_VAR_COPY([$1], [pkg_cv_][$1])
 AS_VAR_IF([$1], [""], [$5], [$4])dnl
 ])dnl PKG_CHECK_VAR
 
-# Copyright (C) 2002-2017 Free Software Foundation, Inc.
+# Copyright (C) 2002-2018 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -308,10 +308,10 @@ AS_VAR_IF([$1], [""], [$5], [$4])dnl
 # generated from the m4 files accompanying Automake X.Y.
 # (This private macro should not be called outside this file.)
 AC_DEFUN([AM_AUTOMAKE_VERSION],
-[am__api_version='1.15'
+[am__api_version='1.16'
 dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
 dnl require some minimum version.  Point them to the right macro.
-m4_if([$1], [1.15.1], [],
+m4_if([$1], [1.16.1], [],
       [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
 ])
 
@@ -327,14 +327,14 @@ m4_define([_AM_AUTOCONF_VERSION], [])
 # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
 # This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
 AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.15.1])dnl
+[AM_AUTOMAKE_VERSION([1.16.1])dnl
 m4_ifndef([AC_AUTOCONF_VERSION],
   [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
 _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
 
 # AM_AUX_DIR_EXPAND                                         -*- Autoconf -*-
 
-# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+# Copyright (C) 2001-2018 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -386,7 +386,7 @@ am_aux_dir=`cd "$ac_aux_dir" && pwd`
 
 # AM_CONDITIONAL                                            -*- Autoconf -*-
 
-# Copyright (C) 1997-2017 Free Software Foundation, Inc.
+# Copyright (C) 1997-2018 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -417,7 +417,7 @@ AC_CONFIG_COMMANDS_PRE(
 Usually this means the macro was only invoked conditionally.]])
 fi])])
 
-# Copyright (C) 1999-2017 Free Software Foundation, Inc.
+# Copyright (C) 1999-2018 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -608,13 +608,12 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl
 
 # Generate code to set up dependency tracking.              -*- Autoconf -*-
 
-# Copyright (C) 1999-2017 Free Software Foundation, Inc.
+# Copyright (C) 1999-2018 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
 
-
 # _AM_OUTPUT_DEPENDENCY_COMMANDS
 # ------------------------------
 AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
@@ -622,49 +621,41 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
   # Older Autoconf quotes --file arguments for eval, but not when files
   # are listed without --file.  Let's play safe and only enable the eval
   # if we detect the quoting.
-  case $CONFIG_FILES in
-  *\'*) eval set x "$CONFIG_FILES" ;;
-  *)   set x $CONFIG_FILES ;;
-  esac
+  # TODO: see whether this extra hack can be removed once we start
+  # requiring Autoconf 2.70 or later.
+  AS_CASE([$CONFIG_FILES],
+          [*\'*], [eval set x "$CONFIG_FILES"],
+          [*], [set x $CONFIG_FILES])
   shift
-  for mf
+  # Used to flag and report bootstrapping failures.
+  am_rc=0
+  for am_mf
   do
     # Strip MF so we end up with the name of the file.
-    mf=`echo "$mf" | sed -e 's/:.*$//'`
-    # Check whether this is an Automake generated Makefile or not.
-    # We used to match only the files named 'Makefile.in', but
-    # some people rename them; so instead we look at the file content.
-    # Grep'ing the first line is not enough: some people post-process
-    # each Makefile.in and add a new line on top of each file to say so.
-    # Grep'ing the whole file is not good either: AIX grep has a line
+    am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'`
+    # Check whether this is an Automake generated Makefile which includes
+    # dependency-tracking related rules and includes.
+    # Grep'ing the whole file directly is not great: AIX grep has a line
     # limit of 2048, but all sed's we know have understand at least 4000.
-    if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
-      dirpart=`AS_DIRNAME("$mf")`
-    else
-      continue
-    fi
-    # Extract the definition of DEPDIR, am__include, and am__quote
-    # from the Makefile without running 'make'.
-    DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
-    test -z "$DEPDIR" && continue
-    am__include=`sed -n 's/^am__include = //p' < "$mf"`
-    test -z "$am__include" && continue
-    am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
-    # Find all dependency output files, they are included files with
-    # $(DEPDIR) in their names.  We invoke sed twice because it is the
-    # simplest approach to changing $(DEPDIR) to its actual value in the
-    # expansion.
-    for file in `sed -n "
-      s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
-        sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
-      # Make sure the directory exists.
-      test -f "$dirpart/$file" && continue
-      fdir=`AS_DIRNAME(["$file"])`
-      AS_MKDIR_P([$dirpart/$fdir])
-      # echo "creating $dirpart/$file"
-      echo '# dummy' > "$dirpart/$file"
-    done
+    sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
+      || continue
+    am_dirpart=`AS_DIRNAME(["$am_mf"])`
+    am_filepart=`AS_BASENAME(["$am_mf"])`
+    AM_RUN_LOG([cd "$am_dirpart" \
+      && sed -e '/# am--include-marker/d' "$am_filepart" \
+        | $MAKE -f - am--depfiles]) || am_rc=$?
   done
+  if test $am_rc -ne 0; then
+    AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments
+    for automatic dependency tracking.  Try re-running configure with the
+    '--disable-dependency-tracking' option to at least be able to build
+    the package (albeit without support for automatic dependency tracking).])
+  fi
+  AS_UNSET([am_dirpart])
+  AS_UNSET([am_filepart])
+  AS_UNSET([am_mf])
+  AS_UNSET([am_rc])
+  rm -f conftest-deps.mk
 }
 ])# _AM_OUTPUT_DEPENDENCY_COMMANDS
 
@@ -673,18 +664,17 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
 # -----------------------------
 # This macro should only be invoked once -- use via AC_REQUIRE.
 #
-# This code is only required when automatic dependency tracking
-# is enabled.  FIXME.  This creates each '.P' file that we will
-# need in order to bootstrap the dependency handling code.
+# This code is only required when automatic dependency tracking is enabled.
+# This creates each '.Po' and '.Plo' makefile fragment that we'll need in
+# order to bootstrap the dependency handling code.
 AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
 [AC_CONFIG_COMMANDS([depfiles],
      [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
-     [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
-])
+     [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])])
 
 # Do all the work for Automake.                             -*- Autoconf -*-
 
-# Copyright (C) 1996-2017 Free Software Foundation, Inc.
+# Copyright (C) 1996-2018 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -771,8 +761,8 @@ AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
 AC_REQUIRE([AC_PROG_MKDIR_P])dnl
 # For better backward compatibility.  To be removed once Automake 1.9.x
 # dies out for good.  For more background, see:
-# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
-# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
 AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
 # We need awk for the "check" target (and possibly the TAP driver).  The
 # system "awk" is bad on some platforms.
@@ -839,7 +829,7 @@ END
 Aborting the configuration process, to ensure you take notice of the issue.
 
 You can download and install GNU coreutils to get an 'rm' implementation
-that behaves properly: <http://www.gnu.org/software/coreutils/>.
+that behaves properly: <https://www.gnu.org/software/coreutils/>.
 
 If you want to complete the configuration process using your problematic
 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
@@ -881,7 +871,7 @@ for _am_header in $config_headers :; do
 done
 echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
 
-# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+# Copyright (C) 2001-2018 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -902,7 +892,7 @@ if test x"${install_sh+set}" != xset; then
 fi
 AC_SUBST([install_sh])])
 
-# Copyright (C) 2003-2017 Free Software Foundation, Inc.
+# Copyright (C) 2003-2018 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -923,7 +913,7 @@ AC_SUBST([am__leading_dot])])
 
 # Check to see how 'make' treats includes.                 -*- Autoconf -*-
 
-# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+# Copyright (C) 2001-2018 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -931,49 +921,42 @@ AC_SUBST([am__leading_dot])])
 
 # AM_MAKE_INCLUDE()
 # -----------------
-# Check to see how make treats includes.
+# Check whether make has an 'include' directive that can support all
+# the idioms we need for our automatic dependency tracking code.
 AC_DEFUN([AM_MAKE_INCLUDE],
-[am_make=${MAKE-make}
-cat > confinc << 'END'
+[AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive])
+cat > confinc.mk << 'END'
 am__doit:
-       @echo this is the am__doit target
+       @echo this is the am__doit target >confinc.out
 .PHONY: am__doit
 END
-# If we don't find an include directive, just comment out the code.
-AC_MSG_CHECKING([for style of include used by $am_make])
 am__include="#"
 am__quote=
-_am_result=none
-# First try GNU make style include.
-echo "include confinc" > confmf
-# Ignore all kinds of additional output from 'make'.
-case `$am_make -s -f confmf 2> /dev/null` in #(
-*the\ am__doit\ target*)
-  am__include=include
-  am__quote=
-  _am_result=GNU
-  ;;
-esac
-# Now try BSD make style include.
-if test "$am__include" = "#"; then
-   echo '.include "confinc"' > confmf
-   case `$am_make -s -f confmf 2> /dev/null` in #(
-   *the\ am__doit\ target*)
-     am__include=.include
-     am__quote="\""
-     _am_result=BSD
-     ;;
-   esac
-fi
-AC_SUBST([am__include])
-AC_SUBST([am__quote])
-AC_MSG_RESULT([$_am_result])
-rm -f confinc confmf
-])
+# BSD make does it like this.
+echo '.include "confinc.mk" # ignored' > confmf.BSD
+# Other make implementations (GNU, Solaris 10, AIX) do it like this.
+echo 'include confinc.mk # ignored' > confmf.GNU
+_am_result=no
+for s in GNU BSD; do
+  AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out])
+  AS_CASE([$?:`cat confinc.out 2>/dev/null`],
+      ['0:this is the am__doit target'],
+      [AS_CASE([$s],
+          [BSD], [am__include='.include' am__quote='"'],
+          [am__include='include' am__quote=''])])
+  if test "$am__include" != "#"; then
+    _am_result="yes ($s style)"
+    break
+  fi
+done
+rm -f confinc.* confmf.*
+AC_MSG_RESULT([${_am_result}])
+AC_SUBST([am__include])])
+AC_SUBST([am__quote])])
 
 # Fake the existence of programs that GNU maintainers use.  -*- Autoconf -*-
 
-# Copyright (C) 1997-2017 Free Software Foundation, Inc.
+# Copyright (C) 1997-2018 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1012,7 +995,7 @@ fi
 
 # Helper functions for option handling.                     -*- Autoconf -*-
 
-# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+# Copyright (C) 2001-2018 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1041,7 +1024,7 @@ AC_DEFUN([_AM_SET_OPTIONS],
 AC_DEFUN([_AM_IF_OPTION],
 [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
 
-# Copyright (C) 1999-2017 Free Software Foundation, Inc.
+# Copyright (C) 1999-2018 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1088,7 +1071,7 @@ AC_LANG_POP([C])])
 # For backward compatibility.
 AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
 
-# Copyright (C) 1999-2017 Free Software Foundation, Inc.
+# Copyright (C) 1999-2018 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1121,10 +1104,12 @@ AC_DEFUN([AM_PATH_PYTHON],
  [
   dnl Find a Python interpreter.  Python versions prior to 2.0 are not
   dnl supported. (2.0 was released on October 16, 2000).
-  dnl FIXME: Remove the need to hard-code Python versions here.
   m4_define_default([_AM_PYTHON_INTERPRETER_LIST],
-[python python2 python3 python3.8 python3.7 python3.6 python3.5 python3.4 python3.3 python3.2 python3.1 python3.0 python2.7 dnl
- python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0])
+[python python2 python3 dnl
+ python3.9 python3.8 python3.7 python3.6 python3.5 python3.4 python3.3 dnl
+ python3.2 python3.1 python3.0 dnl
+ python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 dnl
+ python2.0])
 
   AC_ARG_VAR([PYTHON], [the Python interpreter])
 
@@ -1324,7 +1309,7 @@ for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[[i]]
 sys.exit(sys.hexversion < minverhex)"
   AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])])
 
-# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+# Copyright (C) 2001-2018 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1343,7 +1328,7 @@ AC_DEFUN([AM_RUN_LOG],
 
 # Check to make sure that the build environment is sane.    -*- Autoconf -*-
 
-# Copyright (C) 1996-2017 Free Software Foundation, Inc.
+# Copyright (C) 1996-2018 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1424,7 +1409,7 @@ AC_CONFIG_COMMANDS_PRE(
 rm -f conftest.file
 ])
 
-# Copyright (C) 2009-2017 Free Software Foundation, Inc.
+# Copyright (C) 2009-2018 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1484,7 +1469,7 @@ AC_SUBST([AM_BACKSLASH])dnl
 _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
 ])
 
-# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+# Copyright (C) 2001-2018 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1512,7 +1497,7 @@ fi
 INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
 AC_SUBST([INSTALL_STRIP_PROGRAM])])
 
-# Copyright (C) 2006-2017 Free Software Foundation, Inc.
+# Copyright (C) 2006-2018 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1531,7 +1516,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
 
 # Check how to create a tarball.                            -*- Autoconf -*-
 
-# Copyright (C) 2004-2017 Free Software Foundation, Inc.
+# Copyright (C) 2004-2018 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
diff --git a/compile b/compile
index a85b723..99e5052 100755 (executable)
--- a/compile
+++ b/compile
@@ -1,9 +1,9 @@
 #! /bin/sh
 # Wrapper for compilers which do not understand '-c -o'.
 
-scriptversion=2012-10-14.11; # UTC
+scriptversion=2018-03-07.03; # UTC
 
-# Copyright (C) 1999-2014 Free Software Foundation, Inc.
+# Copyright (C) 1999-2018 Free Software Foundation, Inc.
 # Written by Tom Tromey <tromey@cygnus.com>.
 #
 # This program is free software; you can redistribute it and/or modify
@@ -17,7 +17,7 @@ scriptversion=2012-10-14.11; # UTC
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 # As a special exception to the GNU General Public License, if you
 # distribute this file as part of a program that contains a
@@ -255,7 +255,8 @@ EOF
     echo "compile $scriptversion"
     exit $?
     ;;
-  cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
+  cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \
+  icl | *[/\\]icl | icl.exe | *[/\\]icl.exe )
     func_cl_wrapper "$@"      # Doesn't return...
     ;;
 esac
@@ -339,9 +340,9 @@ exit $ret
 # Local Variables:
 # mode: shell-script
 # sh-indentation: 2
-# eval: (add-hook 'write-file-hooks 'time-stamp)
+# eval: (add-hook 'before-save-hook 'time-stamp)
 # time-stamp-start: "scriptversion="
 # time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-time-zone: "UTC"
+# time-stamp-time-zone: "UTC0"
 # time-stamp-end: "; # UTC"
 # End:
index 31e01ef..f50dcdb 100755 (executable)
@@ -1,8 +1,8 @@
 #! /bin/sh
 # Attempt to guess a canonical system name.
-#   Copyright 1992-2017 Free Software Foundation, Inc.
+#   Copyright 1992-2018 Free Software Foundation, Inc.
 
-timestamp='2017-11-07'
+timestamp='2018-02-24'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -50,7 +50,7 @@ version="\
 GNU config.guess ($timestamp)
 
 Originally written by Per Bothner.
-Copyright 1992-2017 Free Software Foundation, Inc.
+Copyright 1992-2018 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -107,9 +107,9 @@ trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
 dummy=$tmp/dummy ;
 tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
 case $CC_FOR_BUILD,$HOST_CC,$CC in
- ,,)    echo "int x;" > $dummy.c ;
+ ,,)    echo "int x;" > "$dummy.c" ;
        for c in cc gcc c89 c99 ; do
-         if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+         if ($c -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
             CC_FOR_BUILD="$c"; break ;
          fi ;
        done ;
@@ -132,14 +132,14 @@ UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
 UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
 UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
 
-case "${UNAME_SYSTEM}" in
+case "$UNAME_SYSTEM" in
 Linux|GNU|GNU/*)
        # If the system lacks a compiler, then just pick glibc.
        # We could probably try harder.
        LIBC=gnu
 
-       eval $set_cc_for_build
-       cat <<-EOF > $dummy.c
+       eval "$set_cc_for_build"
+       cat <<-EOF > "$dummy.c"
        #include <features.h>
        #if defined(__UCLIBC__)
        LIBC=uclibc
@@ -149,13 +149,20 @@ Linux|GNU|GNU/*)
        LIBC=gnu
        #endif
        EOF
-       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
+       eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`"
+
+       # If ldd exists, use it to detect musl libc.
+       if command -v ldd >/dev/null && \
+               ldd --version 2>&1 | grep -q ^musl
+       then
+           LIBC=musl
+       fi
        ;;
 esac
 
 # Note: order is significant - the case branches are not exclusive.
 
-case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
     *:NetBSD:*:*)
        # NetBSD (nbsd) targets should (where applicable) match one or
        # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
@@ -169,30 +176,30 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
        # portion of the name.  We always set it to "unknown".
        sysctl="sysctl -n hw.machine_arch"
        UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
-           /sbin/$sysctl 2>/dev/null || \
-           /usr/sbin/$sysctl 2>/dev/null || \
+           "/sbin/$sysctl" 2>/dev/null || \
+           "/usr/sbin/$sysctl" 2>/dev/null || \
            echo unknown)`
-       case "${UNAME_MACHINE_ARCH}" in
+       case "$UNAME_MACHINE_ARCH" in
            armeb) machine=armeb-unknown ;;
            arm*) machine=arm-unknown ;;
            sh3el) machine=shl-unknown ;;
            sh3eb) machine=sh-unknown ;;
            sh5el) machine=sh5le-unknown ;;
            earmv*)
-               arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
-               endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'`
-               machine=${arch}${endian}-unknown
+               arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
+               endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
+               machine="${arch}${endian}"-unknown
                ;;
-           *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+           *) machine="$UNAME_MACHINE_ARCH"-unknown ;;
        esac
        # The Operating System including object format, if it has switched
        # to ELF recently (or will in the future) and ABI.
-       case "${UNAME_MACHINE_ARCH}" in
+       case "$UNAME_MACHINE_ARCH" in
            earm*)
                os=netbsdelf
                ;;
            arm*|i386|m68k|ns32k|sh3*|sparc|vax)
-               eval $set_cc_for_build
+               eval "$set_cc_for_build"
                if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
                        | grep -q __ELF__
                then
@@ -208,10 +215,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
                ;;
        esac
        # Determine ABI tags.
-       case "${UNAME_MACHINE_ARCH}" in
+       case "$UNAME_MACHINE_ARCH" in
            earm*)
                expr='s/^earmv[0-9]/-eabi/;s/eb$//'
-               abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"`
+               abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
                ;;
        esac
        # The OS release
@@ -219,52 +226,55 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
        # thus, need a distinct triplet. However, they do not need
        # kernel version information, so it can be replaced with a
        # suitable tag, in the style of linux-gnu.
-       case "${UNAME_VERSION}" in
+       case "$UNAME_VERSION" in
            Debian*)
                release='-gnu'
                ;;
            *)
-               release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2`
+               release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
                ;;
        esac
        # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
        # contains redundant information, the shorter form:
        # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
-       echo "${machine}-${os}${release}${abi}"
+       echo "$machine-${os}${release}${abi}"
        exit ;;
     *:Bitrig:*:*)
        UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
-       echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
+       echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE"
        exit ;;
     *:OpenBSD:*:*)
        UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
-       echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+       echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE"
        exit ;;
     *:LibertyBSD:*:*)
        UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
-       echo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE}
+       echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE"
        exit ;;
     *:MidnightBSD:*:*)
-       echo ${UNAME_MACHINE}-unknown-midnightbsd${UNAME_RELEASE}
+       echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE"
        exit ;;
     *:ekkoBSD:*:*)
-       echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+       echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE"
        exit ;;
     *:SolidBSD:*:*)
-       echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
+       echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE"
        exit ;;
     macppc:MirBSD:*:*)
-       echo powerpc-unknown-mirbsd${UNAME_RELEASE}
+       echo powerpc-unknown-mirbsd"$UNAME_RELEASE"
        exit ;;
     *:MirBSD:*:*)
-       echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+       echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE"
        exit ;;
     *:Sortix:*:*)
-       echo ${UNAME_MACHINE}-unknown-sortix
+       echo "$UNAME_MACHINE"-unknown-sortix
        exit ;;
     *:Redox:*:*)
-       echo ${UNAME_MACHINE}-unknown-redox
+       echo "$UNAME_MACHINE"-unknown-redox
        exit ;;
+    mips:OSF1:*.*)
+        echo mips-dec-osf1
+        exit ;;
     alpha:OSF1:*:*)
        case $UNAME_RELEASE in
        *4.0)
@@ -316,7 +326,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
        # A Tn.n version is a released field test version.
        # A Xn.n version is an unreleased experimental baselevel.
        # 1.2 uses "1.2" for uname -r.
-       echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+       echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`"
        # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
        exitcode=$?
        trap '' 0
@@ -325,10 +335,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
        echo m68k-unknown-sysv4
        exit ;;
     *:[Aa]miga[Oo][Ss]:*:*)
-       echo ${UNAME_MACHINE}-unknown-amigaos
+       echo "$UNAME_MACHINE"-unknown-amigaos
        exit ;;
     *:[Mm]orph[Oo][Ss]:*:*)
-       echo ${UNAME_MACHINE}-unknown-morphos
+       echo "$UNAME_MACHINE"-unknown-morphos
        exit ;;
     *:OS/390:*:*)
        echo i370-ibm-openedition
@@ -340,7 +350,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
        echo powerpc-ibm-os400
        exit ;;
     arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
-       echo arm-acorn-riscix${UNAME_RELEASE}
+       echo arm-acorn-riscix"$UNAME_RELEASE"
        exit ;;
     arm*:riscos:*:*|arm*:RISCOS:*:*)
        echo arm-unknown-riscos
@@ -367,19 +377,19 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
            sparc) echo sparc-icl-nx7; exit ;;
        esac ;;
     s390x:SunOS:*:*)
-       echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
        exit ;;
     sun4H:SunOS:5.*:*)
-       echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
        exit ;;
     sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
-       echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
        exit ;;
     i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
-       echo i386-pc-auroraux${UNAME_RELEASE}
+       echo i386-pc-auroraux"$UNAME_RELEASE"
        exit ;;
     i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
-       eval $set_cc_for_build
+       eval "$set_cc_for_build"
        SUN_ARCH=i386
        # If there is a compiler, see if it is configured for 64-bit objects.
        # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
@@ -392,13 +402,13 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
                SUN_ARCH=x86_64
            fi
        fi
-       echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
        exit ;;
     sun4*:SunOS:6*:*)
        # According to config.sub, this is the proper way to canonicalize
        # SunOS6.  Hard to guess exactly what SunOS6 will be like, but
        # it's likely to be more like Solaris than SunOS4.
-       echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
        exit ;;
     sun4*:SunOS:*:*)
        case "`/usr/bin/arch -k`" in
@@ -407,25 +417,25 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
                ;;
        esac
        # Japanese Language versions have a version number like `4.1.3-JL'.
-       echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+       echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`"
        exit ;;
     sun3*:SunOS:*:*)
-       echo m68k-sun-sunos${UNAME_RELEASE}
+       echo m68k-sun-sunos"$UNAME_RELEASE"
        exit ;;
     sun*:*:4.2BSD:*)
        UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
-       test "x${UNAME_RELEASE}" = x && UNAME_RELEASE=3
+       test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
        case "`/bin/arch`" in
            sun3)
-               echo m68k-sun-sunos${UNAME_RELEASE}
+               echo m68k-sun-sunos"$UNAME_RELEASE"
                ;;
            sun4)
-               echo sparc-sun-sunos${UNAME_RELEASE}
+               echo sparc-sun-sunos"$UNAME_RELEASE"
                ;;
        esac
        exit ;;
     aushp:SunOS:*:*)
-       echo sparc-auspex-sunos${UNAME_RELEASE}
+       echo sparc-auspex-sunos"$UNAME_RELEASE"
        exit ;;
     # The situation for MiNT is a little confusing.  The machine name
     # can be virtually everything (everything which is not
@@ -436,44 +446,44 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
     # MiNT.  But MiNT is downward compatible to TOS, so this should
     # be no problem.
     atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
-       echo m68k-atari-mint${UNAME_RELEASE}
+       echo m68k-atari-mint"$UNAME_RELEASE"
        exit ;;
     atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
-       echo m68k-atari-mint${UNAME_RELEASE}
+       echo m68k-atari-mint"$UNAME_RELEASE"
        exit ;;
     *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
-       echo m68k-atari-mint${UNAME_RELEASE}
+       echo m68k-atari-mint"$UNAME_RELEASE"
        exit ;;
     milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
-       echo m68k-milan-mint${UNAME_RELEASE}
+       echo m68k-milan-mint"$UNAME_RELEASE"
        exit ;;
     hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
-       echo m68k-hades-mint${UNAME_RELEASE}
+       echo m68k-hades-mint"$UNAME_RELEASE"
        exit ;;
     *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
-       echo m68k-unknown-mint${UNAME_RELEASE}
+       echo m68k-unknown-mint"$UNAME_RELEASE"
        exit ;;
     m68k:machten:*:*)
-       echo m68k-apple-machten${UNAME_RELEASE}
+       echo m68k-apple-machten"$UNAME_RELEASE"
        exit ;;
     powerpc:machten:*:*)
-       echo powerpc-apple-machten${UNAME_RELEASE}
+       echo powerpc-apple-machten"$UNAME_RELEASE"
        exit ;;
     RISC*:Mach:*:*)
        echo mips-dec-mach_bsd4.3
        exit ;;
     RISC*:ULTRIX:*:*)
-       echo mips-dec-ultrix${UNAME_RELEASE}
+       echo mips-dec-ultrix"$UNAME_RELEASE"
        exit ;;
     VAX*:ULTRIX*:*:*)
-       echo vax-dec-ultrix${UNAME_RELEASE}
+       echo vax-dec-ultrix"$UNAME_RELEASE"
        exit ;;
     2020:CLIX:*:* | 2430:CLIX:*:*)
-       echo clipper-intergraph-clix${UNAME_RELEASE}
+       echo clipper-intergraph-clix"$UNAME_RELEASE"
        exit ;;
     mips:*:*:UMIPS | mips:*:*:RISCos)
-       eval $set_cc_for_build
-       sed 's/^        //' << EOF >$dummy.c
+       eval "$set_cc_for_build"
+       sed 's/^        //' << EOF > "$dummy.c"
 #ifdef __cplusplus
 #include <stdio.h>  /* for printf() prototype */
        int main (int argc, char *argv[]) {
@@ -494,11 +504,11 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
          exit (-1);
        }
 EOF
-       $CC_FOR_BUILD -o $dummy $dummy.c &&
-         dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
-         SYSTEM_NAME=`$dummy $dummyarg` &&
+       $CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
+         dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+         SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
            { echo "$SYSTEM_NAME"; exit; }
-       echo mips-mips-riscos${UNAME_RELEASE}
+       echo mips-mips-riscos"$UNAME_RELEASE"
        exit ;;
     Motorola:PowerMAX_OS:*:*)
        echo powerpc-motorola-powermax
@@ -524,17 +534,17 @@ EOF
     AViiON:dgux:*:*)
        # DG/UX returns AViiON for all architectures
        UNAME_PROCESSOR=`/usr/bin/uname -p`
-       if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+       if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ]
        then
-           if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
-              [ ${TARGET_BINARY_INTERFACE}x = x ]
+           if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \
+              [ "$TARGET_BINARY_INTERFACE"x = x ]
            then
-               echo m88k-dg-dgux${UNAME_RELEASE}
+               echo m88k-dg-dgux"$UNAME_RELEASE"
            else
-               echo m88k-dg-dguxbcs${UNAME_RELEASE}
+               echo m88k-dg-dguxbcs"$UNAME_RELEASE"
            fi
        else
-           echo i586-dg-dgux${UNAME_RELEASE}
+           echo i586-dg-dgux"$UNAME_RELEASE"
        fi
        exit ;;
     M88*:DolphinOS:*:*)        # DolphinOS (SVR3)
@@ -551,7 +561,7 @@ EOF
        echo m68k-tektronix-bsd
        exit ;;
     *:IRIX*:*:*)
-       echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+       echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`"
        exit ;;
     ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
        echo romp-ibm-aix     # uname -m gives an 8 hex-code CPU id
@@ -563,14 +573,14 @@ EOF
        if [ -x /usr/bin/oslevel ] ; then
                IBM_REV=`/usr/bin/oslevel`
        else
-               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+               IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
        fi
-       echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+       echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV"
        exit ;;
     *:AIX:2:3)
        if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
-               eval $set_cc_for_build
-               sed 's/^                //' << EOF >$dummy.c
+               eval "$set_cc_for_build"
+               sed 's/^                //' << EOF > "$dummy.c"
                #include <sys/systemcfg.h>
 
                main()
@@ -581,7 +591,7 @@ EOF
                        exit(0);
                        }
 EOF
-               if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+               if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"`
                then
                        echo "$SYSTEM_NAME"
                else
@@ -595,7 +605,7 @@ EOF
        exit ;;
     *:AIX:*:[4567])
        IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
-       if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+       if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
                IBM_ARCH=rs6000
        else
                IBM_ARCH=powerpc
@@ -604,9 +614,9 @@ EOF
                IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
                           awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
        else
-               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+               IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
        fi
-       echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+       echo "$IBM_ARCH"-ibm-aix"$IBM_REV"
        exit ;;
     *:AIX:*:*)
        echo rs6000-ibm-aix
@@ -615,7 +625,7 @@ EOF
        echo romp-ibm-bsd4.4
        exit ;;
     ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
-       echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+       echo romp-ibm-bsd"$UNAME_RELEASE"   # 4.3 with uname added to
        exit ;;                             # report: romp-ibm BSD 4.3
     *:BOSX:*:*)
        echo rs6000-bull-bosx
@@ -630,28 +640,28 @@ EOF
        echo m68k-hp-bsd4.4
        exit ;;
     9000/[34678]??:HP-UX:*:*)
-       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
-       case "${UNAME_MACHINE}" in
+       HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
+       case "$UNAME_MACHINE" in
            9000/31?)            HP_ARCH=m68000 ;;
            9000/[34]??)         HP_ARCH=m68k ;;
            9000/[678][0-9][0-9])
                if [ -x /usr/bin/getconf ]; then
                    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
-                   case "${sc_cpu_version}" in
+                   case "$sc_cpu_version" in
                      523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
                      528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
                      532)                      # CPU_PA_RISC2_0
-                       case "${sc_kernel_bits}" in
+                       case "$sc_kernel_bits" in
                          32) HP_ARCH=hppa2.0n ;;
                          64) HP_ARCH=hppa2.0w ;;
                          '') HP_ARCH=hppa2.0 ;;   # HP-UX 10.20
                        esac ;;
                    esac
                fi
-               if [ "${HP_ARCH}" = "" ]; then
-                   eval $set_cc_for_build
-                   sed 's/^            //' << EOF >$dummy.c
+               if [ "$HP_ARCH" = "" ]; then
+                   eval "$set_cc_for_build"
+                   sed 's/^            //' << EOF > "$dummy.c"
 
                #define _HPUX_SOURCE
                #include <stdlib.h>
@@ -684,13 +694,13 @@ EOF
                    exit (0);
                }
 EOF
-                   (CCOPTS="" $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+                   (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
                    test -z "$HP_ARCH" && HP_ARCH=hppa
                fi ;;
        esac
-       if [ ${HP_ARCH} = hppa2.0w ]
+       if [ "$HP_ARCH" = hppa2.0w ]
        then
-           eval $set_cc_for_build
+           eval "$set_cc_for_build"
 
            # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
            # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
@@ -709,15 +719,15 @@ EOF
                HP_ARCH=hppa64
            fi
        fi
-       echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+       echo "$HP_ARCH"-hp-hpux"$HPUX_REV"
        exit ;;
     ia64:HP-UX:*:*)
-       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
-       echo ia64-hp-hpux${HPUX_REV}
+       HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
+       echo ia64-hp-hpux"$HPUX_REV"
        exit ;;
     3050*:HI-UX:*:*)
-       eval $set_cc_for_build
-       sed 's/^        //' << EOF >$dummy.c
+       eval "$set_cc_for_build"
+       sed 's/^        //' << EOF > "$dummy.c"
        #include <unistd.h>
        int
        main ()
@@ -742,7 +752,7 @@ EOF
          exit (0);
        }
 EOF
-       $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+       $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
                { echo "$SYSTEM_NAME"; exit; }
        echo unknown-hitachi-hiuxwe2
        exit ;;
@@ -763,9 +773,9 @@ EOF
        exit ;;
     i*86:OSF1:*:*)
        if [ -x /usr/sbin/sysversion ] ; then
-           echo ${UNAME_MACHINE}-unknown-osf1mk
+           echo "$UNAME_MACHINE"-unknown-osf1mk
        else
-           echo ${UNAME_MACHINE}-unknown-osf1
+           echo "$UNAME_MACHINE"-unknown-osf1
        fi
        exit ;;
     parisc*:Lites*:*:*)
@@ -790,109 +800,109 @@ EOF
        echo c4-convex-bsd
        exit ;;
     CRAY*Y-MP:*:*:*)
-       echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
        exit ;;
     CRAY*[A-Z]90:*:*:*)
-       echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+       echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \
        | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
              -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
              -e 's/\.[^.]*$/.X/'
        exit ;;
     CRAY*TS:*:*:*)
-       echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
        exit ;;
     CRAY*T3E:*:*:*)
-       echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
        exit ;;
     CRAY*SV1:*:*:*)
-       echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
        exit ;;
     *:UNICOS/mp:*:*)
-       echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
        exit ;;
     F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
        FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
        FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
-       FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+       FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'`
        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
        exit ;;
     5000:UNIX_System_V:4.*:*)
        FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
-       FUJITSU_REL=`echo ${UNAME_RELEASE} | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
+       FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
        echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
        exit ;;
     i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
-       echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+       echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE"
        exit ;;
     sparc*:BSD/OS:*:*)
-       echo sparc-unknown-bsdi${UNAME_RELEASE}
+       echo sparc-unknown-bsdi"$UNAME_RELEASE"
        exit ;;
     *:BSD/OS:*:*)
-       echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+       echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE"
        exit ;;
     *:FreeBSD:*:*)
        UNAME_PROCESSOR=`/usr/bin/uname -p`
-       case ${UNAME_PROCESSOR} in
+       case "$UNAME_PROCESSOR" in
            amd64)
                UNAME_PROCESSOR=x86_64 ;;
            i386)
                UNAME_PROCESSOR=i586 ;;
        esac
-       echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+       echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
        exit ;;
     i*:CYGWIN*:*)
-       echo ${UNAME_MACHINE}-pc-cygwin
+       echo "$UNAME_MACHINE"-pc-cygwin
        exit ;;
     *:MINGW64*:*)
-       echo ${UNAME_MACHINE}-pc-mingw64
+       echo "$UNAME_MACHINE"-pc-mingw64
        exit ;;
     *:MINGW*:*)
-       echo ${UNAME_MACHINE}-pc-mingw32
+       echo "$UNAME_MACHINE"-pc-mingw32
        exit ;;
     *:MSYS*:*)
-       echo ${UNAME_MACHINE}-pc-msys
+       echo "$UNAME_MACHINE"-pc-msys
        exit ;;
     i*:PW*:*)
-       echo ${UNAME_MACHINE}-pc-pw32
+       echo "$UNAME_MACHINE"-pc-pw32
        exit ;;
     *:Interix*:*)
-       case ${UNAME_MACHINE} in
+       case "$UNAME_MACHINE" in
            x86)
-               echo i586-pc-interix${UNAME_RELEASE}
+               echo i586-pc-interix"$UNAME_RELEASE"
                exit ;;
            authenticamd | genuineintel | EM64T)
-               echo x86_64-unknown-interix${UNAME_RELEASE}
+               echo x86_64-unknown-interix"$UNAME_RELEASE"
                exit ;;
            IA64)
-               echo ia64-unknown-interix${UNAME_RELEASE}
+               echo ia64-unknown-interix"$UNAME_RELEASE"
                exit ;;
        esac ;;
     i*:UWIN*:*)
-       echo ${UNAME_MACHINE}-pc-uwin
+       echo "$UNAME_MACHINE"-pc-uwin
        exit ;;
     amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
        echo x86_64-unknown-cygwin
        exit ;;
     prep*:SunOS:5.*:*)
-       echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
        exit ;;
     *:GNU:*:*)
        # the GNU system
-       echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+       echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`"
        exit ;;
     *:GNU/*:*:*)
        # other systems with GNU libc and userland
-       echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC}
+       echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC"
        exit ;;
     i*86:Minix:*:*)
-       echo ${UNAME_MACHINE}-pc-minix
+       echo "$UNAME_MACHINE"-pc-minix
        exit ;;
     aarch64:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     aarch64_be:Linux:*:*)
        UNAME_MACHINE=aarch64_be
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     alpha:Linux:*:*)
        case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
@@ -906,63 +916,63 @@ EOF
        esac
        objdump --private-headers /bin/sh | grep -q ld.so.1
        if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     arc:Linux:*:* | arceb:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     arm*:Linux:*:*)
-       eval $set_cc_for_build
+       eval "$set_cc_for_build"
        if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
            | grep -q __ARM_EABI__
        then
-           echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+           echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        else
            if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
                | grep -q __ARM_PCS_VFP
            then
-               echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi
+               echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi
            else
-               echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf
+               echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf
            fi
        fi
        exit ;;
     avr32*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     cris:Linux:*:*)
-       echo ${UNAME_MACHINE}-axis-linux-${LIBC}
+       echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
        exit ;;
     crisv32:Linux:*:*)
-       echo ${UNAME_MACHINE}-axis-linux-${LIBC}
+       echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
        exit ;;
     e2k:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     frv:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     hexagon:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     i*86:Linux:*:*)
-       echo ${UNAME_MACHINE}-pc-linux-${LIBC}
+       echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
        exit ;;
     ia64:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     k1om:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     m32r*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     m68*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     mips:Linux:*:* | mips64:Linux:*:*)
-       eval $set_cc_for_build
-       sed 's/^        //' << EOF >$dummy.c
+       eval "$set_cc_for_build"
+       sed 's/^        //' << EOF > "$dummy.c"
        #undef CPU
        #undef ${UNAME_MACHINE}
        #undef ${UNAME_MACHINE}el
@@ -976,70 +986,74 @@ EOF
        #endif
        #endif
 EOF
-       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
-       test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
+       eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU'`"
+       test "x$CPU" != x && { echo "$CPU-unknown-linux-$LIBC"; exit; }
        ;;
     mips64el:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     openrisc*:Linux:*:*)
-       echo or1k-unknown-linux-${LIBC}
+       echo or1k-unknown-linux-"$LIBC"
        exit ;;
     or32:Linux:*:* | or1k*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     padre:Linux:*:*)
-       echo sparc-unknown-linux-${LIBC}
+       echo sparc-unknown-linux-"$LIBC"
        exit ;;
     parisc64:Linux:*:* | hppa64:Linux:*:*)
-       echo hppa64-unknown-linux-${LIBC}
+       echo hppa64-unknown-linux-"$LIBC"
        exit ;;
     parisc:Linux:*:* | hppa:Linux:*:*)
        # Look for CPU level
        case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
-         PA7*) echo hppa1.1-unknown-linux-${LIBC} ;;
-         PA8*) echo hppa2.0-unknown-linux-${LIBC} ;;
-         *)    echo hppa-unknown-linux-${LIBC} ;;
+         PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;;
+         PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;;
+         *)    echo hppa-unknown-linux-"$LIBC" ;;
        esac
        exit ;;
     ppc64:Linux:*:*)
-       echo powerpc64-unknown-linux-${LIBC}
+       echo powerpc64-unknown-linux-"$LIBC"
        exit ;;
     ppc:Linux:*:*)
-       echo powerpc-unknown-linux-${LIBC}
+       echo powerpc-unknown-linux-"$LIBC"
        exit ;;
     ppc64le:Linux:*:*)
-       echo powerpc64le-unknown-linux-${LIBC}
+       echo powerpc64le-unknown-linux-"$LIBC"
        exit ;;
     ppcle:Linux:*:*)
-       echo powerpcle-unknown-linux-${LIBC}
+       echo powerpcle-unknown-linux-"$LIBC"
        exit ;;
     riscv32:Linux:*:* | riscv64:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     s390:Linux:*:* | s390x:Linux:*:*)
-       echo ${UNAME_MACHINE}-ibm-linux-${LIBC}
+       echo "$UNAME_MACHINE"-ibm-linux-"$LIBC"
        exit ;;
     sh64*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     sh*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     sparc:Linux:*:* | sparc64:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     tile*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     vax:Linux:*:*)
-       echo ${UNAME_MACHINE}-dec-linux-${LIBC}
+       echo "$UNAME_MACHINE"-dec-linux-"$LIBC"
        exit ;;
     x86_64:Linux:*:*)
-       echo ${UNAME_MACHINE}-pc-linux-${LIBC}
+       if objdump -f /bin/sh | grep -q elf32-x86-64; then
+           echo "$UNAME_MACHINE"-pc-linux-"$LIBC"x32
+       else
+           echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
+       fi
        exit ;;
     xtensa*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
        exit ;;
     i*86:DYNIX/ptx:4*:*)
        # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
@@ -1053,34 +1067,34 @@ EOF
        # I am not positive that other SVR4 systems won't match this,
        # I just have to hope.  -- rms.
        # Use sysv4.2uw... so that sysv4* matches it.
-       echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+       echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION"
        exit ;;
     i*86:OS/2:*:*)
        # If we were able to find `uname', then EMX Unix compatibility
        # is probably installed.
-       echo ${UNAME_MACHINE}-pc-os2-emx
+       echo "$UNAME_MACHINE"-pc-os2-emx
        exit ;;
     i*86:XTS-300:*:STOP)
-       echo ${UNAME_MACHINE}-unknown-stop
+       echo "$UNAME_MACHINE"-unknown-stop
        exit ;;
     i*86:atheos:*:*)
-       echo ${UNAME_MACHINE}-unknown-atheos
+       echo "$UNAME_MACHINE"-unknown-atheos
        exit ;;
     i*86:syllable:*:*)
-       echo ${UNAME_MACHINE}-pc-syllable
+       echo "$UNAME_MACHINE"-pc-syllable
        exit ;;
     i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
-       echo i386-unknown-lynxos${UNAME_RELEASE}
+       echo i386-unknown-lynxos"$UNAME_RELEASE"
        exit ;;
     i*86:*DOS:*:*)
-       echo ${UNAME_MACHINE}-pc-msdosdjgpp
+       echo "$UNAME_MACHINE"-pc-msdosdjgpp
        exit ;;
     i*86:*:4.*:*)
-       UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+       UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'`
        if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
-               echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+               echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL"
        else
-               echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+               echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL"
        fi
        exit ;;
     i*86:*:5:[678]*)
@@ -1090,12 +1104,12 @@ EOF
            *Pentium)        UNAME_MACHINE=i586 ;;
            *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
        esac
-       echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+       echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}{$UNAME_VERSION}"
        exit ;;
     i*86:*:3.2:*)
        if test -f /usr/options/cb.name; then
                UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
-               echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+               echo "$UNAME_MACHINE"-pc-isc"$UNAME_REL"
        elif /bin/uname -X 2>/dev/null >/dev/null ; then
                UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
                (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
@@ -1105,9 +1119,9 @@ EOF
                        && UNAME_MACHINE=i686
                (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
                        && UNAME_MACHINE=i686
-               echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+               echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL"
        else
-               echo ${UNAME_MACHINE}-pc-sysv32
+               echo "$UNAME_MACHINE"-pc-sysv32
        fi
        exit ;;
     pc:*:*:*)
@@ -1127,9 +1141,9 @@ EOF
        exit ;;
     i860:*:4.*:*) # i860-SVR4
        if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
-         echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+         echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4
        else # Add other i860-SVR4 vendors below as they are discovered.
-         echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+         echo i860-unknown-sysv"$UNAME_RELEASE"  # Unknown i860-SVR4
        fi
        exit ;;
     mini*:CTIX:SYS*5:*)
@@ -1149,9 +1163,9 @@ EOF
        test -r /etc/.relid \
        && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
-         && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+         && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
        /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
-         && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+         && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
     3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
          && { echo i486-ncr-sysv4; exit; } ;;
@@ -1160,28 +1174,28 @@ EOF
        test -r /etc/.relid \
            && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
-           && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+           && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
        /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
-           && { echo i586-ncr-sysv4.3${OS_REL}; exit; }
+           && { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
        /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
-           && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+           && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
     m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
-       echo m68k-unknown-lynxos${UNAME_RELEASE}
+       echo m68k-unknown-lynxos"$UNAME_RELEASE"
        exit ;;
     mc68030:UNIX_System_V:4.*:*)
        echo m68k-atari-sysv4
        exit ;;
     TSUNAMI:LynxOS:2.*:*)
-       echo sparc-unknown-lynxos${UNAME_RELEASE}
+       echo sparc-unknown-lynxos"$UNAME_RELEASE"
        exit ;;
     rs6000:LynxOS:2.*:*)
-       echo rs6000-unknown-lynxos${UNAME_RELEASE}
+       echo rs6000-unknown-lynxos"$UNAME_RELEASE"
        exit ;;
     PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
-       echo powerpc-unknown-lynxos${UNAME_RELEASE}
+       echo powerpc-unknown-lynxos"$UNAME_RELEASE"
        exit ;;
     SM[BE]S:UNIX_SV:*:*)
-       echo mips-dde-sysv${UNAME_RELEASE}
+       echo mips-dde-sysv"$UNAME_RELEASE"
        exit ;;
     RM*:ReliantUNIX-*:*:*)
        echo mips-sni-sysv4
@@ -1192,7 +1206,7 @@ EOF
     *:SINIX-*:*:*)
        if uname -p 2>/dev/null >/dev/null ; then
                UNAME_MACHINE=`(uname -p) 2>/dev/null`
-               echo ${UNAME_MACHINE}-sni-sysv4
+               echo "$UNAME_MACHINE"-sni-sysv4
        else
                echo ns32k-sni-sysv
        fi
@@ -1212,23 +1226,23 @@ EOF
        exit ;;
     i*86:VOS:*:*)
        # From Paul.Green@stratus.com.
-       echo ${UNAME_MACHINE}-stratus-vos
+       echo "$UNAME_MACHINE"-stratus-vos
        exit ;;
     *:VOS:*:*)
        # From Paul.Green@stratus.com.
        echo hppa1.1-stratus-vos
        exit ;;
     mc68*:A/UX:*:*)
-       echo m68k-apple-aux${UNAME_RELEASE}
+       echo m68k-apple-aux"$UNAME_RELEASE"
        exit ;;
     news*:NEWS-OS:6*:*)
        echo mips-sony-newsos6
        exit ;;
     R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
        if [ -d /usr/nec ]; then
-               echo mips-nec-sysv${UNAME_RELEASE}
+               echo mips-nec-sysv"$UNAME_RELEASE"
        else
-               echo mips-unknown-sysv${UNAME_RELEASE}
+               echo mips-unknown-sysv"$UNAME_RELEASE"
        fi
        exit ;;
     BeBox:BeOS:*:*)    # BeOS running on hardware made by Be, PPC only.
@@ -1247,39 +1261,39 @@ EOF
        echo x86_64-unknown-haiku
        exit ;;
     SX-4:SUPER-UX:*:*)
-       echo sx4-nec-superux${UNAME_RELEASE}
+       echo sx4-nec-superux"$UNAME_RELEASE"
        exit ;;
     SX-5:SUPER-UX:*:*)
-       echo sx5-nec-superux${UNAME_RELEASE}
+       echo sx5-nec-superux"$UNAME_RELEASE"
        exit ;;
     SX-6:SUPER-UX:*:*)
-       echo sx6-nec-superux${UNAME_RELEASE}
+       echo sx6-nec-superux"$UNAME_RELEASE"
        exit ;;
     SX-7:SUPER-UX:*:*)
-       echo sx7-nec-superux${UNAME_RELEASE}
+       echo sx7-nec-superux"$UNAME_RELEASE"
        exit ;;
     SX-8:SUPER-UX:*:*)
-       echo sx8-nec-superux${UNAME_RELEASE}
+       echo sx8-nec-superux"$UNAME_RELEASE"
        exit ;;
     SX-8R:SUPER-UX:*:*)
-       echo sx8r-nec-superux${UNAME_RELEASE}
+       echo sx8r-nec-superux"$UNAME_RELEASE"
        exit ;;
     SX-ACE:SUPER-UX:*:*)
-       echo sxace-nec-superux${UNAME_RELEASE}
+       echo sxace-nec-superux"$UNAME_RELEASE"
        exit ;;
     Power*:Rhapsody:*:*)
-       echo powerpc-apple-rhapsody${UNAME_RELEASE}
+       echo powerpc-apple-rhapsody"$UNAME_RELEASE"
        exit ;;
     *:Rhapsody:*:*)
-       echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+       echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE"
        exit ;;
     *:Darwin:*:*)
        UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
-       eval $set_cc_for_build
+       eval "$set_cc_for_build"
        if test "$UNAME_PROCESSOR" = unknown ; then
            UNAME_PROCESSOR=powerpc
        fi
-       if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then
+       if test "`echo "$UNAME_RELEASE" | sed -e 's/\..*//'`" -le 10 ; then
            if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
                if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
                       (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
@@ -1307,7 +1321,7 @@ EOF
            # that Apple uses in portable devices.
            UNAME_PROCESSOR=x86_64
        fi
-       echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+       echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE"
        exit ;;
     *:procnto*:*:* | *:QNX:[0123456789]*:*)
        UNAME_PROCESSOR=`uname -p`
@@ -1315,22 +1329,25 @@ EOF
                UNAME_PROCESSOR=i386
                UNAME_MACHINE=pc
        fi
-       echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+       echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE"
        exit ;;
     *:QNX:*:4*)
        echo i386-pc-qnx
        exit ;;
     NEO-*:NONSTOP_KERNEL:*:*)
-       echo neo-tandem-nsk${UNAME_RELEASE}
+       echo neo-tandem-nsk"$UNAME_RELEASE"
        exit ;;
     NSE-*:NONSTOP_KERNEL:*:*)
-       echo nse-tandem-nsk${UNAME_RELEASE}
+       echo nse-tandem-nsk"$UNAME_RELEASE"
        exit ;;
     NSR-*:NONSTOP_KERNEL:*:*)
-       echo nsr-tandem-nsk${UNAME_RELEASE}
+       echo nsr-tandem-nsk"$UNAME_RELEASE"
+       exit ;;
+    NSV-*:NONSTOP_KERNEL:*:*)
+       echo nsv-tandem-nsk"$UNAME_RELEASE"
        exit ;;
     NSX-*:NONSTOP_KERNEL:*:*)
-       echo nsx-tandem-nsk${UNAME_RELEASE}
+       echo nsx-tandem-nsk"$UNAME_RELEASE"
        exit ;;
     *:NonStop-UX:*:*)
        echo mips-compaq-nonstopux
@@ -1339,7 +1356,7 @@ EOF
        echo bs2000-siemens-sysv
        exit ;;
     DS/*:UNIX_System_V:*:*)
-       echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+       echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE"
        exit ;;
     *:Plan9:*:*)
        # "uname -m" is not consistent, so use $cputype instead. 386
@@ -1350,7 +1367,7 @@ EOF
        else
            UNAME_MACHINE="$cputype"
        fi
-       echo ${UNAME_MACHINE}-unknown-plan9
+       echo "$UNAME_MACHINE"-unknown-plan9
        exit ;;
     *:TOPS-10:*:*)
        echo pdp10-unknown-tops10
@@ -1371,14 +1388,14 @@ EOF
        echo pdp10-unknown-its
        exit ;;
     SEI:*:*:SEIUX)
-       echo mips-sei-seiux${UNAME_RELEASE}
+       echo mips-sei-seiux"$UNAME_RELEASE"
        exit ;;
     *:DragonFly:*:*)
-       echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+       echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
        exit ;;
     *:*VMS:*:*)
        UNAME_MACHINE=`(uname -p) 2>/dev/null`
-       case "${UNAME_MACHINE}" in
+       case "$UNAME_MACHINE" in
            A*) echo alpha-dec-vms ; exit ;;
            I*) echo ia64-dec-vms ; exit ;;
            V*) echo vax-dec-vms ; exit ;;
@@ -1387,16 +1404,16 @@ EOF
        echo i386-pc-xenix
        exit ;;
     i*86:skyos:*:*)
-       echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE} | sed -e 's/ .*$//'`
+       echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`"
        exit ;;
     i*86:rdos:*:*)
-       echo ${UNAME_MACHINE}-pc-rdos
+       echo "$UNAME_MACHINE"-pc-rdos
        exit ;;
     i*86:AROS:*:*)
-       echo ${UNAME_MACHINE}-pc-aros
+       echo "$UNAME_MACHINE"-pc-aros
        exit ;;
     x86_64:VMkernel:*:*)
-       echo ${UNAME_MACHINE}-unknown-esx
+       echo "$UNAME_MACHINE"-unknown-esx
        exit ;;
     amd64:Isilon\ OneFS:*:*)
        echo x86_64-unknown-onefs
@@ -1405,7 +1422,7 @@ esac
 
 echo "$0: unable to guess system type" >&2
 
-case "${UNAME_MACHINE}:${UNAME_SYSTEM}" in
+case "$UNAME_MACHINE:$UNAME_SYSTEM" in
     mips:Linux | mips64:Linux)
        # If we got here on MIPS GNU/Linux, output extra information.
        cat >&2 <<EOF
@@ -1447,10 +1464,10 @@ hostinfo               = `(hostinfo) 2>/dev/null`
 /usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
 /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
 
-UNAME_MACHINE = ${UNAME_MACHINE}
-UNAME_RELEASE = ${UNAME_RELEASE}
-UNAME_SYSTEM  = ${UNAME_SYSTEM}
-UNAME_VERSION = ${UNAME_VERSION}
+UNAME_MACHINE = "$UNAME_MACHINE"
+UNAME_RELEASE = "$UNAME_RELEASE"
+UNAME_SYSTEM  = "$UNAME_SYSTEM"
+UNAME_VERSION = "$UNAME_VERSION"
 EOF
 
 exit 1
index 00f68b8..1d8e98b 100755 (executable)
@@ -1,8 +1,8 @@
 #! /bin/sh
 # Configuration validation subroutine script.
-#   Copyright 1992-2017 Free Software Foundation, Inc.
+#   Copyright 1992-2018 Free Software Foundation, Inc.
 
-timestamp='2017-11-23'
+timestamp='2018-02-22'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -67,7 +67,7 @@ Report bugs and patches to <config-patches@gnu.org>."
 version="\
 GNU config.sub ($timestamp)
 
-Copyright 1992-2017 Free Software Foundation, Inc.
+Copyright 1992-2018 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -94,7 +94,7 @@ while test $# -gt 0 ; do
 
     *local*)
        # First pass through any local machine types.
-       echo $1
+       echo "$1"
        exit ;;
 
     * )
@@ -112,7 +112,7 @@ esac
 
 # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
 # Here we must recognize all the valid KERNEL-OS combinations.
-maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+maybe_os=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
 case $maybe_os in
   nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
   linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
@@ -120,16 +120,16 @@ case $maybe_os in
   kopensolaris*-gnu* | cloudabi*-eabi* | \
   storm-chaos* | os2-emx* | rtmk-nova*)
     os=-$maybe_os
-    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
     ;;
   android-linux)
     os=-linux-android
-    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
+    basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
     ;;
   *)
-    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
-    if [ $basic_machine != $1 ]
-    then os=`echo $1 | sed 's/.*-/-/'`
+    basic_machine=`echo "$1" | sed 's/-[^-]*$//'`
+    if [ "$basic_machine" != "$1" ]
+    then os=`echo "$1" | sed 's/.*-/-/'`
     else os=; fi
     ;;
 esac
@@ -178,44 +178,44 @@ case $os in
                ;;
        -sco6)
                os=-sco5v6
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
                ;;
        -sco5)
                os=-sco3.2v5
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
                ;;
        -sco4)
                os=-sco3.2v4
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
                ;;
        -sco3.2.[4-9]*)
                os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
                ;;
        -sco3.2v[4-9]*)
                # Don't forget version if it is 3.2v4 or newer.
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
                ;;
        -sco5v6*)
                # Don't forget version if it is 3.2v4 or newer.
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
                ;;
        -sco*)
                os=-sco3.2v2
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
                ;;
        -udk*)
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
                ;;
        -isc)
                os=-isc2.2
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
                ;;
        -clix*)
                basic_machine=clipper-intergraph
                ;;
        -isc*)
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
                ;;
        -lynx*178)
                os=-lynxos178
@@ -227,7 +227,7 @@ case $os in
                os=-lynxos
                ;;
        -ptx*)
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+               basic_machine=`echo "$1" | sed -e 's/86-.*/86-sequent/'`
                ;;
        -psos*)
                os=-psos
@@ -296,7 +296,7 @@ case $basic_machine in
        | nios | nios2 | nios2eb | nios2el \
        | ns16k | ns32k \
        | open8 | or1k | or1knd | or32 \
-       | pdp10 | pdp11 | pj | pjl \
+       | pdp10 | pj | pjl \
        | powerpc | powerpc64 | powerpc64le | powerpcle \
        | pru \
        | pyramid \
@@ -333,7 +333,7 @@ case $basic_machine in
                basic_machine=$basic_machine-unknown
                os=-none
                ;;
-       m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+       m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65)
                ;;
        ms1)
                basic_machine=mt-unknown
@@ -362,7 +362,7 @@ case $basic_machine in
          ;;
        # Object if more than one company name word.
        *-*-*)
-               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2
                exit 1
                ;;
        # Recognize the basic CPU types with company name.
@@ -457,7 +457,7 @@ case $basic_machine in
        # Recognize the various machine names and aliases which stand
        # for a CPU type and a company and sometimes even an OS.
        386bsd)
-               basic_machine=i386-unknown
+               basic_machine=i386-pc
                os=-bsd
                ;;
        3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
@@ -491,7 +491,7 @@ case $basic_machine in
                basic_machine=x86_64-pc
                ;;
        amd64-*)
-               basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+               basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'`
                ;;
        amdahl)
                basic_machine=580-amdahl
@@ -536,7 +536,7 @@ case $basic_machine in
                os=-linux
                ;;
        blackfin-*)
-               basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
+               basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'`
                os=-linux
                ;;
        bluegene*)
@@ -544,13 +544,13 @@ case $basic_machine in
                os=-cnk
                ;;
        c54x-*)
-               basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
+               basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
                ;;
        c55x-*)
-               basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
+               basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
                ;;
        c6x-*)
-               basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
+               basic_machine=tic6x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
                ;;
        c90)
                basic_machine=c90-cray
@@ -648,7 +648,7 @@ case $basic_machine in
                os=$os"spe"
                ;;
        e500v[12]-*)
-               basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+               basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'`
                os=$os"spe"
                ;;
        ebmon29k)
@@ -740,9 +740,6 @@ case $basic_machine in
        hp9k8[0-9][0-9] | hp8[0-9][0-9])
                basic_machine=hppa1.0-hp
                ;;
-       hppa-next)
-               os=-nextstep3
-               ;;
        hppaosf)
                basic_machine=hppa1.1-hp
                os=-osf
@@ -755,26 +752,26 @@ case $basic_machine in
                basic_machine=i370-ibm
                ;;
        i*86v32)
-               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
                os=-sysv32
                ;;
        i*86v4*)
-               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
                os=-sysv4
                ;;
        i*86v)
-               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
                os=-sysv
                ;;
        i*86sol2)
-               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
                os=-solaris2
                ;;
        i386mach)
                basic_machine=i386-mach
                os=-mach
                ;;
-       i386-vsta | vsta)
+       vsta)
                basic_machine=i386-unknown
                os=-vsta
                ;;
@@ -793,19 +790,16 @@ case $basic_machine in
                os=-sysv
                ;;
        leon-*|leon[3-9]-*)
-               basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`
+               basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'`
                ;;
        m68knommu)
                basic_machine=m68k-unknown
                os=-linux
                ;;
        m68knommu-*)
-               basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
+               basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'`
                os=-linux
                ;;
-       m88k-omron*)
-               basic_machine=m88k-omron
-               ;;
        magnum | m3230)
                basic_machine=mips-mips
                os=-sysv
@@ -837,10 +831,10 @@ case $basic_machine in
                os=-mint
                ;;
        mips3*-*)
-               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+               basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`
                ;;
        mips3*)
-               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+               basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`-unknown
                ;;
        monitor)
                basic_machine=m68k-rom68k
@@ -859,7 +853,7 @@ case $basic_machine in
                os=-msdos
                ;;
        ms1-*)
-               basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
+               basic_machine=`echo "$basic_machine" | sed -e 's/ms1-/mt-/'`
                ;;
        msys)
                basic_machine=i686-pc
@@ -946,6 +940,9 @@ case $basic_machine in
        nsr-tandem)
                basic_machine=nsr-tandem
                ;;
+       nsv-tandem)
+               basic_machine=nsv-tandem
+               ;;
        nsx-tandem)
                basic_machine=nsx-tandem
                ;;
@@ -981,7 +978,7 @@ case $basic_machine in
                os=-linux
                ;;
        parisc-*)
-               basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
+               basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'`
                os=-linux
                ;;
        pbd)
@@ -997,7 +994,7 @@ case $basic_machine in
                basic_machine=i386-pc
                ;;
        pc98-*)
-               basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
+               basic_machine=i386-`echo "$basic_machine" | sed 's/^[^-]*-//'`
                ;;
        pentium | p5 | k5 | k6 | nexgen | viac3)
                basic_machine=i586-pc
@@ -1012,16 +1009,16 @@ case $basic_machine in
                basic_machine=i786-pc
                ;;
        pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
-               basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+               basic_machine=i586-`echo "$basic_machine" | sed 's/^[^-]*-//'`
                ;;
        pentiumpro-* | p6-* | 6x86-* | athlon-*)
-               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'`
                ;;
        pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
-               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'`
                ;;
        pentium4-*)
-               basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+               basic_machine=i786-`echo "$basic_machine" | sed 's/^[^-]*-//'`
                ;;
        pn)
                basic_machine=pn-gould
@@ -1031,23 +1028,23 @@ case $basic_machine in
        ppc | ppcbe)    basic_machine=powerpc-unknown
                ;;
        ppc-* | ppcbe-*)
-               basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+               basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'`
                ;;
        ppcle | powerpclittle)
                basic_machine=powerpcle-unknown
                ;;
        ppcle-* | powerpclittle-*)
-               basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+               basic_machine=powerpcle-`echo "$basic_machine" | sed 's/^[^-]*-//'`
                ;;
        ppc64)  basic_machine=powerpc64-unknown
                ;;
-       ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+       ppc64-*) basic_machine=powerpc64-`echo "$basic_machine" | sed 's/^[^-]*-//'`
                ;;
        ppc64le | powerpc64little)
                basic_machine=powerpc64le-unknown
                ;;
        ppc64le-* | powerpc64little-*)
-               basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+               basic_machine=powerpc64le-`echo "$basic_machine" | sed 's/^[^-]*-//'`
                ;;
        ps2)
                basic_machine=i386-ibm
@@ -1101,17 +1098,10 @@ case $basic_machine in
        sequent)
                basic_machine=i386-sequent
                ;;
-       sh)
-               basic_machine=sh-hitachi
-               os=-hms
-               ;;
        sh5el)
                basic_machine=sh5le-unknown
                ;;
-       sh64)
-               basic_machine=sh64-unknown
-               ;;
-       sparclite-wrs | simso-wrs)
+       simso-wrs)
                basic_machine=sparclite-wrs
                os=-vxworks
                ;;
@@ -1130,7 +1120,7 @@ case $basic_machine in
                os=-sysv4
                ;;
        strongarm-* | thumb-*)
-               basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
+               basic_machine=arm-`echo "$basic_machine" | sed 's/^[^-]*-//'`
                ;;
        sun2)
                basic_machine=m68000-sun
@@ -1244,9 +1234,6 @@ case $basic_machine in
                basic_machine=a29k-wrs
                os=-vxworks
                ;;
-       wasm32)
-               basic_machine=wasm32-unknown
-               ;;
        w65*)
                basic_machine=w65-wdc
                os=-none
@@ -1266,20 +1253,12 @@ case $basic_machine in
                basic_machine=xps100-honeywell
                ;;
        xscale-* | xscalee[bl]-*)
-               basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
+               basic_machine=`echo "$basic_machine" | sed 's/^xscale/arm/'`
                ;;
        ymp)
                basic_machine=ymp-cray
                os=-unicos
                ;;
-       z8k-*-coff)
-               basic_machine=z8k-unknown
-               os=-sim
-               ;;
-       z80-*-coff)
-               basic_machine=z80-unknown
-               os=-sim
-               ;;
        none)
                basic_machine=none-none
                os=-none
@@ -1308,10 +1287,6 @@ case $basic_machine in
        vax)
                basic_machine=vax-dec
                ;;
-       pdp10)
-               # there are many clones, so DEC is not a safe bet
-               basic_machine=pdp10-unknown
-               ;;
        pdp11)
                basic_machine=pdp11-dec
                ;;
@@ -1321,9 +1296,6 @@ case $basic_machine in
        sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
                basic_machine=sh-unknown
                ;;
-       sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
-               basic_machine=sparc-sun
-               ;;
        cydra)
                basic_machine=cydra-cydrome
                ;;
@@ -1343,7 +1315,7 @@ case $basic_machine in
                # Make sure to match an already-canonicalized machine name.
                ;;
        *)
-               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2
                exit 1
                ;;
 esac
@@ -1351,10 +1323,10 @@ esac
 # Here we canonicalize certain aliases for manufacturers.
 case $basic_machine in
        *-digital*)
-               basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+               basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'`
                ;;
        *-commodore*)
-               basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+               basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'`
                ;;
        *)
                ;;
@@ -1377,15 +1349,16 @@ case $os in
        -solaris)
                os=-solaris2
                ;;
-       -svr4*)
-               os=-sysv4
-               ;;
        -unixware*)
                os=-sysv4.2uw
                ;;
        -gnu/linux*)
                os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
                ;;
+       # es1800 is here to avoid being matched by es* (a different OS)
+       -es1800*)
+               os=-ose
+               ;;
        # Now accept the basic system types.
        # The portable systems comes first.
        # Each alternative MUST end in a * to match a version number.
@@ -1398,7 +1371,7 @@ case $os in
              | -aos* | -aros* | -cloudabi* | -sortix* \
              | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
              | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
-             | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
+             | -hiux* | -knetbsd* | -mirbsd* | -netbsd* \
              | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \
              | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
              | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
@@ -1409,14 +1382,15 @@ case $os in
              | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
              | -linux-newlib* | -linux-musl* | -linux-uclibc* \
              | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
-             | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+             | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* \
              | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
              | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
              | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
-             | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+             | -morphos* | -superux* | -rtmk* | -windiss* \
              | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
              | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \
-             | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox*)
+             | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox* | -bme* \
+             | -midnightbsd*)
        # Remember, each alternative MUST END IN *, to match a version number.
                ;;
        -qnx*)
@@ -1433,12 +1407,12 @@ case $os in
        -nto*)
                os=`echo $os | sed -e 's|nto|nto-qnx|'`
                ;;
-       -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
-             | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
+       -sim | -xray | -os68k* | -v88r* \
+             | -windows* | -osx | -abug | -netware* | -os9* \
              | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
                ;;
        -mac*)
-               os=`echo $os | sed -e 's|mac|macos|'`
+               os=`echo "$os" | sed -e 's|mac|macos|'`
                ;;
        -linux-dietlibc)
                os=-linux-dietlibc
@@ -1447,10 +1421,10 @@ case $os in
                os=`echo $os | sed -e 's|linux|linux-gnu|'`
                ;;
        -sunos5*)
-               os=`echo $os | sed -e 's|sunos5|solaris2|'`
+               os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
                ;;
        -sunos6*)
-               os=`echo $os | sed -e 's|sunos6|solaris3|'`
+               os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
                ;;
        -opened*)
                os=-openedition
@@ -1461,12 +1435,6 @@ case $os in
        -wince*)
                os=-wince
                ;;
-       -osfrose*)
-               os=-osfrose
-               ;;
-       -osf*)
-               os=-osf
-               ;;
        -utek*)
                os=-bsd
                ;;
@@ -1513,7 +1481,7 @@ case $os in
        -oss*)
                os=-sysv3
                ;;
-       -svr4)
+       -svr4*)
                os=-sysv4
                ;;
        -svr3)
@@ -1528,18 +1496,9 @@ case $os in
        -ose*)
                os=-ose
                ;;
-       -es1800*)
-               os=-ose
-               ;;
-       -xenix)
-               os=-xenix
-               ;;
        -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
                os=-mint
                ;;
-       -aros*)
-               os=-aros
-               ;;
        -zvmoe)
                os=-zvmoe
                ;;
@@ -1568,7 +1527,7 @@ case $os in
        *)
                # Get rid of the `-' at the beginning of $os.
                os=`echo $os | sed 's/[^-]*-//'`
-               echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+               echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2
                exit 1
                ;;
 esac
@@ -1664,9 +1623,6 @@ case $basic_machine in
        *-be)
                os=-beos
                ;;
-       *-haiku)
-               os=-haiku
-               ;;
        *-ibm)
                os=-aix
                ;;
@@ -1721,9 +1677,6 @@ case $basic_machine in
        i370-*)
                os=-mvs
                ;;
-       *-next)
-               os=-nextstep3
-               ;;
        *-gould)
                os=-sysv
                ;;
@@ -1833,11 +1786,11 @@ case $basic_machine in
                                vendor=stratus
                                ;;
                esac
-               basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+               basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"`
                ;;
 esac
 
-echo $basic_machine$os
+echo "$basic_machine$os"
 exit
 
 # Local variables:
index 1a3ce29..b390905 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for nghttp2 1.31.1.
+# Generated by GNU Autoconf 2.69 for nghttp2 1.34.0.
 #
 # Report bugs to <t-tujikawa@users.sourceforge.net>.
 #
@@ -590,8 +590,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='nghttp2'
 PACKAGE_TARNAME='nghttp2'
-PACKAGE_VERSION='1.31.1'
-PACKAGE_STRING='nghttp2 1.31.1'
+PACKAGE_VERSION='1.34.0'
+PACKAGE_STRING='nghttp2 1.34.0'
 PACKAGE_BUGREPORT='t-tujikawa@users.sourceforge.net'
 PACKAGE_URL=''
 
@@ -734,7 +734,6 @@ am__nodep
 AMDEPBACKSLASH
 AMDEP_FALSE
 AMDEP_TRUE
-am__quote
 am__include
 DEPDIR
 am__untar
@@ -840,7 +839,8 @@ PACKAGE_VERSION
 PACKAGE_TARNAME
 PACKAGE_NAME
 PATH_SEPARATOR
-SHELL'
+SHELL
+am__quote'
 ac_subst_files=''
 ac_user_opts='
 enable_option_checking
@@ -1464,7 +1464,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures nghttp2 1.31.1 to adapt to many kinds of systems.
+\`configure' configures nghttp2 1.34.0 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1536,7 +1536,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of nghttp2 1.31.1:";;
+     short | recursive ) echo "Configuration of nghttp2 1.34.0:";;
    esac
   cat <<\_ACEOF
 
@@ -1732,7 +1732,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-nghttp2 configure 1.31.1
+nghttp2 configure 1.34.0
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2692,7 +2692,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by nghttp2 $as_me 1.31.1, which was
+It was created by nghttp2 $as_me 1.34.0, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -12156,7 +12156,7 @@ test -n "$target_alias" &&
     NONENONEs,x,x, &&
   program_prefix=${target_alias}-
 
-am__api_version='1.15'
+am__api_version='1.16'
 
 # Find a good install program.  We prefer a C program (faster),
 # so one script is as good as another.  But avoid the broken or
@@ -12540,45 +12540,45 @@ DEPDIR="${am__leading_dot}deps"
 
 ac_config_commands="$ac_config_commands depfiles"
 
-
-am_make=${MAKE-make}
-cat > confinc << 'END'
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5
+$as_echo_n "checking whether ${MAKE-make} supports the include directive... " >&6; }
+cat > confinc.mk << 'END'
 am__doit:
-       @echo this is the am__doit target
+       @echo this is the am__doit target >confinc.out
 .PHONY: am__doit
 END
-# If we don't find an include directive, just comment out the code.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5
-$as_echo_n "checking for style of include used by $am_make... " >&6; }
 am__include="#"
 am__quote=
-_am_result=none
-# First try GNU make style include.
-echo "include confinc" > confmf
-# Ignore all kinds of additional output from 'make'.
-case `$am_make -s -f confmf 2> /dev/null` in #(
-*the\ am__doit\ target*)
-  am__include=include
-  am__quote=
-  _am_result=GNU
-  ;;
-esac
-# Now try BSD make style include.
-if test "$am__include" = "#"; then
-   echo '.include "confinc"' > confmf
-   case `$am_make -s -f confmf 2> /dev/null` in #(
-   *the\ am__doit\ target*)
-     am__include=.include
-     am__quote="\""
-     _am_result=BSD
+# BSD make does it like this.
+echo '.include "confinc.mk" # ignored' > confmf.BSD
+# Other make implementations (GNU, Solaris 10, AIX) do it like this.
+echo 'include confinc.mk # ignored' > confmf.GNU
+_am_result=no
+for s in GNU BSD; do
+  { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5
+   (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); }
+  case $?:`cat confinc.out 2>/dev/null` in #(
+  '0:this is the am__doit target') :
+    case $s in #(
+  BSD) :
+    am__include='.include' am__quote='"' ;; #(
+  *) :
+    am__include='include' am__quote='' ;;
+esac ;; #(
+  *) :
      ;;
-   esac
-fi
-
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5
-$as_echo "$_am_result" >&6; }
-rm -f confinc confmf
+esac
+  if test "$am__include" != "#"; then
+    _am_result="yes ($s style)"
+    break
+  fi
+done
+rm -f confinc.* confmf.*
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5
+$as_echo "${_am_result}" >&6; }
 
 # Check whether --enable-dependency-tracking was given.
 if test "${enable_dependency_tracking+set}" = set; then :
@@ -12660,7 +12660,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='nghttp2'
- VERSION='1.31.1'
+ VERSION='1.34.0'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -12690,8 +12690,8 @@ MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
 
 # For better backward compatibility.  To be removed once Automake 1.9.x
 # dies out for good.  For more background, see:
-# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
-# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
 mkdir_p='$(MKDIR_P)'
 
 # We need awk for the "check" target (and possibly the TAP driver).  The
@@ -12870,7 +12870,7 @@ END
 Aborting the configuration process, to ensure you take notice of the issue.
 
 You can download and install GNU coreutils to get an 'rm' implementation
-that behaves properly: <http://www.gnu.org/software/coreutils/>.
+that behaves properly: <https://www.gnu.org/software/coreutils/>.
 
 If you want to complete the configuration process using your problematic
 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
 AM_BACKSLASH='\'
 
 
-LT_CURRENT=30
+LT_CURRENT=31
 
 LT_REVISION=1
 
-LT_AGE=16
+LT_AGE=17
 
 
 major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/^0-9//g"`
@@ -14341,7 +14341,7 @@ with_gnu_ld=$lt_cv_prog_gnu_ld
       # Commands to make compiler produce verbose output that lists
       # what "hidden" libraries, object files and flags are used when
       # linking a shared library.
-      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"'
 
     else
       GXX=no
@@ -14833,7 +14833,7 @@ fi
             # explicitly linking system object files so we need to strip them
             # from the output so that they don't get included in the library
             # dependencies.
-            output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+            output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
             ;;
           *)
             if test yes = "$GXX"; then
@@ -14898,7 +14898,7 @@ fi
            # explicitly linking system object files so we need to strip them
            # from the output so that they don't get included in the library
            # dependencies.
-           output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+           output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
            ;;
           *)
            if test yes = "$GXX"; then
@@ -15237,7 +15237,7 @@ fi
              # Commands to make compiler produce verbose output that lists
              # what "hidden" libraries, object files and flags are used when
              # linking a shared library.
-             output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+             output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"'
 
            else
              # FIXME: insert proper C++ library support
@@ -15321,7 +15321,7 @@ fi
                # Commands to make compiler produce verbose output that lists
                # what "hidden" libraries, object files and flags are used when
                # linking a shared library.
-               output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+               output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"'
              else
                # g++ 2.7 appears to require '-G' NOT '-shared' on this
                # platform.
@@ -15332,7 +15332,7 @@ fi
                # Commands to make compiler produce verbose output that lists
                # what "hidden" libraries, object files and flags are used when
                # linking a shared library.
-               output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+               output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"'
              fi
 
              hardcode_libdir_flag_spec_CXX='$wl-R $wl$libdir'
@@ -17728,7 +17728,7 @@ if ${am_cv_pathless_PYTHON+:} false; then :
   $as_echo_n "(cached) " >&6
 else
 
-       for am_cv_pathless_PYTHON in python python2 python3 python3.8 python3.7 python3.6 python3.5 python3.4 python3.3 python3.2 python3.1 python3.0 python2.7  python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0 none; do
+       for am_cv_pathless_PYTHON in python python2 python3  python3.9 python3.8 python3.7 python3.6 python3.5 python3.4 python3.3  python3.2 python3.1 python3.0  python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1  python2.0 none; do
          test "$am_cv_pathless_PYTHON" = none && break
          prog="import sys
 # split strings by '.' and convert to numeric.  Append some zeros
@@ -24563,7 +24563,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by nghttp2 $as_me 1.31.1, which was
+This file was extended by nghttp2 $as_me 1.34.0, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -24629,7 +24629,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-nghttp2 config.status 1.31.1
+nghttp2 config.status 1.34.0
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
@@ -25129,7 +25129,7 @@ fi
 
 
 
-AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
+AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"
 
 _ACEOF
 
@@ -26487,29 +26487,35 @@ _LT_EOF
   # Older Autoconf quotes --file arguments for eval, but not when files
   # are listed without --file.  Let's play safe and only enable the eval
   # if we detect the quoting.
-  case $CONFIG_FILES in
-  *\'*) eval set x "$CONFIG_FILES" ;;
-  *)   set x $CONFIG_FILES ;;
-  esac
+  # TODO: see whether this extra hack can be removed once we start
+  # requiring Autoconf 2.70 or later.
+  case $CONFIG_FILES in #(
+  *\'*) :
+    eval set x "$CONFIG_FILES" ;; #(
+  *) :
+    set x $CONFIG_FILES ;; #(
+  *) :
+     ;;
+esac
   shift
-  for mf
+  # Used to flag and report bootstrapping failures.
+  am_rc=0
+  for am_mf
   do
     # Strip MF so we end up with the name of the file.
-    mf=`echo "$mf" | sed -e 's/:.*$//'`
-    # Check whether this is an Automake generated Makefile or not.
-    # We used to match only the files named 'Makefile.in', but
-    # some people rename them; so instead we look at the file content.
-    # Grep'ing the first line is not enough: some people post-process
-    # each Makefile.in and add a new line on top of each file to say so.
-    # Grep'ing the whole file is not good either: AIX grep has a line
+    am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'`
+    # Check whether this is an Automake generated Makefile which includes
+    # dependency-tracking related rules and includes.
+    # Grep'ing the whole file directly is not great: AIX grep has a line
     # limit of 2048, but all sed's we know have understand at least 4000.
-    if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
-      dirpart=`$as_dirname -- "$mf" ||
-$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-        X"$mf" : 'X\(//\)[^/]' \| \
-        X"$mf" : 'X\(//\)$' \| \
-        X"$mf" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$mf" |
+    sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
+      || continue
+    am_dirpart=`$as_dirname -- "$am_mf" ||
+$as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$am_mf" : 'X\(//\)[^/]' \| \
+        X"$am_mf" : 'X\(//\)$' \| \
+        X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$am_mf" |
     sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
            s//\1/
            q
@@ -26527,53 +26533,48 @@ $as_echo X"$mf" |
            q
          }
          s/.*/./; q'`
-    else
-      continue
-    fi
-    # Extract the definition of DEPDIR, am__include, and am__quote
-    # from the Makefile without running 'make'.
-    DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
-    test -z "$DEPDIR" && continue
-    am__include=`sed -n 's/^am__include = //p' < "$mf"`
-    test -z "$am__include" && continue
-    am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
-    # Find all dependency output files, they are included files with
-    # $(DEPDIR) in their names.  We invoke sed twice because it is the
-    # simplest approach to changing $(DEPDIR) to its actual value in the
-    # expansion.
-    for file in `sed -n "
-      s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
-        sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
-      # Make sure the directory exists.
-      test -f "$dirpart/$file" && continue
-      fdir=`$as_dirname -- "$file" ||
-$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-        X"$file" : 'X\(//\)[^/]' \| \
-        X"$file" : 'X\(//\)$' \| \
-        X"$file" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$file" |
-    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
-           s//\1/
-           q
-         }
-         /^X\(\/\/\)[^/].*/{
+    am_filepart=`$as_basename -- "$am_mf" ||
+$as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$am_mf" : 'X\(//\)$' \| \
+        X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$am_mf" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
            s//\1/
            q
          }
-         /^X\(\/\/\)$/{
+         /^X\/\(\/\/\)$/{
            s//\1/
            q
          }
-         /^X\(\/\).*/{
+         /^X\/\(\/\).*/{
            s//\1/
            q
          }
          s/.*/./; q'`
-      as_dir=$dirpart/$fdir; as_fn_mkdir_p
-      # echo "creating $dirpart/$file"
-      echo '# dummy' > "$dirpart/$file"
-    done
+    { echo "$as_me:$LINENO: cd "$am_dirpart" \
+      && sed -e '/# am--include-marker/d' "$am_filepart" \
+        | $MAKE -f - am--depfiles" >&5
+   (cd "$am_dirpart" \
+      && sed -e '/# am--include-marker/d' "$am_filepart" \
+        | $MAKE -f - am--depfiles) >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); } || am_rc=$?
   done
+  if test $am_rc -ne 0; then
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "Something went wrong bootstrapping makefile fragments
+    for automatic dependency tracking.  Try re-running configure with the
+    '--disable-dependency-tracking' option to at least be able to build
+    the package (albeit without support for automatic dependency tracking).
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+  { am_dirpart=; unset am_dirpart;}
+  { am_filepart=; unset am_filepart;}
+  { am_mf=; unset am_mf;}
+  { am_rc=; unset am_rc;}
+  rm -f conftest-deps.mk
 }
  ;;
 
index b489609..fbbdfcf 100644 (file)
@@ -25,7 +25,7 @@ dnl Do not change user variables!
 dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
 
 AC_PREREQ(2.61)
-AC_INIT([nghttp2], [1.31.1], [t-tujikawa@users.sourceforge.net])
+AC_INIT([nghttp2], [1.34.0], [t-tujikawa@users.sourceforge.net])
 AC_CONFIG_AUX_DIR([.])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_HEADERS([config.h])
@@ -44,9 +44,9 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 
 dnl See versioning rule:
 dnl  http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
-AC_SUBST(LT_CURRENT, 30)
+AC_SUBST(LT_CURRENT, 31)
 AC_SUBST(LT_REVISION, 1)
-AC_SUBST(LT_AGE, 16)
+AC_SUBST(LT_AGE, 17)
 
 major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
 minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
index 697304b..f6cfe15 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -360,8 +360,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
          *config.status*) \
            cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
          *) \
-           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
-           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
        esac;
 
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -385,7 +385,10 @@ ctags CTAGS:
 cscope cscopelist:
 
 
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
        @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        list='$(DISTFILES)'; \
diff --git a/depcomp b/depcomp
index b39f98f..65cbf70 100755 (executable)
--- a/depcomp
+++ b/depcomp
@@ -1,9 +1,9 @@
 #! /bin/sh
 # depcomp - compile a program generating dependencies as side-effects
 
-scriptversion=2016-01-11.22; # UTC
+scriptversion=2018-03-07.03; # UTC
 
-# Copyright (C) 1999-2017 Free Software Foundation, Inc.
+# Copyright (C) 1999-2018 Free Software Foundation, Inc.
 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@ scriptversion=2016-01-11.22; # UTC
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 # As a special exception to the GNU General Public License, if you
 # distribute this file as part of a program that contains a
@@ -783,7 +783,7 @@ exit 0
 # Local Variables:
 # mode: shell-script
 # sh-indentation: 2
-# eval: (add-hook 'write-file-hooks 'time-stamp)
+# eval: (add-hook 'before-save-hook 'time-stamp)
 # time-stamp-start: "scriptversion="
 # time-stamp-format: "%:y-%02m-%02d.%02H"
 # time-stamp-time-zone: "UTC0"
index 07cd34e..199c895 100644 (file)
@@ -164,6 +164,7 @@ APIDOCS= \
        nghttp2_submit_extension.rst \
        nghttp2_submit_goaway.rst \
        nghttp2_submit_headers.rst \
+       nghttp2_submit_origin.rst \
        nghttp2_submit_ping.rst \
        nghttp2_submit_priority.rst \
        nghttp2_submit_push_promise.rst \
index 2f83a16..35d1132 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -521,6 +521,7 @@ APIDOCS = \
        nghttp2_submit_extension.rst \
        nghttp2_submit_goaway.rst \
        nghttp2_submit_headers.rst \
+       nghttp2_submit_origin.rst \
        nghttp2_submit_ping.rst \
        nghttp2_submit_priority.rst \
        nghttp2_submit_push_promise.rst \
@@ -616,8 +617,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
          *config.status*) \
            cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
          *) \
-           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
-           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
        esac;
 
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -718,7 +719,10 @@ ctags CTAGS:
 cscope cscopelist:
 
 
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
        @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        list='$(DISTFILES)'; \
index 0c140d7..0502cfd 100644 (file)
@@ -8,7 +8,7 @@ _nghttpx()
     _get_comp_words_by_ref cur prev
     case $cur in
         -*)
-            COMPREPLY=( $( compgen -W '--worker-read-rate --include --frontend-http2-dump-response-header --tls-ticket-key-file --verify-client-cacert --max-response-header-fields --backend-http2-window-size --frontend-keep-alive-timeout --backend-request-buffer --max-request-header-fields --backend-connect-timeout --tls-max-proto-version --conf --dns-lookup-timeout --backend-http2-max-concurrent-streams --worker-write-burst --npn-list --dns-max-try --fetch-ocsp-response-file --no-via --tls-session-cache-memcached-cert-file --no-http2-cipher-black-list --mruby-file --add-forwarded --client-no-http2-cipher-black-list --stream-read-timeout --client-ciphers --ocsp-update-interval --forwarded-for --accesslog-syslog --dns-cache-timeout --frontend-http2-read-timeout --listener-disable-timeout --ciphers --client-psk-secrets --strip-incoming-x-forwarded-for --no-server-rewrite --private-key-passwd-file --backend-keep-alive-timeout --backend-http-proxy-uri --frontend-max-requests --rlimit-nofile --no-strip-incoming-x-forwarded-proto --tls-ticket-key-memcached-cert-file --no-verify-ocsp --forwarded-by --tls-session-cache-memcached-private-key-file --error-page --ocsp-startup --backend-write-timeout --tls-dyn-rec-warmup-threshold --tls-ticket-key-memcached-max-retry --frontend-http2-window-size --http2-no-cookie-crumbling --worker-read-burst --dh-param-file --accesslog-format --errorlog-syslog --redirect-https-port --request-header-field-buffer --api-max-request-body --frontend-http2-decoder-dynamic-table-size --errorlog-file --frontend-http2-max-concurrent-streams --psk-secrets --frontend-write-timeout --tls-ticket-key-cipher --read-burst --no-add-x-forwarded-proto --backend --server-name --insecure --backend-max-backoff --log-level --host-rewrite --tls-ticket-key-memcached-interval --frontend-http2-setting-timeout --frontend-http2-connection-window-size --worker-frontend-connections --syslog-facility --fastopen --no-location-rewrite --single-thread --tls-session-cache-memcached --no-ocsp --backend-response-buffer --tls-min-proto-version --workers --add-x-forwarded-for --no-server-push --worker-write-rate --add-request-header --backend-http2-settings-timeout --subcert --ecdh-curves --no-kqueue --help --frontend-frame-debug --tls-sct-dir --pid-file --frontend-http2-dump-request-header --daemon --write-rate --altsvc --backend-http2-decoder-dynamic-table-size --user --verify-client-tolerate-expired --frontend-read-timeout --tls-ticket-key-memcached-max-fail --backlog --write-burst --backend-connections-per-host --response-header-field-buffer --tls-ticket-key-memcached-address-family --padding --tls-session-cache-memcached-address-family --stream-write-timeout --cacert --tls-ticket-key-memcached-private-key-file --accesslog-write-early --backend-address-family --backend-http2-connection-window-size --version --add-response-header --backend-read-timeout --frontend-http2-optimize-window-size --frontend --accesslog-file --http2-proxy --backend-http2-encoder-dynamic-table-size --client-private-key-file --single-process --client-cert-file --tls-ticket-key-memcached --tls-dyn-rec-idle-timeout --frontend-http2-optimize-write-buffer-size --verify-client --frontend-http2-encoder-dynamic-table-size --read-rate --backend-connections-per-frontend --strip-incoming-forwarded ' -- "$cur" ) )
+            COMPREPLY=( $( compgen -W '--worker-read-rate --include --frontend-http2-dump-response-header --tls-ticket-key-file --verify-client-cacert --max-response-header-fields --backend-http2-window-size --tls13-client-ciphers --frontend-keep-alive-timeout --backend-request-buffer --max-request-header-fields --backend-connect-timeout --tls-max-proto-version --conf --dns-lookup-timeout --backend-http2-max-concurrent-streams --worker-write-burst --npn-list --dns-max-try --fetch-ocsp-response-file --no-via --tls-session-cache-memcached-cert-file --no-http2-cipher-black-list --mruby-file --add-forwarded --client-no-http2-cipher-black-list --stream-read-timeout --client-ciphers --ocsp-update-interval --forwarded-for --accesslog-syslog --dns-cache-timeout --frontend-http2-read-timeout --listener-disable-timeout --ciphers --client-psk-secrets --strip-incoming-x-forwarded-for --no-server-rewrite --private-key-passwd-file --backend-keep-alive-timeout --backend-http-proxy-uri --frontend-max-requests --tls-no-postpone-early-data --rlimit-nofile --no-strip-incoming-x-forwarded-proto --tls-ticket-key-memcached-cert-file --no-verify-ocsp --forwarded-by --tls-session-cache-memcached-private-key-file --error-page --ocsp-startup --backend-write-timeout --tls-dyn-rec-warmup-threshold --tls-ticket-key-memcached-max-retry --frontend-http2-window-size --http2-no-cookie-crumbling --worker-read-burst --dh-param-file --accesslog-format --errorlog-syslog --redirect-https-port --request-header-field-buffer --api-max-request-body --frontend-http2-decoder-dynamic-table-size --errorlog-file --frontend-http2-max-concurrent-streams --psk-secrets --frontend-write-timeout --tls-ticket-key-cipher --read-burst --no-add-x-forwarded-proto --backend --server-name --insecure --backend-max-backoff --log-level --host-rewrite --tls-ticket-key-memcached-interval --frontend-http2-setting-timeout --frontend-http2-connection-window-size --worker-frontend-connections --syslog-facility --fastopen --no-location-rewrite --single-thread --tls-session-cache-memcached --no-ocsp --backend-response-buffer --tls-min-proto-version --workers --add-x-forwarded-for --no-server-push --worker-write-rate --add-request-header --backend-http2-settings-timeout --subcert --ignore-per-pattern-mruby-error --ecdh-curves --no-kqueue --help --frontend-frame-debug --tls-sct-dir --pid-file --frontend-http2-dump-request-header --daemon --write-rate --altsvc --backend-http2-decoder-dynamic-table-size --no-strip-incoming-early-data --user --verify-client-tolerate-expired --frontend-read-timeout --tls-ticket-key-memcached-max-fail --backlog --write-burst --backend-connections-per-host --tls-max-early-data --response-header-field-buffer --tls-ticket-key-memcached-address-family --padding --tls-session-cache-memcached-address-family --stream-write-timeout --cacert --tls-ticket-key-memcached-private-key-file --accesslog-write-early --backend-address-family --backend-http2-connection-window-size --tls13-ciphers --version --add-response-header --backend-read-timeout --frontend-http2-optimize-window-size --frontend --accesslog-file --http2-proxy --backend-http2-encoder-dynamic-table-size --client-private-key-file --single-process --client-cert-file --tls-ticket-key-memcached --tls-dyn-rec-idle-timeout --frontend-http2-optimize-write-buffer-size --verify-client --frontend-http2-encoder-dynamic-table-size --read-rate --backend-connections-per-frontend --strip-incoming-forwarded ' -- "$cur" ) )
             ;;
         *)
             _filedir
index 5dfaee0..866b2b9 100644 (file)
@@ -282,6 +282,11 @@ Enums
         (``0x0a``) 
         The ALTSVC frame, which is defined in `RFC 7383
         <https://tools.ietf.org/html/rfc7838#section-4>`_.
+    .. macro:: NGHTTP2_ORIGIN
+
+        (``0x0c``) 
+        The ORIGIN frame, which is defined by `RFC 8336
+        <https://tools.ietf.org/html/rfc8336>`_.
 
 .. type:: nghttp2_flag
 
@@ -342,6 +347,11 @@ Enums
 
         (``0x06``) 
         SETTINGS_MAX_HEADER_LIST_SIZE
+    .. macro:: NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL
+
+        (``0x08``) 
+        SETTINGS_ENABLE_CONNECT_PROTOCOL
+        (`RFC 8441 <https://tools.ietf.org/html/rfc8441>`_)
 
 .. type:: nghttp2_error_code
 
index 46dfe62..dede16f 100644 (file)
@@ -1,6 +1,6 @@
 .\" Man page generated from reStructuredText.
 .
-.TH "H2LOAD" "1" "Apr 07, 2018" "1.31.1" "nghttp2"
+.TH "H2LOAD" "1" "Oct 04, 2018" "1.34.0" "nghttp2"
 .SH NAME
 h2load \- HTTP/2 benchmarking tool
 .
@@ -155,7 +155,7 @@ example,  with   \fI\%\-t\fP2  and   \fI\%\-r\fP4,  each  thread   gets  2
 connections per period.  When the rate is 0, the program
 will run  as it  normally does, creating  connections at
 whatever variable rate it  wants.  The default value for
-this option is 0.
+this option is 0.  \fI\%\-r\fP and \fI\%\-D\fP are mutually exclusive.
 .UNINDENT
 .INDENT 0.0
 .TP
@@ -170,7 +170,8 @@ option is 1s.
 .TP
 .B \-D, \-\-duration=<N>
 Specifies the main duration for the measurements in case
-of timing\-based benchmarking.
+of timing\-based  benchmarking.  \fI\%\-D\fP  and \fI\%\-r\fP  are mutually
+exclusive.
 .UNINDENT
 .INDENT 0.0
 .TP
index 1a8171f..f37a515 100644 (file)
@@ -124,7 +124,7 @@ OPTIONS
     connections per period.  When the rate is 0, the program
     will run  as it  normally does, creating  connections at
     whatever variable rate it  wants.  The default value for
-    this option is 0.
+    this option is 0.  :option:`-r` and :option:`\-D` are mutually exclusive.
 
 .. option:: --rate-period=<DURATION>
 
@@ -137,7 +137,8 @@ OPTIONS
 .. option:: -D, --duration=<N>
 
     Specifies the main duration for the measurements in case
-    of timing-based benchmarking.
+    of timing-based  benchmarking.  :option:`-D`  and :option:`\-r`  are mutually
+    exclusive.
 
 .. option:: --warm-up-time=<DURATION>
 
index cf7d525..3cf1682 100644 (file)
@@ -1,6 +1,6 @@
 .\" Man page generated from reStructuredText.
 .
-.TH "NGHTTP" "1" "Apr 07, 2018" "1.31.1" "nghttp2"
+.TH "NGHTTP" "1" "Oct 04, 2018" "1.34.0" "nghttp2"
 .SH NAME
 nghttp \- HTTP/2 client
 .
index b15fae3..19e0f1f 100644 (file)
@@ -12,12 +12,12 @@ Synopsis
     
     This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of
     remote endpoint as if it is received in SETTINGS frame.  Without
-    specifying this option, before the local endpoint receives
-    SETTINGS_MAX_CONCURRENT_STREAMS in SETTINGS frame from remote
-    endpoint, SETTINGS_MAX_CONCURRENT_STREAMS is unlimited.  This may
-    cause problem if local endpoint submits lots of requests initially
-    and sending them at once to the remote peer may lead to the
-    rejection of some requests.  Specifying this option to the sensible
-    value, say 100, may avoid this kind of issue. This value will be
-    overwritten if the local endpoint receives
-    SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint.
+    specifying this option, the maximum number of outgoing concurrent
+    streams is initially limited to 100 to avoid issues when the local
+    endpoint submits lots of requests before receiving initial SETTINGS
+    frame from the remote endpoint, since sending them at once to the
+    remote endpoint could lead to rejection of some of the requests.
+    This value will be overwritten when the local endpoint receives
+    initial SETTINGS frame from the remote endpoint, either to the
+    value advertised in SETTINGS_MAX_CONCURRENT_STREAMS or to the
+    default value (unlimited) if none was advertised.
index 8dda1e1..c6735a0 100644 (file)
@@ -13,8 +13,7 @@ Synopsis
     Submits ALTSVC frame.
     
     ALTSVC frame is a non-critical extension to HTTP/2, and defined in
-    is defined in `RFC 7383
-    <https://tools.ietf.org/html/rfc7838#section-4>`_.
+    `RFC 7383 <https://tools.ietf.org/html/rfc7838#section-4>`_.
     
     The *flags* is currently ignored and should be
     :macro:`NGHTTP2_FLAG_NONE`.
diff --git a/doc/nghttp2_submit_origin.rst b/doc/nghttp2_submit_origin.rst
new file mode 100644 (file)
index 0000000..790441d
--- /dev/null
@@ -0,0 +1,35 @@
+
+nghttp2_submit_origin
+=====================
+
+Synopsis
+--------
+
+*#include <nghttp2/nghttp2.h>*
+
+.. function:: int nghttp2_submit_origin(nghttp2_session *session, uint8_t flags, const nghttp2_origin_entry *ov, size_t nov)
+
+    
+    Submits ORIGIN frame.
+    
+    ORIGIN frame is a non-critical extension to HTTP/2 and defined by
+    `RFC 8336 <https://tools.ietf.org/html/rfc8336>`_.
+    
+    The *flags* is currently ignored and should be
+    :macro:`NGHTTP2_FLAG_NONE`.
+    
+    The *ov* points to the array of origins.  The *nov* specifies the
+    number of origins included in *ov*.  This function creates copies
+    of all elements in *ov*.
+    
+    The ORIGIN frame is only usable by a server.  If this function is
+    invoked with client side session, this function returns
+    :macro:`NGHTTP2_ERR_INVALID_STATE`.
+    
+    :macro:`NGHTTP2_ERR_NOMEM`
+        Out of memory
+    :macro:`NGHTTP2_ERR_INVALID_STATE`
+        The function is called from client side session.
+    :macro:`NGHTTP2_ERR_INVALID_ARGUMENT`
+        There are too many origins, or an origin is too large to fit
+        into a default frame payload.
index 186837c..c7e229f 100644 (file)
@@ -74,8 +74,11 @@ Synopsis
     .. warning::
     
       This function returns assigned stream ID if it succeeds.  But
-      that stream is not opened yet.  The application must not submit
+      that stream is not created yet.  The application must not submit
       frame to that stream ID before
       :type:`nghttp2_before_frame_send_callback` is called for this
-      frame.
+      frame.  This means `nghttp2_session_get_stream_user_data()` does
+      not work before the callback.  But
+      `nghttp2_session_set_stream_user_data()` handles this situation
+      specially, and it can set data to a stream during this period.
     
index 6051b60..d3587f5 100644 (file)
@@ -1,6 +1,6 @@
 .\" Man page generated from reStructuredText.
 .
-.TH "NGHTTPD" "1" "Apr 07, 2018" "1.31.1" "nghttp2"
+.TH "NGHTTPD" "1" "Oct 04, 2018" "1.34.0" "nghttp2"
 .SH NAME
 nghttpd \- HTTP/2 server
 .
index b442168..3d8bf31 100644 (file)
@@ -1,6 +1,6 @@
 .\" Man page generated from reStructuredText.
 .
-.TH "NGHTTPX" "1" "Apr 07, 2018" "1.31.1" "nghttp2"
+.TH "NGHTTPX" "1" "Oct 04, 2018" "1.34.0" "nghttp2"
 .SH NAME
 nghttpx \- HTTP/2 proxy
 .
@@ -137,12 +137,15 @@ Several parameters <PARAM> are accepted after <PATTERN>.
 The  parameters are  delimited  by  ";".  The  available
 parameters       are:      "proto=<PROTO>",       "tls",
 "sni=<SNI_HOST>",         "fall=<N>",        "rise=<N>",
-"affinity=<METHOD>",  "dns", and  "redirect\-if\-not\-tls".
-The  parameter  consists   of  keyword,  and  optionally
-followed by  "=" and value.  For  example, the parameter
-"proto=h2"  consists of  the keyword  "proto" and  value
-"h2".  The parameter "tls" consists of the keyword "tls"
-without value.  Each parameter is described as follows.
+"affinity=<METHOD>",    "dns",    "redirect\-if\-not\-tls",
+"upgrade\-scheme",                        "mruby=<PATH>",
+"read\-timeout=<DURATION>",                           and
+"write\-timeout=<DURATION>".   The parameter  consists of
+keyword, and optionally followed  by "=" and value.  For
+example,  the  parameter   "proto=h2"  consists  of  the
+keyword  "proto" and  value "h2".   The parameter  "tls"
+consists  of  the  keyword "tls"  without  value.   Each
+parameter is described as follows.
 .sp
 The backend application protocol  can be specified using
 optional  "proto"   parameter,  and   in  the   form  of
@@ -235,6 +238,19 @@ particular backend.  This is  a workaround for a backend
 server  which  requires  "https" :scheme  pseudo  header
 field on TLS encrypted connection.
 .sp
+"mruby=<PATH>"  parameter  specifies  a  path  to  mruby
+script  file  which  is  invoked when  this  pattern  is
+matched.  All backends which share the same pattern must
+have the same mruby path.
+.sp
+"read\-timeout=<DURATION>" and "write\-timeout=<DURATION>"
+parameters  specify the  read and  write timeout  of the
+backend connection  when this  pattern is  matched.  All
+backends which share the same pattern must have the same
+timeouts.  If these timeouts  are entirely omitted for a
+pattern,            \fI\%\-\-backend\-read\-timeout\fP           and
+\fI\%\-\-backend\-write\-timeout\fP are used.
+.sp
 Since ";" and ":" are  used as delimiter, <PATTERN> must
 not  contain these  characters.  Since  ";" has  special
 meaning in shell, the option value must be quoted.
@@ -595,19 +611,43 @@ Default: \fB2m\fP
 .B \-\-ciphers=<SUITE>
 Set allowed  cipher list  for frontend  connection.  The
 format of the string is described in OpenSSL ciphers(1).
+This option  sets cipher suites for  TLSv1.2 or earlier.
+Use \fI\%\-\-tls13\-ciphers\fP for TLSv1.3.
 .sp
 Default: \fBECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256\fP
 .UNINDENT
 .INDENT 0.0
 .TP
+.B \-\-tls13\-ciphers=<SUITE>
+Set allowed  cipher list  for frontend  connection.  The
+format of the string is described in OpenSSL ciphers(1).
+This  option  sets  cipher   suites  for  TLSv1.3.   Use
+\fI\%\-\-ciphers\fP for TLSv1.2 or earlier.
+.sp
+Default: \fBTLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256\fP
+.UNINDENT
+.INDENT 0.0
+.TP
 .B \-\-client\-ciphers=<SUITE>
 Set  allowed cipher  list for  backend connection.   The
 format of the string is described in OpenSSL ciphers(1).
+This option  sets cipher suites for  TLSv1.2 or earlier.
+Use \fI\%\-\-tls13\-client\-ciphers\fP for TLSv1.3.
 .sp
 Default: \fBECDHE\-ECDSA\-AES256\-GCM\-SHA384:ECDHE\-RSA\-AES256\-GCM\-SHA384:ECDHE\-ECDSA\-CHACHA20\-POLY1305:ECDHE\-RSA\-CHACHA20\-POLY1305:ECDHE\-ECDSA\-AES128\-GCM\-SHA256:ECDHE\-RSA\-AES128\-GCM\-SHA256:ECDHE\-ECDSA\-AES256\-SHA384:ECDHE\-RSA\-AES256\-SHA384:ECDHE\-ECDSA\-AES128\-SHA256:ECDHE\-RSA\-AES128\-SHA256\fP
 .UNINDENT
 .INDENT 0.0
 .TP
+.B \-\-tls13\-client\-ciphers=<SUITE>
+Set  allowed cipher  list for  backend connection.   The
+format of the string is described in OpenSSL ciphers(1).
+This  option  sets  cipher   suites  for  TLSv1.3.   Use
+\fI\%\-\-tls13\-client\-ciphers\fP for TLSv1.2 or earlier.
+.sp
+Default: \fBTLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256\fP
+.UNINDENT
+.INDENT 0.0
+.TP
 .B \-\-ecdh\-curves=<LIST>
 Set  supported  curve  list  for  frontend  connections.
 <LIST> is a  colon separated list of curve  NID or names
@@ -729,7 +769,7 @@ than TLSv1.2 is specified, make sure that the compatible
 ciphers are  included in \fI\%\-\-ciphers\fP option.   The default
 cipher  list  only   includes  ciphers  compatible  with
 TLSv1.2 or above.  The available versions are:
-TLSv1.2, TLSv1.1, and TLSv1.0
+TLSv1.3, TLSv1.2, TLSv1.1, and TLSv1.0
 .sp
 Default: \fBTLSv1.2\fP
 .UNINDENT
@@ -742,9 +782,9 @@ done in  case\-insensitive manner.  The  versions between
 enabled.  If the protocol list advertised by client does
 not  overlap  this range,  you  will  receive the  error
 message "unknown protocol".  The available versions are:
-TLSv1.2, TLSv1.1, and TLSv1.0
+TLSv1.3, TLSv1.2, TLSv1.1, and TLSv1.0
 .sp
-Default: \fBTLSv1.2\fP
+Default: \fBTLSv1.3\fP
 .UNINDENT
 .INDENT 0.0
 .TP
@@ -997,6 +1037,24 @@ HTTP/2.   To  use  those   cipher  suites  with  HTTP/2,
 consider   to  use   \fI\%\-\-client\-no\-http2\-cipher\-black\-list\fP
 option.  But be aware its implications.
 .UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-tls\-no\-postpone\-early\-data
+By default,  nghttpx postpones forwarding  HTTP requests
+sent in early data, including those sent in partially in
+it, until TLS handshake finishes.  If all backend server
+recognizes "Early\-Data" header  field, using this option
+makes nghttpx  not postpone  forwarding request  and get
+full potential of 0\-RTT data.
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-tls\-max\-early\-data=<SIZE>
+Sets  the  maximum  amount  of 0\-RTT  data  that  server
+accepts.
+.sp
+Default: \fB16K\fP
+.UNINDENT
 .SS HTTP/2
 .INDENT 0.0
 .TP
@@ -1360,6 +1418,12 @@ is received, it is left unaltered.
 .UNINDENT
 .INDENT 0.0
 .TP
+.B \-\-no\-strip\-incoming\-early\-data
+Don\(aqt strip Early\-Data header  field from inbound client
+requests.
+.UNINDENT
+.INDENT 0.0
+.TP
 .B \-\-no\-location\-rewrite
 Don\(aqt  rewrite location  header field  in default  mode.
 When \fI\%\-\-http2\-proxy\fP  is used, location header  field will
@@ -1577,6 +1641,13 @@ signal handling feature is disabled.
 .B \-\-mruby\-file=<PATH>
 Set mruby script file
 .UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-ignore\-per\-pattern\-mruby\-error
+Ignore mruby compile error  for per\-pattern mruby script
+file.  If error  occurred, it is treated as  if no mruby
+file were specified for the pattern.
+.UNINDENT
 .SS Misc
 .INDENT 0.0
 .TP
@@ -1934,9 +2005,28 @@ server.  These hooks allows users to modify header fields, or common
 HTTP variables, like authority or request path, and even return custom
 response without forwarding request to backend servers.
 .sp
-To specify mruby script file, use \fI\%\-\-mruby\-file\fP option.  The
-script will be evaluated once per thread on startup, and it must
-instantiate object and evaluate it as the return value (e.g.,
+There are 2 levels of mruby script invocations: global and
+per\-pattern.  The global mruby script is set by \fI\%\-\-mruby\-file\fP
+option and is called for all requests.  The per\-pattern mruby script
+is set by "mruby" parameter in \fI\%\-b\fP option.  It is invoked for
+a request which matches the particular pattern.  The order of hook
+invocation is: global request phase hook, per\-pattern request phase
+hook, per\-pattern response phase hook, and finally global response
+phase hook.  If a hook returns a response, any later hooks are not
+invoked.  The global request hook is invoked before the pattern
+matching is made and changing request path may affect the pattern
+matching.
+.sp
+Please note that request and response hooks of per\-pattern mruby
+script for a single request might not come from the same script.  This
+might happen after a request hook is executed, backend failed for some
+reason, and at the same time, backend configuration is replaced by API
+request, and then the request uses new configuration on retry.  The
+response hook from new configuration, if it is specified, will be
+invoked.
+.sp
+The all mruby script will be evaluated once per thread on startup, and
+it must instantiate object and evaluate it as the return value (e.g.,
 \fBApp.new\fP).  This object is called app object.  If app object
 defines \fBon_req\fP method, it is called with \fI\%Nghttpx::Env\fP
 object on request hook.  Similarly, if app object defines \fBon_resp\fP
@@ -2073,6 +2163,15 @@ Return true if, and only if a SSL/TLS session is reused.
 .B attribute [R] alpn
 Return ALPN identifier negotiated in this connection.
 .UNINDENT
+.INDENT 7.0
+.TP
+.B attribute [R] tls_handshake_finished
+Return true if SSL/TLS handshake has finished.  If it returns
+false in the request phase hook, the request is received in
+TLSv1.3 early data (0\-RTT) and might be vulnerable to the
+replay attack.  nghttpx will send Early\-Data header field to
+backend servers to indicate this.
+.UNINDENT
 .UNINDENT
 .INDENT 0.0
 .TP
@@ -2224,10 +2323,10 @@ to the backend, and response phase hook for this request will
 not be invoked.  When this method is called in response phase
 hook, response from backend server is canceled and discarded.
 The status code and response header fields should be set
-before using this method.  To set status code, use :rb:meth To
-set response header fields, use
+before using this method.  To set status code, use
 \fI\%Nghttpx::Response#status\fP\&.  If status code is not
-set, 200 is used.  \fI\%Nghttpx::Response#add_header\fP and
+set, 200 is used.  To set response header fields,
+\fI\%Nghttpx::Response#add_header\fP and
 \fI\%Nghttpx::Response#set_header\fP\&.  When this method is
 invoked in response phase hook, the response headers are
 filled with the ones received from backend server.  To send
index d040cbf..5018df0 100644 (file)
@@ -121,12 +121,15 @@ Connections
     The  parameters are  delimited  by  ";".  The  available
     parameters       are:      "proto=<PROTO>",       "tls",
     "sni=<SNI_HOST>",         "fall=<N>",        "rise=<N>",
-    "affinity=<METHOD>",  "dns", and  "redirect-if-not-tls".
-    The  parameter  consists   of  keyword,  and  optionally
-    followed by  "=" and value.  For  example, the parameter
-    "proto=h2"  consists of  the keyword  "proto" and  value
-    "h2".  The parameter "tls" consists of the keyword "tls"
-    without value.  Each parameter is described as follows.
+    "affinity=<METHOD>",    "dns",    "redirect-if-not-tls",
+    "upgrade-scheme",                        "mruby=<PATH>",
+    "read-timeout=<DURATION>",                           and
+    "write-timeout=<DURATION>".   The parameter  consists of
+    keyword, and optionally followed  by "=" and value.  For
+    example,  the  parameter   "proto=h2"  consists  of  the
+    keyword  "proto" and  value "h2".   The parameter  "tls"
+    consists  of  the  keyword "tls"  without  value.   Each
+    parameter is described as follows.
 
     The backend application protocol  can be specified using
     optional  "proto"   parameter,  and   in  the   form  of
@@ -219,6 +222,19 @@ Connections
     server  which  requires  "https" :scheme  pseudo  header
     field on TLS encrypted connection.
 
+    "mruby=<PATH>"  parameter  specifies  a  path  to  mruby
+    script  file  which  is  invoked when  this  pattern  is
+    matched.  All backends which share the same pattern must
+    have the same mruby path.
+
+    "read-timeout=<DURATION>" and "write-timeout=<DURATION>"
+    parameters  specify the  read and  write timeout  of the
+    backend connection  when this  pattern is  matched.  All
+    backends which share the same pattern must have the same
+    timeouts.  If these timeouts  are entirely omitted for a
+    pattern,            :option:`--backend-read-timeout`           and
+    :option:`--backend-write-timeout` are used.
+
     Since ";" and ":" are  used as delimiter, <PATTERN> must
     not  contain these  characters.  Since  ";" has  special
     meaning in shell, the option value must be quoted.
@@ -553,16 +569,38 @@ SSL/TLS
 
     Set allowed  cipher list  for frontend  connection.  The
     format of the string is described in OpenSSL ciphers(1).
+    This option  sets cipher suites for  TLSv1.2 or earlier.
+    Use :option:`--tls13-ciphers` for TLSv1.3.
 
     Default: ``ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256``
 
+.. option:: --tls13-ciphers=<SUITE>
+
+    Set allowed  cipher list  for frontend  connection.  The
+    format of the string is described in OpenSSL ciphers(1).
+    This  option  sets  cipher   suites  for  TLSv1.3.   Use
+    :option:`--ciphers` for TLSv1.2 or earlier.
+
+    Default: ``TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256``
+
 .. option:: --client-ciphers=<SUITE>
 
     Set  allowed cipher  list for  backend connection.   The
     format of the string is described in OpenSSL ciphers(1).
+    This option  sets cipher suites for  TLSv1.2 or earlier.
+    Use :option:`--tls13-client-ciphers` for TLSv1.3.
 
     Default: ``ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256``
 
+.. option:: --tls13-client-ciphers=<SUITE>
+
+    Set  allowed cipher  list for  backend connection.   The
+    format of the string is described in OpenSSL ciphers(1).
+    This  option  sets  cipher   suites  for  TLSv1.3.   Use
+    :option:`--tls13-client-ciphers` for TLSv1.2 or earlier.
+
+    Default: ``TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256``
+
 .. option:: --ecdh-curves=<LIST>
 
     Set  supported  curve  list  for  frontend  connections.
@@ -673,7 +711,7 @@ SSL/TLS
     ciphers are  included in :option:`--ciphers` option.   The default
     cipher  list  only   includes  ciphers  compatible  with
     TLSv1.2 or above.  The available versions are:
-    TLSv1.2, TLSv1.1, and TLSv1.0
+    TLSv1.3, TLSv1.2, TLSv1.1, and TLSv1.0
 
     Default: ``TLSv1.2``
 
@@ -685,9 +723,9 @@ SSL/TLS
     enabled.  If the protocol list advertised by client does
     not  overlap  this range,  you  will  receive the  error
     message "unknown protocol".  The available versions are:
-    TLSv1.2, TLSv1.1, and TLSv1.0
+    TLSv1.3, TLSv1.2, TLSv1.1, and TLSv1.0
 
-    Default: ``TLSv1.2``
+    Default: ``TLSv1.3``
 
 .. option:: --tls-ticket-key-file=<PATH>
 
@@ -915,6 +953,22 @@ SSL/TLS
     consider   to  use   :option:`--client-no-http2-cipher-black-list`
     option.  But be aware its implications.
 
+.. option:: --tls-no-postpone-early-data
+
+    By default,  nghttpx postpones forwarding  HTTP requests
+    sent in early data, including those sent in partially in
+    it, until TLS handshake finishes.  If all backend server
+    recognizes "Early-Data" header  field, using this option
+    makes nghttpx  not postpone  forwarding request  and get
+    full potential of 0-RTT data.
+
+.. option:: --tls-max-early-data=<SIZE>
+
+    Sets  the  maximum  amount  of 0-RTT  data  that  server
+    accepts.
+
+    Default: ``16K``
+
 
 HTTP/2
 ~~~~~~
@@ -1231,6 +1285,11 @@ HTTP
     Don't append to  Via header field.  If  Via header field
     is received, it is left unaltered.
 
+.. option:: --no-strip-incoming-early-data
+
+    Don't strip Early-Data header  field from inbound client
+    requests.
+
 .. option:: --no-location-rewrite
 
     Don't  rewrite location  header field  in default  mode.
@@ -1439,6 +1498,12 @@ Scripting
 
     Set mruby script file
 
+.. option:: --ignore-per-pattern-mruby-error
+
+    Ignore mruby compile error  for per-pattern mruby script
+    file.  If error  occurred, it is treated as  if no mruby
+    file were specified for the pattern.
+
 
 Misc
 ~~~~
@@ -1777,9 +1842,28 @@ server.  These hooks allows users to modify header fields, or common
 HTTP variables, like authority or request path, and even return custom
 response without forwarding request to backend servers.
 
-To specify mruby script file, use :option:`--mruby-file` option.  The
-script will be evaluated once per thread on startup, and it must
-instantiate object and evaluate it as the return value (e.g.,
+There are 2 levels of mruby script invocations: global and
+per-pattern.  The global mruby script is set by :option:`--mruby-file`
+option and is called for all requests.  The per-pattern mruby script
+is set by "mruby" parameter in :option:`-b` option.  It is invoked for
+a request which matches the particular pattern.  The order of hook
+invocation is: global request phase hook, per-pattern request phase
+hook, per-pattern response phase hook, and finally global response
+phase hook.  If a hook returns a response, any later hooks are not
+invoked.  The global request hook is invoked before the pattern
+matching is made and changing request path may affect the pattern
+matching.
+
+Please note that request and response hooks of per-pattern mruby
+script for a single request might not come from the same script.  This
+might happen after a request hook is executed, backend failed for some
+reason, and at the same time, backend configuration is replaced by API
+request, and then the request uses new configuration on retry.  The
+response hook from new configuration, if it is specified, will be
+invoked.
+
+The all mruby script will be evaluated once per thread on startup, and
+it must instantiate object and evaluate it as the return value (e.g.,
 ``App.new``).  This object is called app object.  If app object
 defines ``on_req`` method, it is called with :rb:class:`Nghttpx::Env`
 object on request hook.  Similarly, if app object defines ``on_resp``
@@ -1896,6 +1980,14 @@ respectively.
 
         Return ALPN identifier negotiated in this connection.
 
+    .. rb:attr_reader:: tls_handshake_finished
+
+        Return true if SSL/TLS handshake has finished.  If it returns
+        false in the request phase hook, the request is received in
+        TLSv1.3 early data (0-RTT) and might be vulnerable to the
+        replay attack.  nghttpx will send Early-Data header field to
+        backend servers to indicate this.
+
 .. rb:class:: Request
 
     Object to represent request from client.  The modification to
@@ -2026,10 +2118,10 @@ respectively.
         not be invoked.  When this method is called in response phase
         hook, response from backend server is canceled and discarded.
         The status code and response header fields should be set
-        before using this method.  To set status code, use :rb:meth To
-        set response header fields, use
+        before using this method.  To set status code, use
         :rb:attr:`Nghttpx::Response#status`.  If status code is not
-        set, 200 is used.  :rb:meth:`Nghttpx::Response#add_header` and
+        set, 200 is used.  To set response header fields,
+        :rb:meth:`Nghttpx::Response#add_header` and
         :rb:meth:`Nghttpx::Response#set_header`.  When this method is
         invoked in response phase hook, the response headers are
         filled with the ones received from backend server.  To send
index e5aa7ee..44edcbf 100644 (file)
@@ -110,9 +110,9 @@ HTTP Messaging
 
 By default, nghttp2 library checks HTTP messaging rules described in
 `HTTP/2 specification, section 8
-<https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-8>`_.
-Everything described in that section is not validated however.  We
-briefly describe what the library does in this area.  In the following
+<https://tools.ietf.org/html/rfc7540#section-8>`_.  Everything
+described in that section is not validated however.  We briefly
+describe what the library does in this area.  In the following
 description, without loss of generality we omit CONTINUATION frame
 since they must follow HEADERS frame and are processed atomically.  In
 other words, they are just one big HEADERS frame.  To disable these
@@ -249,7 +249,7 @@ set to :type:`nghttp2_session_callbacks` using
 `nghttp2_session_callbacks_set_pack_extension_callback()`.
 
 For example, we will illustrate how to send `ALTSVC
-<https://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-14>`_ frame.
+<https://tools.ietf.org/html/rfc7838>`_ frame.
 
 .. code-block:: c
 
index ffb2911..dc814a3 100644 (file)
@@ -26,7 +26,7 @@ Coding style
 We use clang-format to format source code consistently.  The
 clang-format configuration file .clang-format is located at the root
 directory.  Since clang-format produces slightly different results
-between versions, we currently use clang-format-5.0.
+between versions, we currently use clang-format-6.0.
 
 To detect any violation to the coding style, we recommend to setup git
 pre-commit hook to check coding style of the changes you introduced.
@@ -34,7 +34,7 @@ The pre-commit file is located at the root directory.  Copy it under
 .git/hooks and make sure that it is executable.  The pre-commit script
 uses clang-format-diff.py to detect any style errors.  If it is not in
 your PATH or it exists under different name (e.g.,
-clang-format-diff-5.0 in debian), either add it to PATH variable or
+clang-format-diff-6.0 in debian), either add it to PATH variable or
 add git option ``clangformatdiff.binary`` to point to the script.
 
 For emacs users, integrating clang-format to emacs is very easy.
index 6acb369..40c74e6 100644 (file)
@@ -471,6 +471,33 @@ such PSK cipher suite with HTTP/2, disable HTTP/2 cipher black list by
 using :option:`--client-no-http2-cipher-black-list` option.  But you
 should understand its implications.
 
+TLSv1.3
+-------
+
+As of nghttpx v1.34.0, if it is built with OpenSSL 1.1.1 or later, it
+supports TLSv1.3.  0-RTT data is supported, but by default its
+processing is postponed until TLS handshake completes to mitigate
+replay attack.  This costs extra round trip and reduces effectiveness
+of 0-RTT data.  :option:`--tls-no-postpone-early-data` makes nghttpx
+not wait for handshake to complete before forwarding request included
+in 0-RTT to get full potential of 0-RTT data.  In this case, nghttpx
+adds ``Early-Data: 1`` header field when forwarding a request to a
+backend server.  All backend servers should recognize this header
+field and understand that there is a risk for replay attack.  See `RFC
+8470 <https://tools.ietf.org/html/rfc8470>`_ for ``Early-Data`` header
+field.
+
+nghttpx disables anti replay protection provided by OpenSSL.  The anti
+replay protection of OpenSSL requires that a resumed request must hit
+the same server which generates the session ticket.  Therefore it
+might not work nicely in a deployment where there are multiple nghttpx
+instances sharing ticket encryption keys via memcached.
+
+Because TLSv1.3 completely changes the semantics of cipher suite
+naming scheme and structure, nghttpx provides the new option
+:option:`--tls13-ciphers` and :option:`--tls13-client-ciphers` to
+change preferred cipher list for TLSv1.3.
+
 Migration from nghttpx v1.18.x or earlier
 -----------------------------------------
 
index 5a78fde..801ac18 100644 (file)
@@ -1194,6 +1194,41 @@ Types (structs, unions and typedefs)
 
         The length of the *field_value*.
 
+.. type:: nghttp2_origin_entry
+
+    
+    The single entry of an origin.
+
+    .. member::   uint8_t *origin
+
+        The pointer to origin.  No validation is made against this field
+        by the library.  This is not necessarily NULL-terminated.
+    .. member::   size_t origin_len
+
+        The length of the *origin*.
+
+.. type:: nghttp2_ext_origin
+
+    
+    The payload of ORIGIN frame.  ORIGIN frame is a non-critical
+    extension to HTTP/2 and defined by `RFC 8336
+    <https://tools.ietf.org/html/rfc8336>`_.
+    
+    If this frame is received, and
+    `nghttp2_option_set_user_recv_extension_type()` is not set, and
+    `nghttp2_option_set_builtin_recv_extension_type()` is set for
+    :macro:`NGHTTP2_ORIGIN`, ``nghttp2_extension.payload`` will point to
+    this struct.
+    
+    It has the following members:
+
+    .. member::   size_t nov
+
+        The number of origins contained in *ov*.
+    .. member::   nghttp2_origin_entry *ov
+
+        The pointer to the array of origins contained in ORIGIN frame.
+
 .. type:: nghttp2_hd_deflater
 
     
index 57dbd55..eb5d825 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -214,7 +214,13 @@ am__v_at_0 = @
 am__v_at_1 = 
 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/asio_cl-asio-cl.Po \
+       ./$(DEPDIR)/asio_cl2-asio-cl2.Po \
+       ./$(DEPDIR)/asio_sv-asio-sv.Po \
+       ./$(DEPDIR)/asio_sv2-asio-sv2.Po ./$(DEPDIR)/client.Po \
+       ./$(DEPDIR)/deflate.Po ./$(DEPDIR)/libevent-client.Po \
+       ./$(DEPDIR)/libevent-server.Po
 am__mv = mv -f
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
        $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -537,8 +543,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
          *config.status*) \
            cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
          *) \
-           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
-           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
        esac;
 
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -597,14 +603,20 @@ mostlyclean-compile:
 distclean-compile:
        -rm -f *.tab.c
 
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_cl-asio-cl.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_cl2-asio-cl2.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_sv-asio-sv.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_sv2-asio-sv2.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/deflate.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libevent-client.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libevent-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_cl-asio-cl.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_cl2-asio-cl2.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_sv-asio-sv.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asio_sv2-asio-sv2.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/deflate.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libevent-client.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libevent-server.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+       @$(MKDIR_P) $(@D)
+       @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
 
 .c.o:
 @am__fastdepCC_TRUE@   $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
@@ -768,7 +780,10 @@ cscopelist-am: $(am__tagged_files)
 distclean-tags:
        -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
 
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
        @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        list='$(DISTFILES)'; \
@@ -838,7 +853,14 @@ clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
        mostlyclean-am
 
 distclean: distclean-am
-       -rm -rf ./$(DEPDIR)
+               -rm -f ./$(DEPDIR)/asio_cl-asio-cl.Po
+       -rm -f ./$(DEPDIR)/asio_cl2-asio-cl2.Po
+       -rm -f ./$(DEPDIR)/asio_sv-asio-sv.Po
+       -rm -f ./$(DEPDIR)/asio_sv2-asio-sv2.Po
+       -rm -f ./$(DEPDIR)/client.Po
+       -rm -f ./$(DEPDIR)/deflate.Po
+       -rm -f ./$(DEPDIR)/libevent-client.Po
+       -rm -f ./$(DEPDIR)/libevent-server.Po
        -rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
        distclean-tags
@@ -884,7 +906,14 @@ install-ps-am:
 installcheck-am:
 
 maintainer-clean: maintainer-clean-am
-       -rm -rf ./$(DEPDIR)
+               -rm -f ./$(DEPDIR)/asio_cl-asio-cl.Po
+       -rm -f ./$(DEPDIR)/asio_cl2-asio-cl2.Po
+       -rm -f ./$(DEPDIR)/asio_sv-asio-sv.Po
+       -rm -f ./$(DEPDIR)/asio_sv2-asio-sv2.Po
+       -rm -f ./$(DEPDIR)/client.Po
+       -rm -f ./$(DEPDIR)/deflate.Po
+       -rm -f ./$(DEPDIR)/libevent-client.Po
+       -rm -f ./$(DEPDIR)/libevent-server.Po
        -rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
@@ -905,9 +934,9 @@ uninstall-am:
 
 .MAKE: install-am install-strip
 
-.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
-       clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \
-       ctags-am distclean distclean-compile distclean-generic \
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+       clean-generic clean-libtool clean-noinstPROGRAMS cscopelist-am \
+       ctags ctags-am distclean distclean-compile distclean-generic \
        distclean-libtool distclean-tags distdir dvi dvi-am html \
        html-am info info-am install install-am install-data \
        install-data-am install-dvi install-dvi-am install-exec \
index c7d7bb1..81b6274 100644 (file)
@@ -67,14 +67,14 @@ int main(int argc, char *argv[]) {
         return;
       }
 
-      req->on_response([&sess](const response &res) {
+      req->on_response([](const response &res) {
         std::cerr << "HTTP/2 " << res.status_code() << std::endl;
         for (auto &kv : res.header()) {
           std::cerr << kv.first << ": " << kv.second.value << "\n";
         }
         std::cerr << std::endl;
 
-        res.on_data([&sess](const uint8_t *data, std::size_t len) {
+        res.on_data([](const uint8_t *data, std::size_t len) {
           std::cerr.write(reinterpret_cast<const char *>(data), len);
           std::cerr << std::endl;
         });
index 2a8ed71..5f6598b 100644 (file)
@@ -91,17 +91,17 @@ int main(int argc, char *argv[]) {
         return;
       }
 
-      req->on_response([&sess, req](const response &res) {
+      req->on_response([](const response &res) {
         std::cerr << "response header was received" << std::endl;
         print_header(res);
 
-        res.on_data([&sess](const uint8_t *data, std::size_t len) {
+        res.on_data([](const uint8_t *data, std::size_t len) {
           std::cerr.write(reinterpret_cast<const char *>(data), len);
           std::cerr << std::endl;
         });
       });
 
-      req->on_close([&sess](uint32_t error_code) {
+      req->on_close([](uint32_t error_code) {
         std::cerr << "request done with error_code=" << error_code << std::endl;
       });
 
index 8d2580e..17ea02b 100644 (file)
 #include <sys/types.h>
 #include <sys/stat.h>
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 #ifdef HAVE_FCNTL_H
-#include <fcntl.h>
+#  include <fcntl.h>
 #endif // HAVE_FCNTL_H
 #include <iostream>
 #include <string>
index bb6f181..2d3c4c5 100644 (file)
  * intentionally made simple.
  */
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <inttypes.h>
 #include <stdlib.h>
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif /* HAVE_UNISTD_H */
 #ifdef HAVE_FCNTL_H
-#include <fcntl.h>
+#  include <fcntl.h>
 #endif /* HAVE_FCNTL_H */
 #include <sys/types.h>
 #ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
+#  include <sys/socket.h>
 #endif /* HAVE_SYS_SOCKET_H */
 #ifdef HAVE_NETDB_H
-#include <netdb.h>
+#  include <netdb.h>
 #endif /* HAVE_NETDB_H */
 #ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
+#  include <netinet/in.h>
 #endif /* HAVE_NETINET_IN_H */
 #include <netinet/tcp.h>
 #include <poll.h>
@@ -345,6 +345,7 @@ static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks) {
       callbacks, on_data_chunk_recv_callback);
 }
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
 /*
  * Callback function for TLS NPN. Since this program only supports
  * HTTP/2 protocol, if server does not offer HTTP/2 the nghttp2
@@ -365,6 +366,7 @@ static int select_next_proto_cb(SSL *ssl, unsigned char **out,
   }
   return SSL_TLSEXT_ERR_OK;
 }
+#endif /* !OPENSSL_NO_NEXTPROTONEG */
 
 /*
  * Setup SSL/TLS context.
@@ -375,7 +377,9 @@ static void init_ssl_ctx(SSL_CTX *ssl_ctx) {
   SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
   SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
   /* Set NPN callback */
+#ifndef OPENSSL_NO_NEXTPROTONEG
   SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
+#endif /* !OPENSSL_NO_NEXTPROTONEG */
 }
 
 static void ssl_handshake(SSL *ssl, int fd) {
index eb71362..63f3ea1 100644 (file)
@@ -23,7 +23,7 @@
  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* !HAVE_CONFIG_H */
 
 #include <stdio.h>
index bfee21e..22b46da 100644 (file)
  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 #ifdef __sgi
-#include <string.h>
-#define errx(exitcode, format, args...)                                        \
-  {                                                                            \
-    warnx(format, ##args);                                                     \
-    exit(exitcode);                                                            \
-  }
-#define warnx(format, args...) fprintf(stderr, format "\n", ##args)
+#  include <string.h>
+#  define errx(exitcode, format, args...)                                      \
+    {                                                                          \
+      warnx(format, ##args);                                                   \
+      exit(exitcode);                                                          \
+    }
+#  define warnx(format, args...) fprintf(stderr, format "\n", ##args)
 char *strndup(const char *s, size_t size);
 #endif
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <sys/types.h>
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif /* HAVE_UNISTD_H */
 #ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
+#  include <sys/socket.h>
 #endif /* HAVE_SYS_SOCKET_H */
 #ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
+#  include <netinet/in.h>
 #endif /* HAVE_NETINET_IN_H */
 #include <netinet/tcp.h>
 #ifndef __sgi
-#include <err.h>
+#  include <err.h>
 #endif
 #include <signal.h>
 #include <string.h>
@@ -308,6 +308,7 @@ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
   return 0;
 }
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
 /* NPN TLS extension client callback. We check that server advertised
    the HTTP/2 protocol the nghttp2 library supports. If not, exit
    the program. */
@@ -322,6 +323,7 @@ static int select_next_proto_cb(SSL *ssl, unsigned char **out,
   }
   return SSL_TLSEXT_ERR_OK;
 }
+#endif /* !OPENSSL_NO_NEXTPROTONEG */
 
 /* Create SSL_CTX. */
 static SSL_CTX *create_ssl_ctx(void) {
@@ -335,11 +337,13 @@ static SSL_CTX *create_ssl_ctx(void) {
                       SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
                           SSL_OP_NO_COMPRESSION |
                           SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
+#ifndef OPENSSL_NO_NEXTPROTONEG
   SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
+#endif /* !OPENSSL_NO_NEXTPROTONEG */
 
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
   SSL_CTX_set_alpn_protos(ssl_ctx, (const unsigned char *)"\x02h2", 3);
-#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
 
   return ssl_ctx;
 }
@@ -504,12 +508,14 @@ static void eventcb(struct bufferevent *bev, short events, void *ptr) {
 
     ssl = bufferevent_openssl_get_ssl(session_data->bev);
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
     SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen);
+#endif /* !OPENSSL_NO_NEXTPROTONEG */
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
     if (alpn == NULL) {
       SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
     }
-#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
 
     if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) {
       fprintf(stderr, "h2 is not negotiated\n");
index 403d2dd..a553617 100644 (file)
  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 #ifdef __sgi
-#define errx(exitcode, format, args...)                                        \
-  {                                                                            \
-    warnx(format, ##args);                                                     \
-    exit(exitcode);                                                            \
-  }
-#define warn(format, args...) warnx(format ": %s", ##args, strerror(errno))
-#define warnx(format, args...) fprintf(stderr, format "\n", ##args)
+#  define errx(exitcode, format, args...)                                      \
+    {                                                                          \
+      warnx(format, ##args);                                                   \
+      exit(exitcode);                                                          \
+    }
+#  define warn(format, args...) warnx(format ": %s", ##args, strerror(errno))
+#  define warnx(format, args...) fprintf(stderr, format "\n", ##args)
 #endif
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <sys/types.h>
 #ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
+#  include <sys/socket.h>
 #endif /* HAVE_SYS_SOCKET_H */
 #ifdef HAVE_NETDB_H
-#include <netdb.h>
+#  include <netdb.h>
 #endif /* HAVE_NETDB_H */
 #include <signal.h>
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif /* HAVE_UNISTD_H */
 #include <sys/stat.h>
 #ifdef HAVE_FCNTL_H
-#include <fcntl.h>
+#  include <fcntl.h>
 #endif /* HAVE_FCNTL_H */
 #include <ctype.h>
 #ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
+#  include <netinet/in.h>
 #endif /* HAVE_NETINET_IN_H */
 #include <netinet/tcp.h>
 #ifndef __sgi
-#include <err.h>
+#  include <err.h>
 #endif
 #include <string.h>
 #include <errno.h>
@@ -109,6 +109,7 @@ struct app_context {
 static unsigned char next_proto_list[256];
 static size_t next_proto_list_len;
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
 static int next_proto_cb(SSL *ssl, const unsigned char **data,
                          unsigned int *len, void *arg) {
   (void)ssl;
@@ -118,6 +119,7 @@ static int next_proto_cb(SSL *ssl, const unsigned char **data,
   *len = (unsigned int)next_proto_list_len;
   return SSL_TLSEXT_ERR_OK;
 }
+#endif /* !OPENSSL_NO_NEXTPROTONEG */
 
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
 static int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
@@ -135,7 +137,7 @@ static int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
 
   return SSL_TLSEXT_ERR_OK;
 }
-#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
 
 /* Create SSL_CTX. */
 static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
@@ -172,11 +174,13 @@ static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
          NGHTTP2_PROTO_VERSION_ID_LEN);
   next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN;
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
   SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL);
+#endif /* !OPENSSL_NO_NEXTPROTONEG */
 
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
   SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, NULL);
-#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
 
   return ssl_ctx;
 }
@@ -690,12 +694,14 @@ static void eventcb(struct bufferevent *bev, short events, void *ptr) {
 
     ssl = bufferevent_openssl_get_ssl(session_data->bev);
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
     SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen);
+#endif /* !OPENSSL_NO_NEXTPROTONEG */
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
     if (alpn == NULL) {
       SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
     }
-#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
 
     if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) {
       fprintf(stderr, "%s h2 is not negotiated\n", session_data->client_addr);
index 59990a1..8175c64 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 # install - install a program, script, or datafile
 
-scriptversion=2014-09-12.12; # UTC
+scriptversion=2018-03-11.20; # UTC
 
 # This originates from X11R5 (mit/util/scripts/install.sh), which was
 # later released in X11R6 (xc/config/util/install.sh) with the
@@ -271,15 +271,18 @@ do
     fi
     dst=$dst_arg
 
-    # If destination is a directory, append the input filename; won't work
-    # if double slashes aren't ignored.
+    # If destination is a directory, append the input filename.
     if test -d "$dst"; then
       if test "$is_target_a_directory" = never; then
         echo "$0: $dst_arg: Is a directory" >&2
         exit 1
       fi
       dstdir=$dst
-      dst=$dstdir/`basename "$src"`
+      dstbase=`basename "$src"`
+      case $dst in
+       */) dst=$dst$dstbase;;
+       *)  dst=$dst/$dstbase;;
+      esac
       dstdir_status=0
     else
       dstdir=`dirname "$dst"`
@@ -288,6 +291,11 @@ do
     fi
   fi
 
+  case $dstdir in
+    */) dstdirslash=$dstdir;;
+    *)  dstdirslash=$dstdir/;;
+  esac
+
   obsolete_mkdir_used=false
 
   if test $dstdir_status != 0; then
@@ -324,14 +332,16 @@ do
             # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
             ;;
           *)
-            # $RANDOM is not portable (e.g. dash);  use it when possible to
-            # lower collision chance
+            # Note that $RANDOM variable is not portable (e.g. dash);  Use it
+            # here however when possible just to lower collision chance.
             tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+
             trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0
 
-            # As "mkdir -p" follows symlinks and we work in /tmp possibly;  so
-            # create the $tmpdir first (and fail if unsuccessful) to make sure
-            # that nobody tries to guess the $tmpdir name.
+            # Because "mkdir -p" follows existing symlinks and we likely work
+            # directly in world-writeable /tmp, make sure that the '$tmpdir'
+            # directory is successfully created first before we actually test
+            # 'mkdir -p' feature.
             if (umask $mkdir_umask &&
                 $mkdirprog $mkdir_mode "$tmpdir" &&
                 exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
@@ -434,8 +444,8 @@ do
   else
 
     # Make a couple of temp file names in the proper directory.
-    dsttmp=$dstdir/_inst.$$_
-    rmtmp=$dstdir/_rm.$$_
+    dsttmp=${dstdirslash}_inst.$$_
+    rmtmp=${dstdirslash}_rm.$$_
 
     # Trap to clean up those temp files at exit.
     trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
@@ -500,9 +510,9 @@ do
 done
 
 # Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
+# eval: (add-hook 'before-save-hook 'time-stamp)
 # time-stamp-start: "scriptversion="
 # time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-time-zone: "UTC"
+# time-stamp-time-zone: "UTC0"
 # time-stamp-end: "; # UTC"
 # End:
index bae2d2d..d9385a5 100644 (file)
@@ -1,7 +1,6 @@
 set(GO_FILES
   nghttpx_http1_test.go
   nghttpx_http2_test.go
-  nghttpx_spdy_test.go
   server_tester.go
 )
 
@@ -22,7 +21,6 @@ set(EXTRA_DIST
 add_custom_target(itprep
   COMMAND go get -d -v golang.org/x/net/http2
   COMMAND go get -d -v github.com/tatsuhiro-t/go-nghttp2
-  COMMAND go get -d -v github.com/tatsuhiro-t/spdy
   COMMAND go get -d -v golang.org/x/net/websocket
 )
 
index 8fe1071..21e741d 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -371,8 +371,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
          *config.status*) \
            cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
          *) \
-           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
-           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
        esac;
 
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -400,7 +400,10 @@ ctags CTAGS:
 cscope cscopelist:
 
 
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
        @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        list='$(DISTFILES)'; \
index d145519..d463131 100644 (file)
@@ -7,7 +7,6 @@ import (
        "errors"
        "fmt"
        "github.com/tatsuhiro-t/go-nghttp2"
-       "github.com/tatsuhiro-t/spdy"
        "golang.org/x/net/http2"
        "golang.org/x/net/http2/hpack"
        "golang.org/x/net/websocket"
@@ -53,14 +52,12 @@ type serverTester struct {
        h2PrefaceSent bool             // HTTP/2 preface was sent in conn
        nextStreamID  uint32           // next stream ID
        fr            *http2.Framer    // HTTP/2 framer
-       spdyFr        *spdy.Framer     // SPDY/3.1 framer
        headerBlkBuf  bytes.Buffer     // buffer to store encoded header block
        enc           *hpack.Encoder   // HTTP/2 HPACK encoder
        header        http.Header      // received header fields
        dec           *hpack.Decoder   // HTTP/2 HPACK decoder
        authority     string           // server's host:port
        frCh          chan http2.Frame // used for incoming HTTP/2 frame
-       spdyFrCh      chan spdy.Frame  // used for incoming SPDY frame
        errCh         chan error
 }
 
@@ -201,7 +198,6 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
                nextStreamID: 1,
                authority:    authority,
                frCh:         make(chan http2.Frame),
-               spdyFrCh:     make(chan spdy.Frame),
                errCh:        make(chan error),
        }
 
@@ -229,7 +225,7 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
                        if alpnH1 {
                                tlsConfig.NextProtos = []string{"http/1.1"}
                        } else {
-                               tlsConfig.NextProtos = []string{"h2", "spdy/3.1"}
+                               tlsConfig.NextProtos = []string{"h2"}
                        }
                        conn, err = tls.Dial("tcp", authority, tlsConfig)
                } else {
@@ -256,12 +252,6 @@ func newServerTesterInternal(src_args []string, t *testing.T, handler http.Handl
        }
 
        st.fr = http2.NewFramer(st.conn, st.conn)
-       spdyFr, err := spdy.NewFramer(st.conn, st.conn)
-       if err != nil {
-               st.Close()
-               st.t.Fatalf("Error spdy.NewFramer: %v", err)
-       }
-       st.spdyFr = spdyFr
        st.enc = hpack.NewEncoder(&st.headerBlkBuf)
        st.dec = hpack.NewDecoder(4096, func(f hpack.HeaderField) {
                st.header.Add(f.Name, f.Value)
@@ -315,26 +305,6 @@ func (st *serverTester) readFrame() (http2.Frame, error) {
        }
 }
 
-func (st *serverTester) readSpdyFrame() (spdy.Frame, error) {
-       go func() {
-               f, err := st.spdyFr.ReadFrame()
-               if err != nil {
-                       st.errCh <- err
-                       return
-               }
-               st.spdyFrCh <- f
-       }()
-
-       select {
-       case f := <-st.spdyFrCh:
-               return f, nil
-       case err := <-st.errCh:
-               return nil, err
-       case <-time.After(2 * time.Second):
-               return nil, errors.New("timeout waiting for frame")
-       }
-}
-
 type requestParam struct {
        name        string              // name for this request to identify the request in log easily
        streamID    uint32              // stream ID, automatically assigned if 0
@@ -475,122 +445,6 @@ func (st *serverTester) http1(rp requestParam) (*serverResponse, error) {
        return res, nil
 }
 
-func (st *serverTester) spdy(rp requestParam) (*serverResponse, error) {
-       res := &serverResponse{}
-
-       var id spdy.StreamId
-       if rp.streamID != 0 {
-               id = spdy.StreamId(rp.streamID)
-               if id >= spdy.StreamId(st.nextStreamID) && id%2 == 1 {
-                       st.nextStreamID = uint32(id) + 2
-               }
-       } else {
-               id = spdy.StreamId(st.nextStreamID)
-               st.nextStreamID += 2
-       }
-
-       method := "GET"
-       if rp.method != "" {
-               method = rp.method
-       }
-
-       scheme := "http"
-       if rp.scheme != "" {
-               scheme = rp.scheme
-       }
-
-       host := st.authority
-       if rp.authority != "" {
-               host = rp.authority
-       }
-
-       path := "/"
-       if rp.path != "" {
-               path = rp.path
-       }
-
-       header := make(http.Header)
-       header.Add(":method", method)
-       header.Add(":scheme", scheme)
-       header.Add(":host", host)
-       header.Add(":path", path)
-       header.Add(":version", "HTTP/1.1")
-       header.Add("test-case", rp.name)
-       for _, h := range rp.header {
-               header.Add(h.Name, h.Value)
-       }
-
-       var synStreamFlags spdy.ControlFlags
-       if len(rp.body) == 0 && !rp.noEndStream {
-               synStreamFlags = spdy.ControlFlagFin
-       }
-       if err := st.spdyFr.WriteFrame(&spdy.SynStreamFrame{
-               CFHeader: spdy.ControlFrameHeader{
-                       Flags: synStreamFlags,
-               },
-               StreamId: id,
-               Headers:  header,
-       }); err != nil {
-               return nil, err
-       }
-
-       if len(rp.body) != 0 {
-               var dataFlags spdy.DataFlags
-               if !rp.noEndStream {
-                       dataFlags = spdy.DataFlagFin
-               }
-               if err := st.spdyFr.WriteFrame(&spdy.DataFrame{
-                       StreamId: id,
-                       Flags:    dataFlags,
-                       Data:     rp.body,
-               }); err != nil {
-                       return nil, err
-               }
-       }
-
-loop:
-       for {
-               fr, err := st.readSpdyFrame()
-               if err != nil {
-                       return res, err
-               }
-               switch f := fr.(type) {
-               case *spdy.SynReplyFrame:
-                       if f.StreamId != id {
-                               break
-                       }
-                       res.header = cloneHeader(f.Headers)
-                       if _, err := fmt.Sscan(res.header.Get(":status"), &res.status); err != nil {
-                               return res, fmt.Errorf("Error parsing status code: %v", err)
-                       }
-                       if f.CFHeader.Flags&spdy.ControlFlagFin != 0 {
-                               break loop
-                       }
-               case *spdy.DataFrame:
-                       if f.StreamId != id {
-                               break
-                       }
-                       res.body = append(res.body, f.Data...)
-                       if f.Flags&spdy.DataFlagFin != 0 {
-                               break loop
-                       }
-               case *spdy.RstStreamFrame:
-                       if f.StreamId != id {
-                               break
-                       }
-                       res.spdyRstErrCode = f.Status
-                       break loop
-               case *spdy.GoAwayFrame:
-                       if f.Status == spdy.GoAwayOK {
-                               break
-                       }
-                       res.spdyGoAwayErrCode = f.Status
-                       break loop
-               }
-       }
-       return res, nil
-}
-
 func (st *serverTester) http2(rp requestParam) (*serverResponse, error) {
        st.headerBlkBuf.Reset()
        st.header = make(http.Header)
@@ -779,8 +633,6 @@ type serverResponse struct {
        streamID          uint32               // stream ID in HTTP/2
        errCode           http2.ErrCode        // error code received in HTTP/2 RST_STREAM or GOAWAY
        connErr           bool                 // true if HTTP/2 connection error
-       spdyGoAwayErrCode spdy.GoAwayStatus    // status code received in SPDY RST_STREAM
-       spdyRstErrCode    spdy.RstStreamStatus // status code received in SPDY GOAWAY
        connClose         bool                 // Connection: close is included in response header in HTTP/1 test
        reqHeader         http.Header          // http request header, currently only sotres pushed request header
        pushResponse      []*serverResponse    // pushed response
index 0846d06..17e422b 100644 (file)
@@ -49,7 +49,7 @@ target_include_directories(nghttp2 INTERFACE
     "${CMAKE_CURRENT_SOURCE_DIR}/includes"
     )
 
-if(HAVE_CUNIT)
+if(HAVE_CUNIT OR ENABLE_STATIC_LIB)
   # Static library (for unittests because of symbol visibility)
   add_library(nghttp2_static STATIC ${NGHTTP2_SOURCES})
   set_target_properties(nghttp2_static PROPERTIES
@@ -58,6 +58,10 @@ if(HAVE_CUNIT)
     ARCHIVE_OUTPUT_NAME nghttp2
   )
   target_compile_definitions(nghttp2_static PUBLIC "-DNGHTTP2_STATICLIB")
+  if(ENABLE_STATIC_LIB)
+    install(TARGETS nghttp2_static
+      DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+  endif()
 endif()
 
 install(TARGETS nghttp2
index 372d0f1..7a8bc30 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -186,7 +186,21 @@ am__v_at_0 = @
 am__v_at_1 = 
 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/nghttp2_buf.Plo \
+       ./$(DEPDIR)/nghttp2_callbacks.Plo \
+       ./$(DEPDIR)/nghttp2_debug.Plo ./$(DEPDIR)/nghttp2_frame.Plo \
+       ./$(DEPDIR)/nghttp2_hd.Plo ./$(DEPDIR)/nghttp2_hd_huffman.Plo \
+       ./$(DEPDIR)/nghttp2_hd_huffman_data.Plo \
+       ./$(DEPDIR)/nghttp2_helper.Plo ./$(DEPDIR)/nghttp2_http.Plo \
+       ./$(DEPDIR)/nghttp2_map.Plo ./$(DEPDIR)/nghttp2_mem.Plo \
+       ./$(DEPDIR)/nghttp2_npn.Plo ./$(DEPDIR)/nghttp2_option.Plo \
+       ./$(DEPDIR)/nghttp2_outbound_item.Plo \
+       ./$(DEPDIR)/nghttp2_pq.Plo \
+       ./$(DEPDIR)/nghttp2_priority_spec.Plo \
+       ./$(DEPDIR)/nghttp2_queue.Plo ./$(DEPDIR)/nghttp2_rcbuf.Plo \
+       ./$(DEPDIR)/nghttp2_session.Plo ./$(DEPDIR)/nghttp2_stream.Plo \
+       ./$(DEPDIR)/nghttp2_submit.Plo ./$(DEPDIR)/nghttp2_version.Plo
 am__mv = mv -f
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
        $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -229,7 +243,7 @@ am__recursive_targets = \
   $(RECURSIVE_CLEAN_TARGETS) \
   $(am__extra_recursive_targets)
 AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
-       distdir
+       distdir distdir-am
 am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
 # Read a list of newline-separated strings from the standard input,
 # and print each of them once, without duplicates.  Input order is
@@ -534,8 +548,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
          *config.status*) \
            cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
          *) \
-           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
-           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
        esac;
 
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -593,28 +607,34 @@ mostlyclean-compile:
 distclean-compile:
        -rm -f *.tab.c
 
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_buf.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_callbacks.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_debug.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_frame.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_huffman.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_huffman_data.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_helper.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_http.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_map.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_mem.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_npn.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_option.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_outbound_item.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_pq.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_priority_spec.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_queue.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_rcbuf.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_session.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_stream.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_submit.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_version.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_buf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_callbacks.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_debug.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_frame.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_huffman.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_huffman_data.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_helper.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_http.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_map.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_mem.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_npn.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_option.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_outbound_item.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_pq.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_priority_spec.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_queue.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_rcbuf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_session.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_stream.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_submit.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_version.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+       @$(MKDIR_P) $(@D)
+       @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
 
 .c.o:
 @am__fastdepCC_TRUE@   $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
@@ -766,7 +786,10 @@ cscopelist-am: $(am__tagged_files)
 distclean-tags:
        -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
 
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
        @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        list='$(DISTFILES)'; \
@@ -866,7 +889,28 @@ clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
        mostlyclean-am
 
 distclean: distclean-recursive
-       -rm -rf ./$(DEPDIR)
+               -rm -f ./$(DEPDIR)/nghttp2_buf.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_callbacks.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_debug.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_frame.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_hd.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_hd_huffman.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_hd_huffman_data.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_helper.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_http.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_map.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_mem.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_npn.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_option.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_outbound_item.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_pq.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_priority_spec.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_queue.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_rcbuf.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_session.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_stream.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_submit.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_version.Plo
        -rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
        distclean-tags
@@ -912,7 +956,28 @@ install-ps-am:
 installcheck-am:
 
 maintainer-clean: maintainer-clean-recursive
-       -rm -rf ./$(DEPDIR)
+               -rm -f ./$(DEPDIR)/nghttp2_buf.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_callbacks.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_debug.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_frame.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_hd.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_hd_huffman.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_hd_huffman_data.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_helper.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_http.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_map.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_mem.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_npn.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_option.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_outbound_item.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_pq.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_priority_spec.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_queue.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_rcbuf.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_session.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_stream.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_submit.Plo
+       -rm -f ./$(DEPDIR)/nghttp2_version.Plo
        -rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
@@ -933,21 +998,21 @@ uninstall-am: uninstall-libLTLIBRARIES uninstall-pkgconfigDATA
 
 .MAKE: $(am__recursive_targets) install-am install-strip
 
-.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
-       check-am clean clean-generic clean-libLTLIBRARIES \
-       clean-libtool cscopelist-am ctags ctags-am distclean \
-       distclean-compile distclean-generic distclean-libtool \
-       distclean-tags distdir dvi dvi-am html html-am info info-am \
-       install install-am install-data install-data-am install-dvi \
-       install-dvi-am install-exec install-exec-am install-html \
-       install-html-am install-info install-info-am \
-       install-libLTLIBRARIES install-man install-pdf install-pdf-am \
-       install-pkgconfigDATA install-ps install-ps-am install-strip \
-       installcheck installcheck-am installdirs installdirs-am \
-       maintainer-clean maintainer-clean-generic mostlyclean \
-       mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
-       pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \
-       uninstall-libLTLIBRARIES uninstall-pkgconfigDATA
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+       am--depfiles check check-am clean clean-generic \
+       clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \
+       ctags-am distclean distclean-compile distclean-generic \
+       distclean-libtool distclean-tags distdir dvi dvi-am html \
+       html-am info info-am install install-am install-data \
+       install-data-am install-dvi install-dvi-am install-exec \
+       install-exec-am install-html install-html-am install-info \
+       install-info-am install-libLTLIBRARIES install-man install-pdf \
+       install-pdf-am install-pkgconfigDATA install-ps install-ps-am \
+       install-strip installcheck installcheck-am installdirs \
+       installdirs-am maintainer-clean maintainer-clean-generic \
+       mostlyclean mostlyclean-compile mostlyclean-generic \
+       mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+       uninstall-am uninstall-libLTLIBRARIES uninstall-pkgconfigDATA
 
 .PRECIOUS: Makefile
 
index 0077dc4..f649c0b 100644 (file)
-#
-# GNU Makefile for nghttp2 / MSVC.
-#
-# By G. Vanem <gvanem@yahoo.no> 2013
-# Updated 3/2015 by Remo Eichenberger @remoe
-# The MIT License apply.
-#
-
-#
-# Choose your weapons:
-#  Set 'USE_CYTHON=1' to build and install the 'nghttp2.pyd' Python extension.
-#
-THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST))
-
-USE_CYTHON := 0
-#USE_CYTHON := 1
-
-_VERSION       := $(shell grep AC_INIT ../configure.ac | cut -d'[' -f3 | sed -e 's/-DEV//g' -e 's/], //g')
-_VERSION       := $(subst ., ,$(_VERSION))
-VER_MAJOR      := $(word 1,$(_VERSION))
-VER_MINOR      := $(word 2,$(_VERSION))
-VER_MICRO   := $(word 3,$(_VERSION))
-VERSION     := $(VER_MAJOR).$(VER_MINOR).$(VER_MICRO)
-VERSION_NUM := (($(VER_MAJOR) << 16) + ($(VER_MINOR) << 8) + $(VER_MICRO))
-
-GENERATED   := 'Generated by $(realpath Makefile.MSVC)'
-
-OBJ_DIR := MSVC_obj
-#SUFFIX :=-vc90-mt-x86
-
-#
-# Where to copy nghttp2.dll + lib + headers to.
-# Note: 'make install' is not in default targets. Do it explicitly.
-#
-TARGET_DIR  ?= ../_VC_ROOT
-VC_ROOT     := $(abspath $(TARGET_DIR))
-INSTALL_BIN := $(VC_ROOT)/bin
-INSTALL_LIB := $(VC_ROOT)/lib
-INSTALL_HDR := $(VC_ROOT)/include
-DLL_R  := $(OBJ_DIR)/nghttp2$(SUFFIX).dll
-DLL_D  := $(OBJ_DIR)/nghttp2d$(SUFFIX).dll
-LIB_R  := $(OBJ_DIR)/nghttp2-static.lib
-LIB_D  := $(OBJ_DIR)/nghttp2d-static.lib
-IMP_R  := $(OBJ_DIR)/nghttp2.lib
-IMP_D  := $(OBJ_DIR)/nghttp2d.lib
-
-#
-# Build for DEBUG-model and RELEASE at the same time.
-#
-TARGETS := $(LIB_R) $(DLL_R) $(IMP_R) \
-          $(LIB_D) $(DLL_D) $(IMP_D)
-
-EXT_LIBS = 
-
-NGHTTP2_PDB_R := $(OBJ_DIR)/nghttp2.pdb
-NGHTTP2_PDB_D := $(OBJ_DIR)/nghttp2d.pdb
-
-CC       = cl
-LD              := link
-AR              := lib
-#CC       := icl
-#LD             := xilink
-#AR             := xilib
-RC              := rc
-CFLAGS   := -I./includes -Dssize_t=long
-
-CFLAGS_R := -nologo -MD  -W3 -Z7 -DBUILDING_NGHTTP2
-CFLAGS_D := -nologo -MDd -W3 -Z7 -DBUILDING_NGHTTP2 \
-           -Ot -D_DEBUG -GF -RTCs -RTCu # -RTCc -GS
-
-LDFLAGS := -nologo -MAP -debug -incremental:no -opt:ref,icf -MANIFEST # -verbose
-
-
-NGHTTP2_SRC := nghttp2_pq.c \
-  nghttp2_map.c \
-  nghttp2_queue.c \
-  nghttp2_frame.c \
-  nghttp2_buf.c \
-  nghttp2_stream.c \
-  nghttp2_outbound_item.c \
-  nghttp2_session.c \
-  nghttp2_submit.c \
-  nghttp2_helper.c \
-  nghttp2_npn.c \
-  nghttp2_hd.c \
-  nghttp2_hd_huffman.c \
-  nghttp2_hd_huffman_data.c \
-  nghttp2_version.c \
-  nghttp2_priority_spec.c \
-  nghttp2_option.c \
-  nghttp2_callbacks.c \
-  nghttp2_mem.c \
-  nghttp2_http.c \
-  nghttp2_rcbuf.c
-
-NGHTTP2_OBJ_R := $(addprefix $(OBJ_DIR)/r_, $(notdir $(NGHTTP2_SRC:.c=.obj)))
-NGHTTP2_OBJ_D := $(addprefix $(OBJ_DIR)/d_, $(notdir $(NGHTTP2_SRC:.c=.obj)))
-
-.PHONY: all intro test_ver install copy_headers_and_libs \
-       install_nghttp2_pyd_0 install_nghttp2_pyd_1 \
-       build_nghttp2_pyd_0 build_nghttp2_pyd_1 \
-       clean_nghttp2_pyd_0 clean_nghttp2_pyd_1
-
-
-all: intro includes/nghttp2/nghttp2ver.h $(OBJ_DIR) $(TARGETS) build_nghttp2_pyd_$(USE_CYTHON)
-       @echo 'Welcome to NgHTTP2 (release + debug).'
-       @echo 'Do a "make -f Makefile.MSVC install" at own risk!'
-
-intro:
-       @echo 'Building NgHTTP (MSVC) ver. "$(VERSION)".'
-
-test_ver:
-       @echo '$$(VERSION):   "$(VERSION)".'
-       @echo '$$(_VERSION):  "$(_VERSION)".'
-       @echo '$$(VER_MAJOR): "$(VER_MAJOR)".'
-       @echo '$$(VER_MINOR): "$(VER_MINOR)".'
-       @echo '$$(VER_MICRO): "$(VER_MICRO)".'
-
-$(OBJ_DIR):
-       - mkdir $(OBJ_DIR)
-
-install: includes/nghttp2/nghttp2.h includes/nghttp2/nghttp2ver.h \
-         $(TARGETS) \
-         copy_headers_and_libs install_nghttp2_pyd_$(USE_CYTHON) 
-
-#
-# This MUST be done before using the 'install_nghttp2_pyd_1' rule.
-#
-copy_headers_and_libs:
-       - mkdir -p $(INSTALL_HDR)/nghttp2 $(INSTALL_BIN) $(INSTALL_LIB)
-       cp --update $(addprefix includes/nghttp2/, nghttp2.h nghttp2ver.h)     $(INSTALL_HDR)/nghttp2
-       cp --update $(DLL_R) $(DLL_D) $(NGHTTP2_PDB_R) $(NGHTTP2_PDB_D) $(INSTALL_BIN)
-       cp --update $(IMP_R) $(IMP_D) $(LIB_R) $(LIB_D) $(INSTALL_LIB)
-       @echo
-
-$(LIB_R): $(NGHTTP2_OBJ_R)
-       $(AR) -nologo -out:$@ $^
-       @echo
-
-$(LIB_D): $(NGHTTP2_OBJ_D)
-       $(AR) -nologo -out:$@ $^
-       @echo
-
-
-$(IMP_R): $(DLL_R)
-
-$(DLL_R): $(NGHTTP2_OBJ_R) $(OBJ_DIR)/r_nghttp2.res 
-       $(LD) $(LDFLAGS) -dll -out:$@ -implib:$(IMP_R) $(NGHTTP2_OBJ_R) -PDB:$(NGHTTP2_PDB_R) $(OBJ_DIR)/r_nghttp2.res $(EXT_LIBS)
-       mt -nologo -manifest $@.manifest -outputresource:$@\;2
-       @echo
-
-$(IMP_D): $(DLL_D)
-       
-$(DLL_D): $(NGHTTP2_OBJ_D) $(OBJ_DIR)/d_nghttp2.res 
-       $(LD) $(LDFLAGS) -dll -out:$@ -implib:$(IMP_D) $(NGHTTP2_OBJ_D) -PDB:$(NGHTTP2_PDB_D) $(OBJ_DIR)/d_nghttp2.res $(EXT_LIBS)
-       mt -nologo -manifest $@.manifest -outputresource:$@\;2
-       @echo
-
-
-WIN_OBJDIR:=$(shell cygpath -w $(abspath $(OBJ_DIR)))
-WIN_OBJDIR:=$(subst \,/,$(WIN_OBJDIR))
-
-../python/setup.py: ../python/setup.py.in $(THIS_MAKEFILE)
-       cd ../python ; \
-       echo '# $(GENERATED). DO NOT EDIT.' > setup.py ; \
-       sed -e 's/@top_srcdir@/../'   \
-           -e 's%@top_builddir@%$(WIN_OBJDIR)%' \
-           -e 's/@PACKAGE_VERSION@/$(VERSION)/' setup.py.in >> setup.py ;
-
-build_nghttp2_pyd_0: ;
-
-build_nghttp2_pyd_1: $(addprefix ../python/, setup.py nghttp2.pyx)
-       cd ../python ; \
-       python setup.py build_ext -i -f bdist_wininst
-
-install_nghttp2_pyd_0: ;
-               
-install_nghttp2_pyd_1: $(addprefix ../python/, setup.py nghttp2.pyx)
-       cd ../python ; \
-       pip install .
-
-clean_nghttp2_pyd_0: ;
-
-clean_nghttp2_pyd_1:
-       cd ../python ; \
-       rm -fR build dist
-
-$(OBJ_DIR)/r_%.obj: %.c $(THIS_MAKEFILE)
-       $(CC) $(CFLAGS_R) $(CFLAGS) -Fo$@ -c $<
-       @echo
-
-$(OBJ_DIR)/d_%.obj: %.c $(THIS_MAKEFILE)
-       $(CC) $(CFLAGS_D) $(CFLAGS) -Fo$@ -c $<
-       @echo
-
-$(OBJ_DIR)/r_nghttp2.res: $(OBJ_DIR)/nghttp2.rc $(THIS_MAKEFILE)
-       $(RC) -D_RELEASE -Fo $@ $<
-       @echo
-
-$(OBJ_DIR)/d_nghttp2.res: $(OBJ_DIR)/nghttp2.rc $(THIS_MAKEFILE)
-       $(RC) -D_DEBUG -Fo $@ $<
-       @echo
-
-includes/nghttp2/nghttp2ver.h: includes/nghttp2/nghttp2ver.h.in $(THIS_MAKEFILE)
-       sed < includes/nghttp2/nghttp2ver.h.in     \
-            -e 's/@PACKAGE_VERSION@/$(VERSION)/g' \
-            -e 's/@PACKAGE_VERSION_NUM@/$(VERSION_NUM)/g' > $@
-       touch --reference=includes/nghttp2/nghttp2ver.h.in $@
-
-
-define RES_FILE
-  #include <winver.h>
-
-  VS_VERSION_INFO VERSIONINFO
-    FILEVERSION    $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0
-    PRODUCTVERSION $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0
-    FILEFLAGSMASK  0x3fL
-    FILEOS         0x40004L
-    FILETYPE       0x2L
-    FILESUBTYPE    0x0L
-  #ifdef _DEBUG
-    #define        VER_STR  "$(VERSION).0 (MSVC debug)"
-    #define        DBG      "d"
-    FILEFLAGS      0x1L
-  #else
-    #define        VER_STR  "$(VERSION).0 (MSVC release)"
-    #define        DBG      ""
-    FILEFLAGS      0x0L
-  #endif
-  BEGIN
-    BLOCK "StringFileInfo"
-    BEGIN
-      BLOCK "040904b0"
-      BEGIN
-        VALUE "CompanyName",      "http://tatsuhiro-t.github.io/nghttp2/"
-        VALUE "FileDescription",  "nghttp2; HTTP/2 C library"
-        VALUE "FileVersion",      VER_STR
-        VALUE "InternalName",     "nghttp2" DBG
-        VALUE "LegalCopyright",   "The MIT License"
-        VALUE "LegalTrademarks",  ""
-        VALUE "OriginalFilename", "nghttp2" DBG ".dll"
-        VALUE "ProductName",      "NGHTTP2."
-        VALUE "ProductVersion",   VER_STR
-      END
-    END
-  BLOCK "VarFileInfo"
-  BEGIN
-    VALUE "Translation", 0x409, 1200
-  END
-  END
-endef
-
-export RES_FILE
-
-$(OBJ_DIR)/nghttp2.rc: Makefile.MSVC
-       @echo 'Generating $@...'
-       @echo ' /* $(GENERATED). DO NOT EDIT.' > $@
-       @echo '  */'                          >> $@
-       @echo "$$RES_FILE"                    >> $@
-
-clean:
-       rm -f $(OBJ_DIR)/* includes/nghttp2/nghttp2ver.h
-       @echo
-
-vclean realclean: clean clean_nghttp2_pyd_$(USE_CYTHON)
-       - rm -rf $(OBJ_DIR)
-       - rm -f .depend.MSVC
-
-#
-# Use gcc to generated the dependencies. No MSVC specific args please!
-#
-REPLACE_R = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/r_\1.obj: /'
-REPLACE_D = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/d_\1.obj: /'
-
-depend: includes/nghttp2/nghttp2ver.h
-       @echo '# $(GENERATED). DO NOT EDIT.' > .depend.MSVC
-       gcc -MM $(CFLAGS) $(NGHTTP2_SRC)    >> .depend.tmp
-       @echo '#'                           >> .depend.MSVC
-       @echo '# Release lib objects:'      >> .depend.MSVC
-       sed -e $(REPLACE_R) .depend.tmp     >> .depend.MSVC
-       @echo '#'                           >> .depend.MSVC
-       @echo '# Debug lib objects:'        >> .depend.MSVC
-       sed -e $(REPLACE_D) .depend.tmp     >> .depend.MSVC
-       rm -f .depend.tmp
-
--include .depend.MSVC
+#\r
+# GNU Makefile for nghttp2 / MSVC.\r
+#\r
+# By G. Vanem <gvanem@yahoo.no> 2013\r
+# Updated 3/2015 by Remo Eichenberger @remoe\r
+# The MIT License apply.\r
+#\r
+\r
+#\r
+# Choose your weapons:\r
+#  Set 'USE_CYTHON=1' to build and install the 'nghttp2.pyd' Python extension.\r
+#\r
+THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST))\r
+\r
+USE_CYTHON := 0\r
+#USE_CYTHON := 1\r
+\r
+_VERSION       := $(shell grep AC_INIT ../configure.ac | cut -d'[' -f3 | sed -e 's/-DEV//g' -e 's/], //g')\r
+_VERSION       := $(subst ., ,$(_VERSION))\r
+VER_MAJOR      := $(word 1,$(_VERSION))\r
+VER_MINOR      := $(word 2,$(_VERSION))\r
+VER_MICRO   := $(word 3,$(_VERSION))\r
+VERSION     := $(VER_MAJOR).$(VER_MINOR).$(VER_MICRO)\r
+VERSION_NUM := (($(VER_MAJOR) << 16) + ($(VER_MINOR) << 8) + $(VER_MICRO))\r
+\r
+GENERATED   := 'Generated by $(realpath Makefile.MSVC)'\r
+\r
+OBJ_DIR := MSVC_obj\r
+#SUFFIX :=-vc90-mt-x86\r
+\r
+#\r
+# Where to copy nghttp2.dll + lib + headers to.\r
+# Note: 'make install' is not in default targets. Do it explicitly.\r
+#\r
+TARGET_DIR  ?= ../_VC_ROOT\r
+VC_ROOT     := $(abspath $(TARGET_DIR))\r
+INSTALL_BIN := $(VC_ROOT)/bin\r
+INSTALL_LIB := $(VC_ROOT)/lib\r
+INSTALL_HDR := $(VC_ROOT)/include\r
+DLL_R  := $(OBJ_DIR)/nghttp2$(SUFFIX).dll\r
+DLL_D  := $(OBJ_DIR)/nghttp2d$(SUFFIX).dll\r
+LIB_R  := $(OBJ_DIR)/nghttp2-static.lib\r
+LIB_D  := $(OBJ_DIR)/nghttp2d-static.lib\r
+IMP_R  := $(OBJ_DIR)/nghttp2.lib\r
+IMP_D  := $(OBJ_DIR)/nghttp2d.lib\r
+\r
+#\r
+# Build for DEBUG-model and RELEASE at the same time.\r
+#\r
+TARGETS := $(LIB_R) $(DLL_R) $(IMP_R) \\r
+          $(LIB_D) $(DLL_D) $(IMP_D)\r
+\r
+EXT_LIBS = \r
+\r
+NGHTTP2_PDB_R := $(OBJ_DIR)/nghttp2.pdb\r
+NGHTTP2_PDB_D := $(OBJ_DIR)/nghttp2d.pdb\r
+\r
+CC       = cl\r
+LD              := link\r
+AR              := lib\r
+#CC       := icl\r
+#LD             := xilink\r
+#AR             := xilib\r
+RC              := rc\r
+CFLAGS   := -I./includes -Dssize_t=long\r
+\r
+CFLAGS_R := -nologo -MD  -W3 -Z7 -DBUILDING_NGHTTP2\r
+CFLAGS_D := -nologo -MDd -W3 -Z7 -DBUILDING_NGHTTP2 \\r
+           -Ot -D_DEBUG -GF -RTCs -RTCu # -RTCc -GS\r
+\r
+LDFLAGS := -nologo -MAP -debug -incremental:no -opt:ref,icf -MANIFEST # -verbose\r
+\r
+\r
+NGHTTP2_SRC := nghttp2_pq.c \\r
+  nghttp2_map.c \\r
+  nghttp2_queue.c \\r
+  nghttp2_frame.c \\r
+  nghttp2_buf.c \\r
+  nghttp2_stream.c \\r
+  nghttp2_outbound_item.c \\r
+  nghttp2_session.c \\r
+  nghttp2_submit.c \\r
+  nghttp2_helper.c \\r
+  nghttp2_npn.c \\r
+  nghttp2_hd.c \\r
+  nghttp2_hd_huffman.c \\r
+  nghttp2_hd_huffman_data.c \\r
+  nghttp2_version.c \\r
+  nghttp2_priority_spec.c \\r
+  nghttp2_option.c \\r
+  nghttp2_callbacks.c \\r
+  nghttp2_mem.c \\r
+  nghttp2_http.c \\r
+  nghttp2_rcbuf.c\r
+\r
+NGHTTP2_OBJ_R := $(addprefix $(OBJ_DIR)/r_, $(notdir $(NGHTTP2_SRC:.c=.obj)))\r
+NGHTTP2_OBJ_D := $(addprefix $(OBJ_DIR)/d_, $(notdir $(NGHTTP2_SRC:.c=.obj)))\r
+\r
+.PHONY: all intro test_ver install copy_headers_and_libs \\r
+       install_nghttp2_pyd_0 install_nghttp2_pyd_1 \\r
+       build_nghttp2_pyd_0 build_nghttp2_pyd_1 \\r
+       clean_nghttp2_pyd_0 clean_nghttp2_pyd_1\r
+\r
+\r
+all: intro includes/nghttp2/nghttp2ver.h $(OBJ_DIR) $(TARGETS) build_nghttp2_pyd_$(USE_CYTHON)\r
+       @echo 'Welcome to NgHTTP2 (release + debug).'\r
+       @echo 'Do a "make -f Makefile.MSVC install" at own risk!'\r
+\r
+intro:\r
+       @echo 'Building NgHTTP (MSVC) ver. "$(VERSION)".'\r
+\r
+test_ver:\r
+       @echo '$$(VERSION):   "$(VERSION)".'\r
+       @echo '$$(_VERSION):  "$(_VERSION)".'\r
+       @echo '$$(VER_MAJOR): "$(VER_MAJOR)".'\r
+       @echo '$$(VER_MINOR): "$(VER_MINOR)".'\r
+       @echo '$$(VER_MICRO): "$(VER_MICRO)".'\r
+\r
+$(OBJ_DIR):\r
+       - mkdir $(OBJ_DIR)\r
+\r
+install: includes/nghttp2/nghttp2.h includes/nghttp2/nghttp2ver.h \\r
+         $(TARGETS) \\r
+         copy_headers_and_libs install_nghttp2_pyd_$(USE_CYTHON) \r
+\r
+#\r
+# This MUST be done before using the 'install_nghttp2_pyd_1' rule.\r
+#\r
+copy_headers_and_libs:\r
+       - mkdir -p $(INSTALL_HDR)/nghttp2 $(INSTALL_BIN) $(INSTALL_LIB)\r
+       cp --update $(addprefix includes/nghttp2/, nghttp2.h nghttp2ver.h)     $(INSTALL_HDR)/nghttp2\r
+       cp --update $(DLL_R) $(DLL_D) $(NGHTTP2_PDB_R) $(NGHTTP2_PDB_D) $(INSTALL_BIN)\r
+       cp --update $(IMP_R) $(IMP_D) $(LIB_R) $(LIB_D) $(INSTALL_LIB)\r
+       @echo\r
+\r
+$(LIB_R): $(NGHTTP2_OBJ_R)\r
+       $(AR) -nologo -out:$@ $^\r
+       @echo\r
+\r
+$(LIB_D): $(NGHTTP2_OBJ_D)\r
+       $(AR) -nologo -out:$@ $^\r
+       @echo\r
+\r
+\r
+$(IMP_R): $(DLL_R)\r
+\r
+$(DLL_R): $(NGHTTP2_OBJ_R) $(OBJ_DIR)/r_nghttp2.res \r
+       $(LD) $(LDFLAGS) -dll -out:$@ -implib:$(IMP_R) $(NGHTTP2_OBJ_R) -PDB:$(NGHTTP2_PDB_R) $(OBJ_DIR)/r_nghttp2.res $(EXT_LIBS)\r
+       mt -nologo -manifest $@.manifest -outputresource:$@\;2\r
+       @echo\r
+\r
+$(IMP_D): $(DLL_D)\r
+       \r
+$(DLL_D): $(NGHTTP2_OBJ_D) $(OBJ_DIR)/d_nghttp2.res \r
+       $(LD) $(LDFLAGS) -dll -out:$@ -implib:$(IMP_D) $(NGHTTP2_OBJ_D) -PDB:$(NGHTTP2_PDB_D) $(OBJ_DIR)/d_nghttp2.res $(EXT_LIBS)\r
+       mt -nologo -manifest $@.manifest -outputresource:$@\;2\r
+       @echo\r
+\r
+\r
+WIN_OBJDIR:=$(shell cygpath -w $(abspath $(OBJ_DIR)))\r
+WIN_OBJDIR:=$(subst \,/,$(WIN_OBJDIR))\r
+\r
+../python/setup.py: ../python/setup.py.in $(THIS_MAKEFILE)\r
+       cd ../python ; \\r
+       echo '# $(GENERATED). DO NOT EDIT.' > setup.py ; \\r
+       sed -e 's/@top_srcdir@/../'   \\r
+           -e 's%@top_builddir@%$(WIN_OBJDIR)%' \\r
+           -e 's/@PACKAGE_VERSION@/$(VERSION)/' setup.py.in >> setup.py ;\r
+\r
+build_nghttp2_pyd_0: ;\r
+\r
+build_nghttp2_pyd_1: $(addprefix ../python/, setup.py nghttp2.pyx)\r
+       cd ../python ; \\r
+       python setup.py build_ext -i -f bdist_wininst\r
+\r
+install_nghttp2_pyd_0: ;\r
+               \r
+install_nghttp2_pyd_1: $(addprefix ../python/, setup.py nghttp2.pyx)\r
+       cd ../python ; \\r
+       pip install .\r
+\r
+clean_nghttp2_pyd_0: ;\r
+\r
+clean_nghttp2_pyd_1:\r
+       cd ../python ; \\r
+       rm -fR build dist\r
+\r
+$(OBJ_DIR)/r_%.obj: %.c $(THIS_MAKEFILE)\r
+       $(CC) $(CFLAGS_R) $(CFLAGS) -Fo$@ -c $<\r
+       @echo\r
+\r
+$(OBJ_DIR)/d_%.obj: %.c $(THIS_MAKEFILE)\r
+       $(CC) $(CFLAGS_D) $(CFLAGS) -Fo$@ -c $<\r
+       @echo\r
+\r
+$(OBJ_DIR)/r_nghttp2.res: $(OBJ_DIR)/nghttp2.rc $(THIS_MAKEFILE)\r
+       $(RC) -D_RELEASE -Fo $@ $<\r
+       @echo\r
+\r
+$(OBJ_DIR)/d_nghttp2.res: $(OBJ_DIR)/nghttp2.rc $(THIS_MAKEFILE)\r
+       $(RC) -D_DEBUG -Fo $@ $<\r
+       @echo\r
+\r
+includes/nghttp2/nghttp2ver.h: includes/nghttp2/nghttp2ver.h.in $(THIS_MAKEFILE)\r
+       sed < includes/nghttp2/nghttp2ver.h.in     \\r
+            -e 's/@PACKAGE_VERSION@/$(VERSION)/g' \\r
+            -e 's/@PACKAGE_VERSION_NUM@/$(VERSION_NUM)/g' > $@\r
+       touch --reference=includes/nghttp2/nghttp2ver.h.in $@\r
+\r
+\r
+define RES_FILE\r
+  #include <winver.h>\r
+\r
+  VS_VERSION_INFO VERSIONINFO\r
+    FILEVERSION    $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0\r
+    PRODUCTVERSION $(VER_MAJOR), $(VER_MINOR), $(VER_MICRO), 0\r
+    FILEFLAGSMASK  0x3fL\r
+    FILEOS         0x40004L\r
+    FILETYPE       0x2L\r
+    FILESUBTYPE    0x0L\r
+  #ifdef _DEBUG\r
+    #define        VER_STR  "$(VERSION).0 (MSVC debug)"\r
+    #define        DBG      "d"\r
+    FILEFLAGS      0x1L\r
+  #else\r
+    #define        VER_STR  "$(VERSION).0 (MSVC release)"\r
+    #define        DBG      ""\r
+    FILEFLAGS      0x0L\r
+  #endif\r
+  BEGIN\r
+    BLOCK "StringFileInfo"\r
+    BEGIN\r
+      BLOCK "040904b0"\r
+      BEGIN\r
+        VALUE "CompanyName",      "http://tatsuhiro-t.github.io/nghttp2/"\r
+        VALUE "FileDescription",  "nghttp2; HTTP/2 C library"\r
+        VALUE "FileVersion",      VER_STR\r
+        VALUE "InternalName",     "nghttp2" DBG\r
+        VALUE "LegalCopyright",   "The MIT License"\r
+        VALUE "LegalTrademarks",  ""\r
+        VALUE "OriginalFilename", "nghttp2" DBG ".dll"\r
+        VALUE "ProductName",      "NGHTTP2."\r
+        VALUE "ProductVersion",   VER_STR\r
+      END\r
+    END\r
+  BLOCK "VarFileInfo"\r
+  BEGIN\r
+    VALUE "Translation", 0x409, 1200\r
+  END\r
+  END\r
+endef\r
+\r
+export RES_FILE\r
+\r
+$(OBJ_DIR)/nghttp2.rc: Makefile.MSVC\r
+       @echo 'Generating $@...'\r
+       @echo ' /* $(GENERATED). DO NOT EDIT.' > $@\r
+       @echo '  */'                          >> $@\r
+       @echo "$$RES_FILE"                    >> $@\r
+\r
+clean:\r
+       rm -f $(OBJ_DIR)/* includes/nghttp2/nghttp2ver.h\r
+       @echo\r
+\r
+vclean realclean: clean clean_nghttp2_pyd_$(USE_CYTHON)\r
+       - rm -rf $(OBJ_DIR)\r
+       - rm -f .depend.MSVC\r
+\r
+#\r
+# Use gcc to generated the dependencies. No MSVC specific args please!\r
+#\r
+REPLACE_R = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/r_\1.obj: /'\r
+REPLACE_D = 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/d_\1.obj: /'\r
+\r
+depend: includes/nghttp2/nghttp2ver.h\r
+       @echo '# $(GENERATED). DO NOT EDIT.' > .depend.MSVC\r
+       gcc -MM $(CFLAGS) $(NGHTTP2_SRC)    >> .depend.tmp\r
+       @echo '#'                           >> .depend.MSVC\r
+       @echo '# Release lib objects:'      >> .depend.MSVC\r
+       sed -e $(REPLACE_R) .depend.tmp     >> .depend.MSVC\r
+       @echo '#'                           >> .depend.MSVC\r
+       @echo '# Debug lib objects:'        >> .depend.MSVC\r
+       sed -e $(REPLACE_D) .depend.tmp     >> .depend.MSVC\r
+       rm -f .depend.tmp\r
+\r
+-include .depend.MSVC\r
index 973e337..f30d6c5 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -403,8 +403,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
          *config.status*) \
            cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
          *) \
-           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
-           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
        esac;
 
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -498,7 +498,10 @@ cscopelist-am: $(am__tagged_files)
 distclean-tags:
        -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
 
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
        @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        list='$(DISTFILES)'; \
index 14f8950..e7198b3 100644 (file)
@@ -28,7 +28,7 @@
 /* Define WIN32 when build target is Win32 API (borrowed from
    libcurl) */
 #if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
-#define WIN32
+#  define WIN32
 #endif
 
 #ifdef __cplusplus
@@ -40,9 +40,9 @@ extern "C" {
 /* MSVC < 2013 does not have inttypes.h because it is not C99
    compliant.  See compiler macros and version number in
    https://sourceforge.net/p/predef/wiki/Compilers/ */
-#include <stdint.h>
+#  include <stdint.h>
 #else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
-#include <inttypes.h>
+#  include <inttypes.h>
 #endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
 #include <sys/types.h>
 #include <stdarg.h>
@@ -50,20 +50,20 @@ extern "C" {
 #include <nghttp2/nghttp2ver.h>
 
 #ifdef NGHTTP2_STATICLIB
-#define NGHTTP2_EXTERN
+#  define NGHTTP2_EXTERN
 #elif defined(WIN32)
-#ifdef BUILDING_NGHTTP2
-#define NGHTTP2_EXTERN __declspec(dllexport)
-#else /* !BUILDING_NGHTTP2 */
-#define NGHTTP2_EXTERN __declspec(dllimport)
-#endif /* !BUILDING_NGHTTP2 */
-#else  /* !defined(WIN32) */
-#ifdef BUILDING_NGHTTP2
-#define NGHTTP2_EXTERN __attribute__((visibility("default")))
-#else /* !BUILDING_NGHTTP2 */
-#define NGHTTP2_EXTERN
-#endif /* !BUILDING_NGHTTP2 */
-#endif /* !defined(WIN32) */
+#  ifdef BUILDING_NGHTTP2
+#    define NGHTTP2_EXTERN __declspec(dllexport)
+#  else /* !BUILDING_NGHTTP2 */
+#    define NGHTTP2_EXTERN __declspec(dllimport)
+#  endif /* !BUILDING_NGHTTP2 */
+#else    /* !defined(WIN32) */
+#  ifdef BUILDING_NGHTTP2
+#    define NGHTTP2_EXTERN __attribute__((visibility("default")))
+#  else /* !BUILDING_NGHTTP2 */
+#    define NGHTTP2_EXTERN
+#  endif /* !BUILDING_NGHTTP2 */
+#endif   /* !defined(WIN32) */
 
 /**
  * @macro
@@ -611,7 +611,12 @@ typedef enum {
    * The ALTSVC frame, which is defined in `RFC 7383
    * <https://tools.ietf.org/html/rfc7838#section-4>`_.
    */
-  NGHTTP2_ALTSVC = 0x0a
+  NGHTTP2_ALTSVC = 0x0a,
+  /**
+   * The ORIGIN frame, which is defined by `RFC 8336
+   * <https://tools.ietf.org/html/rfc8336>`_.
+   */
+  NGHTTP2_ORIGIN = 0x0c
 } nghttp2_frame_type;
 
 /**
@@ -675,7 +680,12 @@ typedef enum {
   /**
    * SETTINGS_MAX_HEADER_LIST_SIZE
    */
-  NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06
+  NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06,
+  /**
+   * SETTINGS_ENABLE_CONNECT_PROTOCOL
+   * (`RFC 8441 <https://tools.ietf.org/html/rfc8441>`_)
+   */
+  NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08
 } nghttp2_settings_id;
 /* Note: If we add SETTINGS, update the capacity of
    NGHTTP2_INBOUND_NUM_IV as well */
@@ -2473,15 +2483,15 @@ nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val);
  *
  * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of
  * remote endpoint as if it is received in SETTINGS frame.  Without
- * specifying this option, before the local endpoint receives
- * SETTINGS_MAX_CONCURRENT_STREAMS in SETTINGS frame from remote
- * endpoint, SETTINGS_MAX_CONCURRENT_STREAMS is unlimited.  This may
- * cause problem if local endpoint submits lots of requests initially
- * and sending them at once to the remote peer may lead to the
- * rejection of some requests.  Specifying this option to the sensible
- * value, say 100, may avoid this kind of issue. This value will be
- * overwritten if the local endpoint receives
- * SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint.
+ * specifying this option, the maximum number of outgoing concurrent
+ * streams is initially limited to 100 to avoid issues when the local
+ * endpoint submits lots of requests before receiving initial SETTINGS
+ * frame from the remote endpoint, since sending them at once to the
+ * remote endpoint could lead to rejection of some of the requests.
+ * This value will be overwritten when the local endpoint receives
+ * initial SETTINGS frame from the remote endpoint, either to the
+ * value advertised in SETTINGS_MAX_CONCURRENT_STREAMS or to the
+ * default value (unlimited) if none was advertised.
  */
 NGHTTP2_EXTERN void
 nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option,
@@ -3797,10 +3807,13 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec);
  * .. warning::
  *
  *   This function returns assigned stream ID if it succeeds.  But
- *   that stream is not opened yet.  The application must not submit
+ *   that stream is not created yet.  The application must not submit
  *   frame to that stream ID before
  *   :type:`nghttp2_before_frame_send_callback` is called for this
- *   frame.
+ *   frame.  This means `nghttp2_session_get_stream_user_data()` does
+ *   not work before the callback.  But
+ *   `nghttp2_session_set_stream_user_data()` handles this situation
+ *   specially, and it can set data to a stream during this period.
  *
  */
 NGHTTP2_EXTERN int32_t nghttp2_submit_request(
@@ -4516,8 +4529,7 @@ typedef struct {
  * Submits ALTSVC frame.
  *
  * ALTSVC frame is a non-critical extension to HTTP/2, and defined in
- * is defined in `RFC 7383
- * <https://tools.ietf.org/html/rfc7838#section-4>`_.
+ * `RFC 7383 <https://tools.ietf.org/html/rfc7838#section-4>`_.
  *
  * The |flags| is currently ignored and should be
  * :enum:`NGHTTP2_FLAG_NONE`.
@@ -4552,6 +4564,81 @@ NGHTTP2_EXTERN int nghttp2_submit_altsvc(nghttp2_session *session,
                                          size_t field_value_len);
 
 /**
+ * @struct
+ *
+ * The single entry of an origin.
+ */
+typedef struct {
+  /**
+   * The pointer to origin.  No validation is made against this field
+   * by the library.  This is not necessarily NULL-terminated.
+   */
+  uint8_t *origin;
+  /**
+   * The length of the |origin|.
+   */
+  size_t origin_len;
+} nghttp2_origin_entry;
+
+/**
+ * @struct
+ *
+ * The payload of ORIGIN frame.  ORIGIN frame is a non-critical
+ * extension to HTTP/2 and defined by `RFC 8336
+ * <https://tools.ietf.org/html/rfc8336>`_.
+ *
+ * If this frame is received, and
+ * `nghttp2_option_set_user_recv_extension_type()` is not set, and
+ * `nghttp2_option_set_builtin_recv_extension_type()` is set for
+ * :enum:`NGHTTP2_ORIGIN`, ``nghttp2_extension.payload`` will point to
+ * this struct.
+ *
+ * It has the following members:
+ */
+typedef struct {
+  /**
+   * The number of origins contained in |ov|.
+   */
+  size_t nov;
+  /**
+   * The pointer to the array of origins contained in ORIGIN frame.
+   */
+  nghttp2_origin_entry *ov;
+} nghttp2_ext_origin;
+
+/**
+ * @function
+ *
+ * Submits ORIGIN frame.
+ *
+ * ORIGIN frame is a non-critical extension to HTTP/2 and defined by
+ * `RFC 8336 <https://tools.ietf.org/html/rfc8336>`_.
+ *
+ * The |flags| is currently ignored and should be
+ * :enum:`NGHTTP2_FLAG_NONE`.
+ *
+ * The |ov| points to the array of origins.  The |nov| specifies the
+ * number of origins included in |ov|.  This function creates copies
+ * of all elements in |ov|.
+ *
+ * The ORIGIN frame is only usable by a server.  If this function is
+ * invoked with client side session, this function returns
+ * :enum:`NGHTTP2_ERR_INVALID_STATE`.
+ *
+ * :enum:`NGHTTP2_ERR_NOMEM`
+ *     Out of memory
+ * :enum:`NGHTTP2_ERR_INVALID_STATE`
+ *     The function is called from client side session.
+ * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
+ *     There are too many origins, or an origin is too large to fit
+ *     into a default frame payload.
+ */
+NGHTTP2_EXTERN int nghttp2_submit_origin(nghttp2_session *session,
+                                         uint8_t flags,
+                                         const nghttp2_origin_entry *ov,
+                                         size_t nov);
+
+/**
  * @function
  *
  * Compares ``lhs->name`` of length ``lhs->namelen`` bytes and
index a3271d9..420adbd 100644 (file)
@@ -29,7 +29,7 @@
  * @macro
  * Version number of the nghttp2 library release
  */
-#define NGHTTP2_VERSION "1.31.1"
+#define NGHTTP2_VERSION "1.34.0"
 
 /**
  * @macro
@@ -37,6 +37,6 @@
  * release. This is a 24 bit number with 8 bits for major number, 8 bits
  * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
  */
-#define NGHTTP2_VERSION_NUM 0x011f01
+#define NGHTTP2_VERSION_NUM 0x012200
 
 #endif /* NGHTTP2VER_H */
index 9f484a2..06cce67 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_BUF_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
index b607bbb..61e51fa 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_CALLBACKS_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
index 2e2cd0d..cbb4dd5 100644 (file)
 #define NGHTTP2_DEBUG_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
 
 #ifdef DEBUGBUILD
-#define DEBUGF(...) nghttp2_debug_vprintf(__VA_ARGS__)
+#  define DEBUGF(...) nghttp2_debug_vprintf(__VA_ARGS__)
 void nghttp2_debug_vprintf(const char *format, ...);
 #else
-#define DEBUGF(...)                                                            \
-  do {                                                                         \
-  } while (0)
+#  define DEBUGF(...)                                                          \
+    do {                                                                       \
+    } while (0)
 #endif
 
 #endif /* NGHTTP2_DEBUG_H */
index fa7cb69..4821de4 100644 (file)
@@ -223,6 +223,36 @@ void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem) {
   nghttp2_mem_free(mem, altsvc->origin);
 }
 
+void nghttp2_frame_origin_init(nghttp2_extension *frame,
+                               nghttp2_origin_entry *ov, size_t nov) {
+  nghttp2_ext_origin *origin;
+  size_t payloadlen = 0;
+  size_t i;
+
+  for (i = 0; i < nov; ++i) {
+    payloadlen += 2 + ov[i].origin_len;
+  }
+
+  nghttp2_frame_hd_init(&frame->hd, payloadlen, NGHTTP2_ORIGIN,
+                        NGHTTP2_FLAG_NONE, 0);
+
+  origin = frame->payload;
+  origin->ov = ov;
+  origin->nov = nov;
+}
+
+void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem) {
+  nghttp2_ext_origin *origin;
+
+  origin = frame->payload;
+  if (origin == NULL) {
+    return;
+  }
+  /* We use the same buffer for all resources pointed by the field of
+     origin directly or indirectly. */
+  nghttp2_mem_free(mem, origin->ov);
+}
+
 size_t nghttp2_frame_priority_len(uint8_t flags) {
   if (flags & NGHTTP2_FLAG_PRIORITY) {
     return NGHTTP2_PRIORITY_SPECLEN;
@@ -746,6 +776,106 @@ int nghttp2_frame_unpack_altsvc_payload2(nghttp2_extension *frame,
   return 0;
 }
 
+int nghttp2_frame_pack_origin(nghttp2_bufs *bufs, nghttp2_extension *frame) {
+  nghttp2_buf *buf;
+  nghttp2_ext_origin *origin;
+  nghttp2_origin_entry *orig;
+  size_t i;
+
+  origin = frame->payload;
+
+  buf = &bufs->head->buf;
+
+  if (nghttp2_buf_avail(buf) < frame->hd.length) {
+    return NGHTTP2_ERR_FRAME_SIZE_ERROR;
+  }
+
+  buf->pos -= NGHTTP2_FRAME_HDLEN;
+
+  nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd);
+
+  for (i = 0; i < origin->nov; ++i) {
+    orig = &origin->ov[i];
+    nghttp2_put_uint16be(buf->last, (uint16_t)orig->origin_len);
+    buf->last += 2;
+    buf->last = nghttp2_cpymem(buf->last, orig->origin, orig->origin_len);
+  }
+
+  assert(nghttp2_buf_len(buf) == NGHTTP2_FRAME_HDLEN + frame->hd.length);
+
+  return 0;
+}
+
+int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame,
+                                        const uint8_t *payload,
+                                        size_t payloadlen, nghttp2_mem *mem) {
+  nghttp2_ext_origin *origin;
+  const uint8_t *p, *end;
+  uint8_t *dst;
+  size_t originlen;
+  nghttp2_origin_entry *ov;
+  size_t nov = 0;
+  size_t len = 0;
+
+  origin = frame->payload;
+  p = payload;
+  end = p + payloadlen;
+
+  for (; p != end;) {
+    if (end - p < 2) {
+      return NGHTTP2_ERR_FRAME_SIZE_ERROR;
+    }
+    originlen = nghttp2_get_uint16(p);
+    p += 2;
+    if (originlen == 0) {
+      continue;
+    }
+    if (originlen > (size_t)(end - p)) {
+      return NGHTTP2_ERR_FRAME_SIZE_ERROR;
+    }
+    p += originlen;
+    /* 1 for terminal NULL */
+    len += originlen + 1;
+    ++nov;
+  }
+
+  if (nov == 0) {
+    origin->ov = NULL;
+    origin->nov = 0;
+
+    return 0;
+  }
+
+  len += nov * sizeof(nghttp2_origin_entry);
+
+  ov = nghttp2_mem_malloc(mem, len);
+  if (ov == NULL) {
+    return NGHTTP2_ERR_NOMEM;
+  }
+
+  origin->ov = ov;
+  origin->nov = nov;
+
+  dst = (uint8_t *)ov + nov * sizeof(nghttp2_origin_entry);
+  p = payload;
+
+  for (; p != end;) {
+    originlen = nghttp2_get_uint16(p);
+    p += 2;
+    if (originlen == 0) {
+      continue;
+    }
+    ov->origin = dst;
+    ov->origin_len = originlen;
+    dst = nghttp2_cpymem(dst, p, originlen);
+    *dst++ = '\0';
+    p += originlen;
+    ++ov;
+  }
+
+  return 0;
+}
+
 nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
                                               size_t niv, nghttp2_mem *mem) {
   nghttp2_settings_entry *iv_copy;
@@ -920,6 +1050,11 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) {
       break;
     case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
       break;
+    case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
+      if (iv[i].value != 0 && iv[i].value != 1) {
+        return 0;
+      }
+      break;
     }
   }
   return 1;
index 35ca214..615bbf3 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_FRAME_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
@@ -72,6 +72,7 @@
 /* Union of extension frame payload */
 typedef union {
   nghttp2_ext_altsvc altsvc;
+  nghttp2_ext_origin origin;
 } nghttp2_ext_frame_payload;
 
 void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);
@@ -393,6 +394,36 @@ int nghttp2_frame_unpack_altsvc_payload2(nghttp2_extension *frame,
                                          size_t payloadlen, nghttp2_mem *mem);
 
 /*
+ * Packs ORIGIN frame |frame| in wire frame format and store it in
+ * |bufs|.
+ *
+ * The caller must make sure that nghttp2_bufs_reset(bufs) is called
+ * before calling this function.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP2_ERR_FRAME_SIZE_ERROR
+ *     The length of the frame is too large.
+ */
+int nghttp2_frame_pack_origin(nghttp2_bufs *bufs, nghttp2_extension *ext);
+
+/*
+ * Unpacks ORIGIN wire format into |frame|.  The |payload| of length
+ * |payloadlen| contains the frame payload.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP2_ERR_NOMEM
+ *     Out of memory.
+ * NGHTTP2_ERR_FRAME_SIZE_ERROR
+ *     The payload is too small.
+ */
+int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame,
+                                        const uint8_t *payload,
+                                        size_t payloadlen, nghttp2_mem *mem);
+/*
  * Initializes HEADERS frame |frame| with given values.  |frame| takes
  * ownership of |nva|, so caller must not free it. If |stream_id| is
  * not assigned yet, it must be -1.
@@ -490,6 +521,24 @@ void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id,
 void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem);
 
 /*
+ * Initializes ORIGIN frame |frame| with given values.  This function
+ * assumes that frame->payload points to nghttp2_ext_origin object.
+ * Also |ov| and the memory pointed by the field of its elements are
+ * allocated in single buffer, starting with |ov|.  On success, this
+ * function takes ownership of |ov|, so caller must not free it.
+ */
+void nghttp2_frame_origin_init(nghttp2_extension *frame,
+                               nghttp2_origin_entry *ov, size_t nov);
+
+/*
+ * Frees up resources under |frame|.  This function does not free
+ * nghttp2_ext_origin object pointed by frame->payload.  This function
+ * only frees nghttp2_ext_origin.ov.  Therefore, other fields must be
+ * allocated in the same buffer with ov.
+ */
+void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem);
+
+/*
  * Returns the number of padding bytes after payload.  The total
  * padding length is given in the |padlen|.  The returned value does
  * not include the Pad Length field.  If |padlen| is 0, this function
index 1eb3be3..a61f0d4 100644 (file)
@@ -45,7 +45,7 @@
 /* 3rd parameter is nghttp2_token value for header field name.  We use
    first enum value if same header names are repeated (e.g.,
    :status). */
-static nghttp2_hd_static_entry static_table[] = {
+static const nghttp2_hd_static_entry static_table[] = {
     MAKE_STATIC_ENT(":authority", "", 0, 3153725150u),
     MAKE_STATIC_ENT(":method", "GET", 1, 695666056u),
     MAKE_STATIC_ENT(":method", "POST", 1, 695666056u),
@@ -271,6 +271,15 @@ static int32_t lookup_token(const uint8_t *name, size_t namelen) {
       break;
     }
     break;
+  case 9:
+    switch (name[8]) {
+    case 'l':
+      if (memeq(":protoco", name, 8)) {
+        return NGHTTP2_TOKEN__PROTOCOL;
+      }
+      break;
+    }
+    break;
   case 10:
     switch (name[9]) {
     case 'e':
@@ -1159,7 +1168,7 @@ static search_result search_static_table(const nghttp2_nv *nv, int32_t token,
                                          int name_only) {
   search_result res = {token, 0};
   int i;
-  nghttp2_hd_static_entry *ent;
+  const nghttp2_hd_static_entry *ent;
 
   if (name_only) {
     return res;
@@ -1184,7 +1193,7 @@ static search_result search_hd_table(nghttp2_hd_context *context,
                                      int indexing_mode, nghttp2_hd_map *map,
                                      uint32_t hash) {
   search_result res = {-1, 0};
-  nghttp2_hd_entry *ent;
+  const nghttp2_hd_entry *ent;
   int exact_match;
   int name_only = indexing_mode == NGHTTP2_HD_NEVER_INDEXING;
 
@@ -1289,8 +1298,9 @@ nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t idx) {
     return hd_ringbuf_get(&context->hd_table, idx - NGHTTP2_STATIC_TABLE_LENGTH)
         ->nv;
   } else {
-    nghttp2_hd_static_entry *ent = &static_table[idx];
-    nghttp2_hd_nv nv = {&ent->name, &ent->value, ent->token,
+    const nghttp2_hd_static_entry *ent = &static_table[idx];
+    nghttp2_hd_nv nv = {(nghttp2_rcbuf *)&ent->name,
+                        (nghttp2_rcbuf *)&ent->value, ent->token,
                         NGHTTP2_NV_FLAG_NONE};
     return nv;
   }
index 760bfbc..14ae980 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_HD_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
@@ -111,6 +111,7 @@ typedef enum {
   NGHTTP2_TOKEN_KEEP_ALIVE,
   NGHTTP2_TOKEN_PROXY_CONNECTION,
   NGHTTP2_TOKEN_UPGRADE,
+  NGHTTP2_TOKEN__PROTOCOL,
 } nghttp2_token;
 
 struct nghttp2_hd_entry;
index 8332340..c6e3942 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_HD_HUFFMAN_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
index 3b282c7..81a8a0c 100644 (file)
@@ -340,7 +340,7 @@ const char *nghttp2_strerror(int error_code) {
 }
 
 /* Generated by gennmchartbl.py */
-static int VALID_HD_NAME_CHARS[] = {
+static const int VALID_HD_NAME_CHARS[] = {
     0 /* NUL  */, 0 /* SOH  */, 0 /* STX  */, 0 /* ETX  */,
     0 /* EOT  */, 0 /* ENQ  */, 0 /* ACK  */, 0 /* BEL  */,
     0 /* BS   */, 0 /* HT   */, 0 /* LF   */, 0 /* VT   */,
@@ -428,7 +428,7 @@ int nghttp2_check_header_name(const uint8_t *name, size_t len) {
 }
 
 /* Generated by genvchartbl.py */
-static int VALID_HD_VALUE_CHARS[] = {
+static const int VALID_HD_VALUE_CHARS[] = {
     0 /* NUL  */, 0 /* SOH  */, 0 /* STX  */, 0 /* ETX  */,
     0 /* EOT  */, 0 /* ENQ  */, 0 /* ACK  */, 0 /* BEL  */,
     0 /* BS   */, 1 /* HT   */, 0 /* LF   */, 0 /* VT   */,
index 4a32564..b1f18ce 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_HELPER_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <string.h>
index 8240f8d..6e8acfd 100644 (file)
@@ -113,7 +113,7 @@ static int check_path(nghttp2_stream *stream) {
 }
 
 static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
-                                  int trailer) {
+                                  int trailer, int connect_protocol) {
   if (nv->name->base[0] == ':') {
     if (trailer ||
         (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
@@ -146,10 +146,6 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
             return NGHTTP2_ERR_HTTP_HEADER;
           }
           stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT;
-          if (stream->http_flags &
-              (NGHTTP2_HTTP_FLAG__PATH | NGHTTP2_HTTP_FLAG__SCHEME)) {
-            return NGHTTP2_ERR_HTTP_HEADER;
-          }
         }
         break;
       case 'S':
@@ -162,9 +158,6 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
     }
     break;
   case NGHTTP2_TOKEN__PATH:
-    if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
-      return NGHTTP2_ERR_HTTP_HEADER;
-    }
     if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
       return NGHTTP2_ERR_HTTP_HEADER;
     }
@@ -175,9 +168,6 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
     }
     break;
   case NGHTTP2_TOKEN__SCHEME:
-    if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
-      return NGHTTP2_ERR_HTTP_HEADER;
-    }
     if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
       return NGHTTP2_ERR_HTTP_HEADER;
     }
@@ -186,6 +176,15 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
       stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
     }
     break;
+  case NGHTTP2_TOKEN__PROTOCOL:
+    if (!connect_protocol) {
+      return NGHTTP2_ERR_HTTP_HEADER;
+    }
+
+    if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PROTOCOL)) {
+      return NGHTTP2_ERR_HTTP_HEADER;
+    }
+    break;
   case NGHTTP2_TOKEN_HOST:
     if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) {
       return NGHTTP2_ERR_HTTP_HEADER;
@@ -244,7 +243,7 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
       return NGHTTP2_ERR_HTTP_HEADER;
     }
     stream->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len);
-    if (stream->status_code == -1) {
+    if (stream->status_code == -1 || stream->status_code == 101) {
       return NGHTTP2_ERR_HTTP_HEADER;
     }
     break;
@@ -265,7 +264,7 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
       return NGHTTP2_ERR_REMOVE_HTTP_HEADER;
     }
     if (stream->status_code / 100 == 1 ||
-        (stream->status_code == 200 &&
+        (stream->status_code / 100 == 2 &&
          (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT))) {
       return NGHTTP2_ERR_HTTP_HEADER;
     }
@@ -458,7 +457,9 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
   }
 
   if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
-    return http_request_on_header(stream, nv, trailer);
+    return http_request_on_header(stream, nv, trailer,
+                                  session->server &&
+                                      session->pending_enable_connect_protocol);
   }
 
   return http_response_on_header(stream, nv, trailer);
@@ -466,8 +467,11 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
 
 int nghttp2_http_on_request_headers(nghttp2_stream *stream,
                                     nghttp2_frame *frame) {
-  if (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) {
-    if ((stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
+  if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
+      (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) {
+    if ((stream->http_flags &
+         (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) ||
+        (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) {
       return -1;
     }
     stream->content_length = -1;
@@ -478,6 +482,11 @@ int nghttp2_http_on_request_headers(nghttp2_stream *stream,
          (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) {
       return -1;
     }
+    if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) &&
+        ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0 ||
+         (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0)) {
+      return -1;
+    }
     if (!check_path(stream)) {
       return -1;
     }
index ac684c4..dd057cd 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_HTTP_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
index 30cf727..b23585c 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_INT_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
index 2126248..f6e29e3 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_MAP_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
index 2d1bd6a..f83dbcb 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_MEM_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
index 587f418..95ffee7 100644 (file)
 #define NGHTTP2_NET_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
+#  include <arpa/inet.h>
 #endif /* HAVE_ARPA_INET_H */
 
 #ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
+#  include <netinet/in.h>
 #endif /* HAVE_NETINET_IN_H */
 
 #include <nghttp2/nghttp2.h>
    define inline functions for those function so that we don't have
    dependeny on that lib. */
 
-#ifdef _MSC_VER
-#define STIN static __inline
-#else
-#define STIN static inline
-#endif
+#  ifdef _MSC_VER
+#    define STIN static __inline
+#  else
+#    define STIN static inline
+#  endif
 
 STIN uint32_t htonl(uint32_t hostlong) {
   uint32_t res;
index a481bde..c6f1c04 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_NPN_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
index aec5dcf..8946d7d 100644 (file)
@@ -86,6 +86,10 @@ void nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option,
     option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES;
     option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ALTSVC;
     return;
+  case NGHTTP2_ORIGIN:
+    option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES;
+    option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ORIGIN;
+    return;
   default:
     return;
   }
index c743e33..29e72aa 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_OPTION_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
index 1633cc3..f651c80 100644 (file)
@@ -86,6 +86,9 @@ void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) {
     case NGHTTP2_ALTSVC:
       nghttp2_frame_altsvc_free(&frame->ext, mem);
       break;
+    case NGHTTP2_ORIGIN:
+      nghttp2_frame_origin_free(&frame->ext, mem);
+      break;
     default:
       assert(0);
       break;
index 89a8a92..b5f503a 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_OUTBOUND_ITEM_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
index 71cf96a..2d7b702 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_PQ_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
index 98fac21..92ece82 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_PRIORITY_SPEC_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
index c7eb753..a06fa6c 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_QUEUE_H
 
 #ifdef HAVE_CONFIG_H
-#include "config.h"
+#  include "config.h"
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
index 29d1543..6814e70 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_RCBUF_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
index c58f059..ef4932a 100644 (file)
@@ -219,6 +219,10 @@ static int session_terminate_session(nghttp2_session *session,
     return 0;
   }
 
+  /* Ignore all incoming frames because we are going to tear down the
+     session. */
+  session->iframe.state = NGHTTP2_IB_IGN_ALL;
+
   if (reason == NULL) {
     debug_data = NULL;
     debug_datalen = 0;
@@ -344,6 +348,12 @@ static void session_inbound_frame_reset(nghttp2_session *session) {
         }
         nghttp2_frame_altsvc_free(&iframe->frame.ext, mem);
         break;
+      case NGHTTP2_ORIGIN:
+        if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN) == 0) {
+          break;
+        }
+        nghttp2_frame_origin_free(&iframe->frame.ext, mem);
+        break;
       }
     }
 
@@ -1745,6 +1755,13 @@ static int session_predicate_altsvc_send(nghttp2_session *session,
   return 0;
 }
 
+static int session_predicate_origin_send(nghttp2_session *session) {
+  if (session_is_closing(session)) {
+    return NGHTTP2_ERR_SESSION_CLOSING;
+  }
+  return 0;
+}
+
 /* Take into account settings max frame size and both connection-level
    flow control here */
 static ssize_t
@@ -2277,6 +2294,18 @@ static int session_prep_frame(nghttp2_session *session,
       nghttp2_frame_pack_altsvc(&session->aob.framebufs, &frame->ext);
 
       return 0;
+    case NGHTTP2_ORIGIN:
+      rv = session_predicate_origin_send(session);
+      if (rv != 0) {
+        return rv;
+      }
+
+      rv = nghttp2_frame_pack_origin(&session->aob.framebufs, &frame->ext);
+      if (rv != 0) {
+        return rv;
+      }
+
+      return 0;
     default:
       /* Unreachable here */
       assert(0);
@@ -4332,6 +4361,9 @@ int nghttp2_session_update_local_settings(nghttp2_session *session,
     case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
       session->local_settings.max_header_list_size = iv[i].value;
       break;
+    case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
+      session->local_settings.enable_connect_protocol = iv[i].value;
+      break;
     }
   }
 
@@ -4381,6 +4413,12 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
     return session_call_on_frame_received(session, frame);
   }
 
+  if (!session->remote_settings_received) {
+    session->remote_settings.max_concurrent_streams =
+        NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS;
+    session->remote_settings_received = 1;
+  }
+
   for (i = 0; i < frame->settings.niv; ++i) {
     nghttp2_settings_entry *entry = &frame->settings.iv[i];
 
@@ -4465,6 +4503,26 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
       session->remote_settings.max_header_list_size = entry->value;
 
       break;
+    case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
+
+      if (entry->value != 0 && entry->value != 1) {
+        return session_handle_invalid_connection(
+            session, frame, NGHTTP2_ERR_PROTO,
+            "SETTINGS: invalid SETTINGS_ENABLE_CONNECT_PROTOCOL");
+      }
+
+      if (!session->server &&
+          session->remote_settings.enable_connect_protocol &&
+          entry->value == 0) {
+        return session_handle_invalid_connection(
+            session, frame, NGHTTP2_ERR_PROTO,
+            "SETTINGS: server attempted to disable "
+            "SETTINGS_ENABLE_CONNECT_PROTOCOL");
+      }
+
+      session->remote_settings.enable_connect_protocol = entry->value;
+
+      break;
     }
   }
 
@@ -4817,6 +4875,11 @@ int nghttp2_session_on_altsvc_received(nghttp2_session *session,
   return session_call_on_frame_received(session, frame);
 }
 
+int nghttp2_session_on_origin_received(nghttp2_session *session,
+                                       nghttp2_frame *frame) {
+  return session_call_on_frame_received(session, frame);
+}
+
 static int session_process_altsvc_frame(nghttp2_session *session) {
   nghttp2_inbound_frame *iframe = &session->iframe;
   nghttp2_frame *frame = &iframe->frame;
@@ -4832,6 +4895,25 @@ static int session_process_altsvc_frame(nghttp2_session *session) {
   return nghttp2_session_on_altsvc_received(session, frame);
 }
 
+static int session_process_origin_frame(nghttp2_session *session) {
+  nghttp2_inbound_frame *iframe = &session->iframe;
+  nghttp2_frame *frame = &iframe->frame;
+  nghttp2_mem *mem = &session->mem;
+  int rv;
+
+  rv = nghttp2_frame_unpack_origin_payload(&frame->ext, iframe->lbuf.pos,
+                                           nghttp2_buf_len(&iframe->lbuf), mem);
+  if (rv != 0) {
+    if (nghttp2_is_fatal(rv)) {
+      return rv;
+    }
+    /* Ignore ORIGIN frame which cannot be parsed. */
+    return 0;
+  }
+
+  return nghttp2_session_on_origin_received(session, frame);
+}
+
 static int session_process_extension_frame(nghttp2_session *session) {
   int rv;
   nghttp2_inbound_frame *iframe = &session->iframe;
@@ -5191,6 +5273,7 @@ static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) {
   case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
   case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
   case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
+  case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
     break;
   default:
     DEBUGF("recv: unknown settings id=0x%02x\n", iv.settings_id);
@@ -5346,9 +5429,6 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
 
       if (iframe->sbuf.pos[3] != NGHTTP2_SETTINGS ||
           (iframe->sbuf.pos[4] & NGHTTP2_FLAG_ACK)) {
-
-        iframe->state = NGHTTP2_IB_IGN_ALL;
-
         rv = session_call_error_callback(
             session, NGHTTP2_ERR_SETTINGS_EXPECTED,
             "Remote peer returned unexpected data while we expected "
@@ -5395,10 +5475,6 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
         DEBUGF("recv: length is too large %zu > %u\n", iframe->frame.hd.length,
                session->local_settings.max_frame_size);
 
-        busy = 1;
-
-        iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
-
         rv = nghttp2_session_terminate_session_with_reason(
             session, NGHTTP2_FRAME_SIZE_ERROR, "too large frame size");
 
@@ -5406,7 +5482,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
           return rv;
         }
 
-        break;
+        return (ssize_t)inlen;
       }
 
       switch (iframe->frame.hd.type) {
@@ -5420,6 +5496,9 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
         busy = 1;
 
         rv = session_on_data_received_fail_fast(session);
+        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+          return (ssize_t)inlen;
+        }
         if (rv == NGHTTP2_ERR_IGN_PAYLOAD) {
           DEBUGF("recv: DATA not allowed stream_id=%d\n",
                  iframe->frame.hd.stream_id);
@@ -5433,7 +5512,6 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
 
         rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
         if (rv < 0) {
-          iframe->state = NGHTTP2_IB_IGN_DATA;
           rv = nghttp2_session_terminate_session_with_reason(
               session, NGHTTP2_PROTOCOL_ERROR,
               "DATA: insufficient padding space");
@@ -5441,7 +5519,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
           if (nghttp2_is_fatal(rv)) {
             return rv;
           }
-          break;
+          return (ssize_t)inlen;
         }
 
         if (rv == 1) {
@@ -5462,17 +5540,13 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
 
         rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
         if (rv < 0) {
-          busy = 1;
-
-          iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
-
           rv = nghttp2_session_terminate_session_with_reason(
               session, NGHTTP2_PROTOCOL_ERROR,
               "HEADERS: insufficient padding space");
           if (nghttp2_is_fatal(rv)) {
             return rv;
           }
-          break;
+          return (ssize_t)inlen;
         }
 
         if (rv == 1) {
@@ -5514,6 +5588,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
 
         busy = 1;
 
+        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+          return (ssize_t)inlen;
+        }
+
         if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
           rv = nghttp2_session_add_rst_stream(
               session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR);
@@ -5628,15 +5706,13 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
 
         rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd);
         if (rv < 0) {
-          busy = 1;
-          iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
           rv = nghttp2_session_terminate_session_with_reason(
               session, NGHTTP2_PROTOCOL_ERROR,
               "PUSH_PROMISE: insufficient padding space");
           if (nghttp2_is_fatal(rv)) {
             return rv;
           }
-          break;
+          return (ssize_t)inlen;
         }
 
         if (rv == 1) {
@@ -5696,11 +5772,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
           return rv;
         }
 
-        busy = 1;
-
-        iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
-
-        break;
+        return (ssize_t)inlen;
       default:
         DEBUGF("recv: extension frame\n");
 
@@ -5754,6 +5826,42 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
             inbound_frame_set_mark(iframe, 2);
 
             break;
+          case NGHTTP2_ORIGIN:
+            if (!(session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN)) {
+              busy = 1;
+              iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
+              break;
+            }
+
+            DEBUGF("recv: ORIGIN\n");
+
+            iframe->frame.ext.payload = &iframe->ext_frame_payload.origin;
+
+            if (session->server || iframe->frame.hd.stream_id ||
+                (iframe->frame.hd.flags & 0xf0)) {
+              busy = 1;
+              iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
+              break;
+            }
+
+            iframe->frame.hd.flags = NGHTTP2_FLAG_NONE;
+
+            if (iframe->payloadleft) {
+              iframe->raw_lbuf = nghttp2_mem_malloc(mem, iframe->payloadleft);
+
+              if (iframe->raw_lbuf == NULL) {
+                return NGHTTP2_ERR_NOMEM;
+              }
+
+              nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf,
+                                    iframe->payloadleft);
+            } else {
+              busy = 1;
+            }
+
+            iframe->state = NGHTTP2_IB_READ_ORIGIN_PAYLOAD;
+
+            break;
           default:
             busy = 1;
 
@@ -5770,6 +5878,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
         case NGHTTP2_IB_IGN_PAYLOAD:
         case NGHTTP2_IB_FRAME_SIZE_ERROR:
         case NGHTTP2_IB_IGN_DATA:
+        case NGHTTP2_IB_IGN_ALL:
           break;
         default:
           rv = session_call_on_begin_frame(session, &iframe->frame.hd);
@@ -5800,21 +5909,19 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
       case NGHTTP2_HEADERS:
         if (iframe->padlen == 0 &&
             (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) {
+          pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags);
           padlen = inbound_frame_compute_pad(iframe);
-          if (padlen < 0) {
-            busy = 1;
+          if (padlen < 0 ||
+              (size_t)padlen + pri_fieldlen > 1 + iframe->payloadleft) {
             rv = nghttp2_session_terminate_session_with_reason(
                 session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: invalid padding");
             if (nghttp2_is_fatal(rv)) {
               return rv;
             }
-            iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
-            break;
+            return (ssize_t)inlen;
           }
           iframe->frame.headers.padlen = (size_t)padlen;
 
-          pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags);
-
           if (pri_fieldlen > 0) {
             if (iframe->payloadleft < pri_fieldlen) {
               busy = 1;
@@ -5837,6 +5944,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
 
         busy = 1;
 
+        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+          return (ssize_t)inlen;
+        }
+
         if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
           rv = nghttp2_session_add_rst_stream(
               session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR);
@@ -5861,6 +5972,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
           return rv;
         }
 
+        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+          return (ssize_t)inlen;
+        }
+
         session_inbound_frame_reset(session);
 
         break;
@@ -5870,6 +5985,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
           return rv;
         }
 
+        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+          return (ssize_t)inlen;
+        }
+
         session_inbound_frame_reset(session);
 
         break;
@@ -5877,16 +5996,15 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
         if (iframe->padlen == 0 &&
             (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) {
           padlen = inbound_frame_compute_pad(iframe);
-          if (padlen < 0) {
-            busy = 1;
+          if (padlen < 0 || (size_t)padlen + 4 /* promised stream id */
+                                > 1 + iframe->payloadleft) {
             rv = nghttp2_session_terminate_session_with_reason(
                 session, NGHTTP2_PROTOCOL_ERROR,
                 "PUSH_PROMISE: invalid padding");
             if (nghttp2_is_fatal(rv)) {
               return rv;
             }
-            iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
-            break;
+            return (ssize_t)inlen;
           }
 
           iframe->frame.push_promise.padlen = (size_t)padlen;
@@ -5911,6 +6029,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
 
         busy = 1;
 
+        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+          return (ssize_t)inlen;
+        }
+
         if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
           rv = nghttp2_session_add_rst_stream(
               session, iframe->frame.push_promise.promised_stream_id,
@@ -5936,6 +6058,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
           return rv;
         }
 
+        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+          return (ssize_t)inlen;
+        }
+
         session_inbound_frame_reset(session);
 
         break;
@@ -5967,6 +6093,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
           return rv;
         }
 
+        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+          return (ssize_t)inlen;
+        }
+
         session_inbound_frame_reset(session);
 
         break;
@@ -6028,6 +6158,12 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
 
       data_readlen = inbound_frame_effective_readlen(
           iframe, iframe->payloadleft - readlen, readlen);
+
+      if (data_readlen == -1) {
+        /* everything is padding */
+        data_readlen = 0;
+      }
+
       trail_padlen = nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen);
 
       final = (iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) &&
@@ -6047,6 +6183,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
           return rv;
         }
 
+        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+          return (ssize_t)inlen;
+        }
+
         if (rv == NGHTTP2_ERR_PAUSE) {
           in += hd_proclen;
           iframe->payloadleft -= hd_proclen;
@@ -6156,11 +6296,9 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
         return rv;
       }
 
-      busy = 1;
+      assert(iframe->state == NGHTTP2_IB_IGN_ALL);
 
-      iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
-
-      break;
+      return (ssize_t)inlen;
     case NGHTTP2_IB_READ_SETTINGS:
       DEBUGF("recv: [IB_READ_SETTINGS]\n");
 
@@ -6189,6 +6327,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
         return rv;
       }
 
+      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+        return (ssize_t)inlen;
+      }
+
       session_inbound_frame_reset(session);
 
       break;
@@ -6219,6 +6361,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
         return rv;
       }
 
+      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+        return (ssize_t)inlen;
+      }
+
       session_inbound_frame_reset(session);
 
       break;
@@ -6258,11 +6404,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
           return rv;
         }
 
-        busy = 1;
-
-        iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
-
-        break;
+        return (ssize_t)inlen;
       }
 
       /* CONTINUATION won't bear NGHTTP2_PADDED flag */
@@ -6306,6 +6448,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
         return rv;
       }
 
+      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+        return (ssize_t)inlen;
+      }
+
       /* Pad Length field is consumed immediately */
       rv =
           nghttp2_session_consume(session, iframe->frame.hd.stream_id, readlen);
@@ -6314,6 +6460,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
         return rv;
       }
 
+      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+        return (ssize_t)inlen;
+      }
+
       stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id);
       if (stream) {
         rv = session_update_recv_stream_window_size(
@@ -6334,8 +6484,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
         if (nghttp2_is_fatal(rv)) {
           return rv;
         }
-        iframe->state = NGHTTP2_IB_IGN_DATA;
-        break;
+        return (ssize_t)inlen;
       }
 
       iframe->frame.data.padlen = (size_t)padlen;
@@ -6369,6 +6518,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
           return rv;
         }
 
+        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+          return (ssize_t)inlen;
+        }
+
         rv = session_update_recv_stream_window_size(
             session, stream, readlen,
             iframe->payloadleft ||
@@ -6395,6 +6548,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
           if (nghttp2_is_fatal(rv)) {
             return rv;
           }
+
+          if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+            return (ssize_t)inlen;
+          }
         }
 
         DEBUGF("recv: data_readlen=%zd\n", data_readlen);
@@ -6410,6 +6567,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
                 if (nghttp2_is_fatal(rv)) {
                   return rv;
                 }
+
+                if (iframe->state == NGHTTP2_IB_IGN_DATA) {
+                  return (ssize_t)inlen;
+                }
               }
 
               rv = nghttp2_session_add_rst_stream(
@@ -6467,6 +6628,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
           return rv;
         }
 
+        if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+          return (ssize_t)inlen;
+        }
+
         if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
 
           /* Ignored DATA is considered as "consumed" immediately. */
@@ -6475,6 +6640,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
           if (nghttp2_is_fatal(rv)) {
             return rv;
           }
+
+          if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+            return (ssize_t)inlen;
+          }
         }
       }
 
@@ -6529,7 +6698,6 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
       DEBUGF("recv: [IB_READ_ALTSVC_PAYLOAD]\n");
 
       readlen = inbound_frame_payload_readlen(iframe, in, last);
-
       if (readlen > 0) {
         iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
 
@@ -6547,11 +6715,44 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
       }
 
       rv = session_process_altsvc_frame(session);
+      if (nghttp2_is_fatal(rv)) {
+        return rv;
+      }
+
+      session_inbound_frame_reset(session);
+
+      break;
+    case NGHTTP2_IB_READ_ORIGIN_PAYLOAD:
+      DEBUGF("recv: [IB_READ_ORIGIN_PAYLOAD]\n");
+
+      readlen = inbound_frame_payload_readlen(iframe, in, last);
+
+      if (readlen > 0) {
+        iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen);
+
+        iframe->payloadleft -= readlen;
+        in += readlen;
+      }
+
+      DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen,
+             iframe->payloadleft);
+
+      if (iframe->payloadleft) {
+        assert(nghttp2_buf_avail(&iframe->lbuf) > 0);
+
+        break;
+      }
+
+      rv = session_process_origin_frame(session);
 
       if (nghttp2_is_fatal(rv)) {
         return rv;
       }
 
+      if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+        return (ssize_t)inlen;
+      }
+
       session_inbound_frame_reset(session);
 
       break;
@@ -6875,6 +7076,13 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
     }
   }
 
+  for (i = niv; i > 0; --i) {
+    if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL) {
+      session->pending_enable_connect_protocol = (uint8_t)iv[i - 1].value;
+      break;
+    }
+  }
+
   return 0;
 }
 
@@ -7031,12 +7239,42 @@ int nghttp2_session_set_stream_user_data(nghttp2_session *session,
                                          int32_t stream_id,
                                          void *stream_user_data) {
   nghttp2_stream *stream;
+  nghttp2_frame *frame;
+  nghttp2_outbound_item *item;
+
   stream = nghttp2_session_get_stream(session, stream_id);
-  if (!stream) {
+  if (stream) {
+    stream->stream_user_data = stream_user_data;
+    return 0;
+  }
+
+  if (session->server || !nghttp2_session_is_my_stream_id(session, stream_id) ||
+      !nghttp2_outbound_queue_top(&session->ob_syn)) {
     return NGHTTP2_ERR_INVALID_ARGUMENT;
   }
-  stream->stream_user_data = stream_user_data;
-  return 0;
+
+  frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame;
+  assert(frame->hd.type == NGHTTP2_HEADERS);
+
+  if (frame->hd.stream_id > stream_id ||
+      (uint32_t)stream_id >= session->next_stream_id) {
+    return NGHTTP2_ERR_INVALID_ARGUMENT;
+  }
+
+  for (item = session->ob_syn.head; item; item = item->qnext) {
+    if (item->frame.hd.stream_id < stream_id) {
+      continue;
+    }
+
+    if (item->frame.hd.stream_id > stream_id) {
+      break;
+    }
+
+    item->aux_data.headers.stream_user_data = stream_user_data;
+    return 0;
+  }
+
+  return NGHTTP2_ERR_INVALID_ARGUMENT;
 }
 
 int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) {
@@ -7153,6 +7391,8 @@ uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session,
     return session->remote_settings.max_frame_size;
   case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
     return session->remote_settings.max_header_list_size;
+  case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
+    return session->remote_settings.enable_connect_protocol;
   }
 
   assert(0);
@@ -7174,6 +7414,8 @@ uint32_t nghttp2_session_get_local_settings(nghttp2_session *session,
     return session->local_settings.max_frame_size;
   case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
     return session->local_settings.max_header_list_size;
+  case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
+    return session->local_settings.enable_connect_protocol;
   }
 
   assert(0);
index c7cb27d..40a8865 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_SESSION_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
@@ -61,7 +61,8 @@ typedef enum {
  */
 typedef enum {
   NGHTTP2_TYPEMASK_NONE = 0,
-  NGHTTP2_TYPEMASK_ALTSVC = 1 << 0
+  NGHTTP2_TYPEMASK_ALTSVC = 1 << 0,
+  NGHTTP2_TYPEMASK_ORIGIN = 1 << 1
 } nghttp2_typemask;
 
 typedef enum {
@@ -121,6 +122,7 @@ typedef enum {
   NGHTTP2_IB_IGN_DATA,
   NGHTTP2_IB_IGN_ALL,
   NGHTTP2_IB_READ_ALTSVC_PAYLOAD,
+  NGHTTP2_IB_READ_ORIGIN_PAYLOAD,
   NGHTTP2_IB_READ_EXTENSION_PAYLOAD
 } nghttp2_inbound_state;
 
@@ -162,6 +164,7 @@ typedef struct {
   uint32_t initial_window_size;
   uint32_t max_frame_size;
   uint32_t max_header_list_size;
+  uint32_t enable_connect_protocol;
 } nghttp2_settings_storage;
 
 typedef enum {
@@ -301,8 +304,10 @@ struct nghttp2_session {
      increased/decreased by submitting WINDOW_UPDATE. See
      nghttp2_submit_window_update(). */
   int32_t local_window_size;
-  /* Settings value received from the remote endpoint. We just use ID
-     as index. The index = 0 is unused. */
+  /* This flag is used to indicate that the local endpoint received initial
+     SETTINGS frame from the remote endpoint. */
+  uint8_t remote_settings_received;
+  /* Settings value received from the remote endpoint. */
   nghttp2_settings_storage remote_settings;
   /* Settings value of the local endpoint. */
   nghttp2_settings_storage local_settings;
@@ -317,6 +322,9 @@ struct nghttp2_session {
   /* Unacked local ENABLE_PUSH value.  We use this to refuse
      PUSH_PROMISE before SETTINGS ACK is received. */
   uint8_t pending_enable_push;
+  /* Unacked local ENABLE_CONNECT_PROTOCOL value.  We use this to
+     accept :protocol header field before SETTINGS_ACK is received. */
+  uint8_t pending_enable_connect_protocol;
   /* Nonzero if the session is server side. */
   uint8_t server;
   /* Flags indicating GOAWAY is sent and/or received. The flags are
@@ -698,7 +706,7 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
  * NGHTTP2_ERR_NOMEM
  *     Out of memory.
  * NGHTTP2_ERR_CALLBACK_FAILURE
- *   The callback function failed.
+ *     The callback function failed.
  * NGHTTP2_ERR_FLOODED
  *     There are too many items in outbound queue, and this is most
  *     likely caused by misbehaviour of peer.
@@ -716,7 +724,7 @@ int nghttp2_session_on_ping_received(nghttp2_session *session,
  * NGHTTP2_ERR_NOMEM
  *     Out of memory.
  * NGHTTP2_ERR_CALLBACK_FAILURE
- *   The callback function failed.
+ *     The callback function failed.
  */
 int nghttp2_session_on_goaway_received(nghttp2_session *session,
                                        nghttp2_frame *frame);
@@ -731,7 +739,7 @@ int nghttp2_session_on_goaway_received(nghttp2_session *session,
  * NGHTTP2_ERR_NOMEM
  *     Out of memory.
  * NGHTTP2_ERR_CALLBACK_FAILURE
- *   The callback function failed.
+ *     The callback function failed.
  */
 int nghttp2_session_on_window_update_received(nghttp2_session *session,
                                               nghttp2_frame *frame);
@@ -744,12 +752,25 @@ int nghttp2_session_on_window_update_received(nghttp2_session *session,
  * negative error codes:
  *
  * NGHTTP2_ERR_CALLBACK_FAILURE
- *   The callback function failed.
+ *     The callback function failed.
  */
 int nghttp2_session_on_altsvc_received(nghttp2_session *session,
                                        nghttp2_frame *frame);
 
 /*
+ * Called when ORIGIN is received, assuming |frame| is properly
+ * initialized.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP2_ERR_CALLBACK_FAILURE
+ *     The callback function failed.
+ */
+int nghttp2_session_on_origin_received(nghttp2_session *session,
+                                       nghttp2_frame *frame);
+
+/*
  * Called when DATA is received, assuming |frame| is properly
  * initialized.
  *
@@ -759,7 +780,7 @@ int nghttp2_session_on_altsvc_received(nghttp2_session *session,
  * NGHTTP2_ERR_NOMEM
  *     Out of memory.
  * NGHTTP2_ERR_CALLBACK_FAILURE
- *   The callback function failed.
+ *     The callback function failed.
  */
 int nghttp2_session_on_data_received(nghttp2_session *session,
                                      nghttp2_frame *frame);
index da0e5d5..fb8dc14 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_STREAM_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
@@ -130,7 +130,8 @@ typedef enum {
   /* "http" or "https" scheme */
   NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 13,
   /* set if final response is expected */
-  NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14
+  NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14,
+  NGHTTP2_HTTP_FLAG__PROTOCOL = 1 << 15,
 } nghttp2_http_flag;
 
 struct nghttp2_stream {
index 6c15c82..f604eff 100644 (file)
@@ -571,6 +571,89 @@ fail_item_malloc:
   return rv;
 }
 
+int nghttp2_submit_origin(nghttp2_session *session, uint8_t flags,
+                          const nghttp2_origin_entry *ov, size_t nov) {
+  nghttp2_mem *mem;
+  uint8_t *p;
+  nghttp2_outbound_item *item;
+  nghttp2_frame *frame;
+  nghttp2_ext_origin *origin;
+  nghttp2_origin_entry *ov_copy;
+  size_t len = 0;
+  size_t i;
+  int rv;
+  (void)flags;
+
+  mem = &session->mem;
+
+  if (!session->server) {
+    return NGHTTP2_ERR_INVALID_STATE;
+  }
+
+  if (nov) {
+    for (i = 0; i < nov; ++i) {
+      len += ov[i].origin_len;
+    }
+
+    if (2 * nov + len > NGHTTP2_MAX_PAYLOADLEN) {
+      return NGHTTP2_ERR_INVALID_ARGUMENT;
+    }
+
+    /* The last nov is added for terminal NULL character. */
+    ov_copy =
+        nghttp2_mem_malloc(mem, nov * sizeof(nghttp2_origin_entry) + len + nov);
+    if (ov_copy == NULL) {
+      return NGHTTP2_ERR_NOMEM;
+    }
+
+    p = (uint8_t *)ov_copy + nov * sizeof(nghttp2_origin_entry);
+
+    for (i = 0; i < nov; ++i) {
+      ov_copy[i].origin = p;
+      ov_copy[i].origin_len = ov[i].origin_len;
+      p = nghttp2_cpymem(p, ov[i].origin, ov[i].origin_len);
+      *p++ = '\0';
+    }
+
+    assert((size_t)(p - (uint8_t *)ov_copy) ==
+           nov * sizeof(nghttp2_origin_entry) + len + nov);
+  } else {
+    ov_copy = NULL;
+  }
+
+  item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
+  if (item == NULL) {
+    rv = NGHTTP2_ERR_NOMEM;
+    goto fail_item_malloc;
+  }
+
+  nghttp2_outbound_item_init(item);
+
+  item->aux_data.ext.builtin = 1;
+
+  origin = &item->ext_frame_payload.origin;
+
+  frame = &item->frame;
+  frame->ext.payload = origin;
+
+  nghttp2_frame_origin_init(&frame->ext, ov_copy, nov);
+
+  rv = nghttp2_session_add_item(session, item);
+  if (rv != 0) {
+    nghttp2_frame_origin_free(&frame->ext, mem);
+    nghttp2_mem_free(mem, item);
+
+    return rv;
+  }
+
+  return 0;
+
+fail_item_malloc:
+  free(ov_copy);
+
+  return rv;
+}
+
 static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
                                  const nghttp2_data_provider *data_prd) {
   uint8_t flags = NGHTTP2_FLAG_NONE;
index 545388c..74d702f 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_SUBMIT_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
index 8c5710d..4211f2c 100644 (file)
@@ -23,7 +23,7 @@
  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <nghttp2/nghttp2.h>
index 8938ab3..4edfa7a 100644 (file)
@@ -1,40 +1,40 @@
-#include <winver.h>
-
-VS_VERSION_INFO VERSIONINFO
-
-FILEVERSION    @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0
-PRODUCTVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0
-FILEFLAGSMASK  0x3fL
-FILEOS         0x40004L
-FILETYPE       0x2L
-FILESUBTYPE    0x0L
-#ifdef _DEBUG
-       #define        VER_STR  "@PROJECT_VERSION@.0 (MSVC debug)"
-       #define        DBG      "d"
-       FILEFLAGS      0x1L
-#else
-       #define        VER_STR  "@PROJECT_VERSION@.0 (MSVC release)"
-       #define        DBG      ""
-       FILEFLAGS      0x0L
-#endif
-BEGIN
-BLOCK "StringFileInfo"
-BEGIN
-  BLOCK "040904b0"
-  BEGIN
-       VALUE "CompanyName",      "https://nghttp2.org/"
-       VALUE "FileDescription",  "nghttp2; HTTP/2 C library"
-       VALUE "FileVersion",      VER_STR
-       VALUE "InternalName",     "nghttp2" DBG
-       VALUE "LegalCopyright",   "The MIT License"
-       VALUE "LegalTrademarks",  ""
-       VALUE "OriginalFilename", "nghttp2" DBG ".dll"
-       VALUE "ProductName",      "NGHTTP2."
-       VALUE "ProductVersion",   VER_STR
-  END
-END
-BLOCK "VarFileInfo"
-BEGIN
-VALUE "Translation", 0x409, 1200
-END
-END
+#include <winver.h>\r
+\r
+VS_VERSION_INFO VERSIONINFO\r
+\r
+FILEVERSION    @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0\r
+PRODUCTVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0\r
+FILEFLAGSMASK  0x3fL\r
+FILEOS         0x40004L\r
+FILETYPE       0x2L\r
+FILESUBTYPE    0x0L\r
+#ifdef _DEBUG\r
+       #define        VER_STR  "@PROJECT_VERSION@.0 (MSVC debug)"\r
+       #define        DBG      "d"\r
+       FILEFLAGS      0x1L\r
+#else\r
+       #define        VER_STR  "@PROJECT_VERSION@.0 (MSVC release)"\r
+       #define        DBG      ""\r
+       FILEFLAGS      0x0L\r
+#endif\r
+BEGIN\r
+BLOCK "StringFileInfo"\r
+BEGIN\r
+  BLOCK "040904b0"\r
+  BEGIN\r
+       VALUE "CompanyName",      "https://nghttp2.org/"\r
+       VALUE "FileDescription",  "nghttp2; HTTP/2 C library"\r
+       VALUE "FileVersion",      VER_STR\r
+       VALUE "InternalName",     "nghttp2" DBG\r
+       VALUE "LegalCopyright",   "The MIT License"\r
+       VALUE "LegalTrademarks",  ""\r
+       VALUE "OriginalFilename", "nghttp2" DBG ".dll"\r
+       VALUE "ProductName",      "NGHTTP2."\r
+       VALUE "ProductVersion",   VER_STR\r
+  END\r
+END\r
+BLOCK "VarFileInfo"\r
+BEGIN\r
+VALUE "Translation", 0x409, 1200\r
+END\r
+END\r
index a736cf9..d3230f7 100644 (file)
--- a/ltmain.sh
+++ b/ltmain.sh
@@ -31,7 +31,7 @@
 
 PROGRAM=libtool
 PACKAGE=libtool
-VERSION="2.4.6 Debian-2.4.6-2"
+VERSION="2.4.6 Debian-2.4.6-4"
 package_revision=2.4.6
 
 
@@ -64,7 +64,7 @@ package_revision=2.4.6
 # libraries, which are installed to $pkgauxdir.
 
 # Set a version string for this script.
-scriptversion=2015-01-20.17; # UTC
+scriptversion=2015-10-12.13; # UTC
 
 # General shell script boiler plate, and helper functions.
 # Written by Gary V. Vaughan, 2004
@@ -580,16 +580,16 @@ if test yes = "$_G_HAVE_PLUSEQ_OP"; then
   {
     $debug_cmd
 
-    func_quote_for_eval "$2"
-    eval "$1+=\\ \$func_quote_for_eval_result"
+    func_quote_arg pretty "$2"
+    eval "$1+=\\ \$func_quote_arg_result"
   }'
 else
   func_append_quoted ()
   {
     $debug_cmd
 
-    func_quote_for_eval "$2"
-    eval "$1=\$$1\\ \$func_quote_for_eval_result"
+    func_quote_arg pretty "$2"
+    eval "$1=\$$1\\ \$func_quote_arg_result"
   }
 fi
 
@@ -1091,85 +1091,181 @@ func_relative_path ()
 }
 
 
-# func_quote_for_eval ARG...
-# --------------------------
-# Aesthetically quote ARGs to be evaled later.
-# This function returns two values:
-#   i) func_quote_for_eval_result
-#      double-quoted, suitable for a subsequent eval
-#  ii) func_quote_for_eval_unquoted_result
-#      has all characters that are still active within double
-#      quotes backslashified.
-func_quote_for_eval ()
+# func_quote_portable EVAL ARG
+# ----------------------------
+# Internal function to portably implement func_quote_arg.  Note that we still
+# keep attention to performance here so we as much as possible try to avoid
+# calling sed binary (so far O(N) complexity as long as func_append is O(1)).
+func_quote_portable ()
 {
     $debug_cmd
 
-    func_quote_for_eval_unquoted_result=
-    func_quote_for_eval_result=
-    while test 0 -lt $#; do
-      case $1 in
-        *[\\\`\"\$]*)
-         _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;;
-        *)
-          _G_unquoted_arg=$1 ;;
-      esac
-      if test -n "$func_quote_for_eval_unquoted_result"; then
-       func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg"
-      else
-        func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg"
+    func_quote_portable_result=$2
+
+    # one-time-loop (easy break)
+    while true
+    do
+      if $1; then
+        func_quote_portable_result=`$ECHO "$2" | $SED \
+          -e "$sed_double_quote_subst" -e "$sed_double_backslash"`
+        break
       fi
 
-      case $_G_unquoted_arg in
-        # Double-quote args containing shell metacharacters to delay
-        # word splitting, command substitution and variable expansion
-        # for a subsequent eval.
-        # Many Bourne shells cannot handle close brackets correctly
-        # in scan sets, so we specify it separately.
-        *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \  ]*|*]*|"")
-          _G_quoted_arg=\"$_G_unquoted_arg\"
+      # Quote for eval.
+      case $func_quote_portable_result in
+        *[\\\`\"\$]*)
+          case $func_quote_portable_result in
+            *[\[\*\?]*)
+              func_quote_portable_result=`$ECHO "$func_quote_portable_result" | $SED "$sed_quote_subst"`
+              break
+              ;;
+          esac
+
+          func_quote_portable_old_IFS=$IFS
+          for _G_char in '\' '`' '"' '$'
+          do
+            # STATE($1) PREV($2) SEPARATOR($3)
+            set start "" ""
+            func_quote_portable_result=dummy"$_G_char$func_quote_portable_result$_G_char"dummy
+            IFS=$_G_char
+            for _G_part in $func_quote_portable_result
+            do
+              case $1 in
+              quote)
+                func_append func_quote_portable_result "$3$2"
+                set quote "$_G_part" "\\$_G_char"
+                ;;
+              start)
+                set first "" ""
+                func_quote_portable_result=
+                ;;
+              first)
+                set quote "$_G_part" ""
+                ;;
+              esac
+            done
+          done
+          IFS=$func_quote_portable_old_IFS
           ;;
-        *)
-          _G_quoted_arg=$_G_unquoted_arg
-         ;;
+        *) ;;
       esac
-
-      if test -n "$func_quote_for_eval_result"; then
-       func_append func_quote_for_eval_result " $_G_quoted_arg"
-      else
-        func_append func_quote_for_eval_result "$_G_quoted_arg"
-      fi
-      shift
+      break
     done
+
+    func_quote_portable_unquoted_result=$func_quote_portable_result
+    case $func_quote_portable_result in
+      # double-quote args containing shell metacharacters to delay
+      # word splitting, command substitution and variable expansion
+      # for a subsequent eval.
+      # many bourne shells cannot handle close brackets correctly
+      # in scan sets, so we specify it separately.
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \    ]*|*]*|"")
+        func_quote_portable_result=\"$func_quote_portable_result\"
+        ;;
+    esac
 }
 
 
-# func_quote_for_expand ARG
-# -------------------------
-# Aesthetically quote ARG to be evaled later; same as above,
-# but do not quote variable references.
-func_quote_for_expand ()
-{
-    $debug_cmd
+# func_quotefast_eval ARG
+# -----------------------
+# Quote one ARG (internal).  This is equivalent to 'func_quote_arg eval ARG',
+# but optimized for speed.  Result is stored in $func_quotefast_eval.
+if test xyes = `(x=; printf -v x %q yes; echo x"$x") 2>/dev/null`; then
+  func_quotefast_eval ()
+  {
+    printf -v func_quotefast_eval_result %q "$1"
+  }
+else
+  func_quotefast_eval ()
+  {
+    func_quote_portable false "$1"
+    func_quotefast_eval_result=$func_quote_portable_result
+  }
+fi
 
-    case $1 in
-      *[\\\`\"]*)
-       _G_arg=`$ECHO "$1" | $SED \
-           -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;;
-      *)
-        _G_arg=$1 ;;
+
+# func_quote_arg MODEs ARG
+# ------------------------
+# Quote one ARG to be evaled later.  MODEs argument may contain zero ore more
+# specifiers listed below separated by ',' character.  This function returns two
+# values:
+#   i) func_quote_arg_result
+#      double-quoted (when needed), suitable for a subsequent eval
+#  ii) func_quote_arg_unquoted_result
+#      has all characters that are still active within double
+#      quotes backslashified.  Available only if 'unquoted' is specified.
+#
+# Available modes:
+# ----------------
+# 'eval' (default)
+#       - escape shell special characters
+# 'expand'
+#       - the same as 'eval';  but do not quote variable references
+# 'pretty'
+#       - request aesthetic output, i.e. '"a b"' instead of 'a\ b'.  This might
+#         later used in func_quote to get output like: 'echo "a b"' instead of
+#         'echo a\ b'.  This is slower than default on some shells.
+# 'unquoted'
+#       - produce also $func_quote_arg_unquoted_result which does not contain
+#         wrapping double-quotes.
+#
+# Examples for 'func_quote_arg pretty,unquoted string':
+#
+#   string      | *_result              | *_unquoted_result
+#   ------------+-----------------------+-------------------
+#   "           | \"                    | \"
+#   a b         | "a b"                 | a b
+#   "a b"       | "\"a b\""             | \"a b\"
+#   *           | "*"                   | *
+#   z="${x-$y}" | "z=\"\${x-\$y}\""     | z=\"\${x-\$y}\"
+#
+# Examples for 'func_quote_arg pretty,unquoted,expand string':
+#
+#   string        |   *_result          |  *_unquoted_result
+#   --------------+---------------------+--------------------
+#   z="${x-$y}"   | "z=\"${x-$y}\""     | z=\"${x-$y}\"
+func_quote_arg ()
+{
+    _G_quote_expand=false
+    case ,$1, in
+      *,expand,*)
+        _G_quote_expand=:
+        ;;
     esac
 
-    case $_G_arg in
-      # Double-quote args containing shell metacharacters to delay
-      # word splitting and command substitution for a subsequent eval.
-      # Many Bourne shells cannot handle close brackets correctly
-      # in scan sets, so we specify it separately.
-      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \    ]*|*]*|"")
-        _G_arg=\"$_G_arg\"
+    case ,$1, in
+      *,pretty,*|*,expand,*|*,unquoted,*)
+        func_quote_portable $_G_quote_expand "$2"
+        func_quote_arg_result=$func_quote_portable_result
+        func_quote_arg_unquoted_result=$func_quote_portable_unquoted_result
+        ;;
+      *)
+        # Faster quote-for-eval for some shells.
+        func_quotefast_eval "$2"
+        func_quote_arg_result=$func_quotefast_eval_result
         ;;
     esac
+}
 
-    func_quote_for_expand_result=$_G_arg
+
+# func_quote MODEs ARGs...
+# ------------------------
+# Quote all ARGs to be evaled later and join them into single command.  See
+# func_quote_arg's description for more info.
+func_quote ()
+{
+    $debug_cmd
+    _G_func_quote_mode=$1 ; shift
+    func_quote_result=
+    while test 0 -lt $#; do
+      func_quote_arg "$_G_func_quote_mode" "$1"
+      if test -n "$func_quote_result"; then
+        func_append func_quote_result " $func_quote_arg_result"
+      else
+        func_append func_quote_result "$func_quote_arg_result"
+      fi
+      shift
+    done
 }
 
 
@@ -1215,8 +1311,8 @@ func_show_eval ()
     _G_cmd=$1
     _G_fail_exp=${2-':'}
 
-    func_quote_for_expand "$_G_cmd"
-    eval "func_notquiet $func_quote_for_expand_result"
+    func_quote_arg pretty,expand "$_G_cmd"
+    eval "func_notquiet $func_quote_arg_result"
 
     $opt_dry_run || {
       eval "$_G_cmd"
@@ -1241,8 +1337,8 @@ func_show_eval_locale ()
     _G_fail_exp=${2-':'}
 
     $opt_quiet || {
-      func_quote_for_expand "$_G_cmd"
-      eval "func_echo $func_quote_for_expand_result"
+      func_quote_arg expand,pretty "$_G_cmd"
+      eval "func_echo $func_quote_arg_result"
     }
 
     $opt_dry_run || {
@@ -1370,7 +1466,7 @@ func_lt_ver ()
 #! /bin/sh
 
 # Set a version string for this script.
-scriptversion=2014-01-07.03; # UTC
+scriptversion=2015-10-12.13; # UTC
 
 # A portable, pluggable option parser for Bourne shell.
 # Written by Gary V. Vaughan, 2010
@@ -1530,6 +1626,8 @@ func_run_hooks ()
 {
     $debug_cmd
 
+    _G_rc_run_hooks=false
+
     case " $hookable_fns " in
       *" $1 "*) ;;
       *) func_fatal_error "'$1' does not support hook funcions.n" ;;
@@ -1538,16 +1636,16 @@ func_run_hooks ()
     eval _G_hook_fns=\$$1_hooks; shift
 
     for _G_hook in $_G_hook_fns; do
-      eval $_G_hook '"$@"'
-
-      # store returned options list back into positional
-      # parameters for next 'cmd' execution.
-      eval _G_hook_result=\$${_G_hook}_result
-      eval set dummy "$_G_hook_result"; shift
+      if eval $_G_hook '"$@"'; then
+        # store returned options list back into positional
+        # parameters for next 'cmd' execution.
+        eval _G_hook_result=\$${_G_hook}_result
+        eval set dummy "$_G_hook_result"; shift
+        _G_rc_run_hooks=:
+      fi
     done
 
-    func_quote_for_eval ${1+"$@"}
-    func_run_hooks_result=$func_quote_for_eval_result
+    $_G_rc_run_hooks && func_run_hooks_result=$_G_hook_result
 }
 
 
@@ -1557,10 +1655,16 @@ func_run_hooks ()
 ## --------------- ##
 
 # In order to add your own option parsing hooks, you must accept the
-# full positional parameter list in your hook function, remove any
-# options that you action, and then pass back the remaining unprocessed
+# full positional parameter list in your hook function, you may remove/edit
+# any options that you action, and then pass back the remaining unprocessed
 # options in '<hooked_function_name>_result', escaped suitably for
-# 'eval'.  Like this:
+# 'eval'.  In this case you also must return $EXIT_SUCCESS to let the
+# hook's caller know that it should pay attention to
+# '<hooked_function_name>_result'.  Returning $EXIT_FAILURE signalizes that
+# arguments are left untouched by the hook and therefore caller will ignore the
+# result variable.
+#
+# Like this:
 #
 #    my_options_prep ()
 #    {
@@ -1570,9 +1674,11 @@ func_run_hooks ()
 #        usage_message=$usage_message'
 #      -s, --silent       don'\''t print informational messages
 #    '
-#
-#        func_quote_for_eval ${1+"$@"}
-#        my_options_prep_result=$func_quote_for_eval_result
+#        # No change in '$@' (ignored completely by this hook).  There is
+#        # no need to do the equivalent (but slower) action:
+#        # func_quote eval ${1+"$@"}
+#        # my_options_prep_result=$func_quote_result
+#        false
 #    }
 #    func_add_hook func_options_prep my_options_prep
 #
@@ -1581,25 +1687,37 @@ func_run_hooks ()
 #    {
 #        $debug_cmd
 #
+#        args_changed=false
+#
 #        # Note that for efficiency, we parse as many options as we can
 #        # recognise in a loop before passing the remainder back to the
 #        # caller on the first unrecognised argument we encounter.
 #        while test $# -gt 0; do
 #          opt=$1; shift
 #          case $opt in
-#            --silent|-s) opt_silent=: ;;
+#            --silent|-s) opt_silent=:
+#                         args_changed=:
+#                         ;;
 #            # Separate non-argument short options:
 #            -s*)         func_split_short_opt "$_G_opt"
 #                         set dummy "$func_split_short_opt_name" \
 #                             "-$func_split_short_opt_arg" ${1+"$@"}
 #                         shift
+#                         args_changed=:
 #                         ;;
-#            *)            set dummy "$_G_opt" "$*"; shift; break ;;
+#            *)           # Make sure the first unrecognised option "$_G_opt"
+#                         # is added back to "$@", we could need that later
+#                         # if $args_changed is true.
+#                         set dummy "$_G_opt" ${1+"$@"}; shift; break ;;
 #          esac
 #        done
 #
-#        func_quote_for_eval ${1+"$@"}
-#        my_silent_option_result=$func_quote_for_eval_result
+#        if $args_changed; then
+#          func_quote eval ${1+"$@"}
+#          my_silent_option_result=$func_quote_result
+#        fi
+#
+#        $args_changed
 #    }
 #    func_add_hook func_parse_options my_silent_option
 #
@@ -1611,16 +1729,32 @@ func_run_hooks ()
 #        $opt_silent && $opt_verbose && func_fatal_help "\
 #    '--silent' and '--verbose' options are mutually exclusive."
 #
-#        func_quote_for_eval ${1+"$@"}
-#        my_option_validation_result=$func_quote_for_eval_result
+#        false
 #    }
 #    func_add_hook func_validate_options my_option_validation
 #
-# You'll alse need to manually amend $usage_message to reflect the extra
+# You'll also need to manually amend $usage_message to reflect the extra
 # options you parse.  It's preferable to append if you can, so that
 # multiple option parsing hooks can be added safely.
 
 
+# func_options_finish [ARG]...
+# ----------------------------
+# Finishing the option parse loop (call 'func_options' hooks ATM).
+func_options_finish ()
+{
+    $debug_cmd
+
+    _G_func_options_finish_exit=false
+    if func_run_hooks func_options ${1+"$@"}; then
+      func_options_finish_result=$func_run_hooks_result
+      _G_func_options_finish_exit=:
+    fi
+
+    $_G_func_options_finish_exit
+}
+
+
 # func_options [ARG]...
 # ---------------------
 # All the functions called inside func_options are hookable. See the
@@ -1630,17 +1764,28 @@ func_options ()
 {
     $debug_cmd
 
-    func_options_prep ${1+"$@"}
-    eval func_parse_options \
-        ${func_options_prep_result+"$func_options_prep_result"}
-    eval func_validate_options \
-        ${func_parse_options_result+"$func_parse_options_result"}
+    _G_rc_options=false
 
-    eval func_run_hooks func_options \
-        ${func_validate_options_result+"$func_validate_options_result"}
+    for my_func in options_prep parse_options validate_options options_finish
+    do
+      if eval func_$my_func '${1+"$@"}'; then
+        eval _G_res_var='$'"func_${my_func}_result"
+        eval set dummy "$_G_res_var" ; shift
+        _G_rc_options=:
+      fi
+    done
 
-    # save modified positional parameters for caller
-    func_options_result=$func_run_hooks_result
+    # Save modified positional parameters for caller.  As a top-level
+    # options-parser function we always need to set the 'func_options_result'
+    # variable (regardless the $_G_rc_options value).
+    if $_G_rc_options; then
+      func_options_result=$_G_res_var
+    else
+      func_quote eval ${1+"$@"}
+      func_options_result=$func_quote_result
+    fi
+
+    $_G_rc_options
 }
 
 
@@ -1649,9 +1794,9 @@ func_options ()
 # All initialisations required before starting the option parse loop.
 # Note that when calling hook functions, we pass through the list of
 # positional parameters.  If a hook function modifies that list, and
-# needs to propogate that back to rest of this script, then the complete
+# needs to propagate that back to rest of this script, then the complete
 # modified list must be put in 'func_run_hooks_result' before
-# returning.
+# returning $EXIT_SUCCESS (otherwise $EXIT_FAILURE is returned).
 func_hookable func_options_prep
 func_options_prep ()
 {
@@ -1661,10 +1806,14 @@ func_options_prep ()
     opt_verbose=false
     opt_warning_types=
 
-    func_run_hooks func_options_prep ${1+"$@"}
+    _G_rc_options_prep=false
+    if func_run_hooks func_options_prep ${1+"$@"}; then
+      _G_rc_options_prep=:
+      # save modified positional parameters for caller
+      func_options_prep_result=$func_run_hooks_result
+    fi
 
-    # save modified positional parameters for caller
-    func_options_prep_result=$func_run_hooks_result
+    $_G_rc_options_prep
 }
 
 
@@ -1678,18 +1827,20 @@ func_parse_options ()
 
     func_parse_options_result=
 
+    _G_rc_parse_options=false
     # this just eases exit handling
     while test $# -gt 0; do
       # Defer to hook functions for initial option parsing, so they
       # get priority in the event of reusing an option name.
-      func_run_hooks func_parse_options ${1+"$@"}
-
-      # Adjust func_parse_options positional parameters to match
-      eval set dummy "$func_run_hooks_result"; shift
+      if func_run_hooks func_parse_options ${1+"$@"}; then
+        eval set dummy "$func_run_hooks_result"; shift
+        _G_rc_parse_options=:
+      fi
 
       # Break out of the loop if we already parsed every option.
       test $# -gt 0 || break
 
+      _G_match_parse_options=:
       _G_opt=$1
       shift
       case $_G_opt in
@@ -1704,7 +1855,10 @@ func_parse_options ()
                      ;;
 
         --warnings|--warning|-W)
-                      test $# = 0 && func_missing_arg $_G_opt && break
+                      if test $# = 0 && func_missing_arg $_G_opt; then
+                        _G_rc_parse_options=:
+                        break
+                      fi
                       case " $warning_categories $1" in
                         *" $1 "*)
                           # trailing space prevents matching last $1 above
@@ -1757,15 +1911,25 @@ func_parse_options ()
                       shift
                       ;;
 
-        --)           break ;;
+        --)           _G_rc_parse_options=: ; break ;;
         -*)           func_fatal_help "unrecognised option: '$_G_opt'" ;;
-        *)            set dummy "$_G_opt" ${1+"$@"}; shift; break ;;
+        *)            set dummy "$_G_opt" ${1+"$@"}; shift
+                      _G_match_parse_options=false
+                      break
+                      ;;
       esac
+
+      $_G_match_parse_options && _G_rc_parse_options=:
     done
 
-    # save modified positional parameters for caller
-    func_quote_for_eval ${1+"$@"}
-    func_parse_options_result=$func_quote_for_eval_result
+
+    if $_G_rc_parse_options; then
+      # save modified positional parameters for caller
+      func_quote eval ${1+"$@"}
+      func_parse_options_result=$func_quote_result
+    fi
+
+    $_G_rc_parse_options
 }
 
 
@@ -1778,16 +1942,21 @@ func_validate_options ()
 {
     $debug_cmd
 
+    _G_rc_validate_options=false
+
     # Display all warnings if -W was not given.
     test -n "$opt_warning_types" || opt_warning_types=" $warning_categories"
 
-    func_run_hooks func_validate_options ${1+"$@"}
+    if func_run_hooks func_validate_options ${1+"$@"}; then
+      # save modified positional parameters for caller
+      func_validate_options_result=$func_run_hooks_result
+      _G_rc_validate_options=:
+    fi
 
     # Bail if the options were screwed!
     $exit_cmd $EXIT_FAILURE
 
-    # save modified positional parameters for caller
-    func_validate_options_result=$func_run_hooks_result
+    $_G_rc_validate_options
 }
 
 
@@ -2068,7 +2237,7 @@ include the following information:
        compiler:       $LTCC
        compiler flags: $LTCFLAGS
        linker:         $LD (gnu? $with_gnu_ld)
-       version:        $progname $scriptversion Debian-2.4.6-2
+       version:        $progname $scriptversion Debian-2.4.6-4
        automake:       `($AUTOMAKE --version) 2>/dev/null |$SED 1q`
        autoconf:       `($AUTOCONF --version) 2>/dev/null |$SED 1q`
 
@@ -2270,6 +2439,8 @@ libtool_options_prep ()
     nonopt=
     preserve_args=
 
+    _G_rc_lt_options_prep=:
+
     # Shorthand for --mode=foo, only valid as the first argument
     case $1 in
     clean|clea|cle|cl)
@@ -2293,11 +2464,18 @@ libtool_options_prep ()
     uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u)
       shift; set dummy --mode uninstall ${1+"$@"}; shift
       ;;
+    *)
+      _G_rc_lt_options_prep=false
+      ;;
     esac
 
-    # Pass back the list of options.
-    func_quote_for_eval ${1+"$@"}
-    libtool_options_prep_result=$func_quote_for_eval_result
+    if $_G_rc_lt_options_prep; then
+      # Pass back the list of options.
+      func_quote eval ${1+"$@"}
+      libtool_options_prep_result=$func_quote_result
+    fi
+
+    $_G_rc_lt_options_prep
 }
 func_add_hook func_options_prep libtool_options_prep
 
@@ -2309,9 +2487,12 @@ libtool_parse_options ()
 {
     $debug_cmd
 
+    _G_rc_lt_parse_options=false
+
     # Perform our own loop to consume as many options as possible in
     # each iteration.
     while test $# -gt 0; do
+      _G_match_lt_parse_options=:
       _G_opt=$1
       shift
       case $_G_opt in
@@ -2386,15 +2567,22 @@ libtool_parse_options ()
                         func_append preserve_args " $_G_opt"
                         ;;
 
-       # An option not handled by this hook function:
-        *)             set dummy "$_G_opt" ${1+"$@"};  shift; break  ;;
+        # An option not handled by this hook function:
+        *)              set dummy "$_G_opt" ${1+"$@"} ; shift
+                        _G_match_lt_parse_options=false
+                        break
+                        ;;
       esac
+      $_G_match_lt_parse_options && _G_rc_lt_parse_options=:
     done
 
+    if $_G_rc_lt_parse_options; then
+      # save modified positional parameters for caller
+      func_quote eval ${1+"$@"}
+      libtool_parse_options_result=$func_quote_result
+    fi
 
-    # save modified positional parameters for caller
-    func_quote_for_eval ${1+"$@"}
-    libtool_parse_options_result=$func_quote_for_eval_result
+    $_G_rc_lt_parse_options
 }
 func_add_hook func_parse_options libtool_parse_options
 
@@ -2451,8 +2639,8 @@ libtool_validate_options ()
     }
 
     # Pass back the unparsed argument list
-    func_quote_for_eval ${1+"$@"}
-    libtool_validate_options_result=$func_quote_for_eval_result
+    func_quote eval ${1+"$@"}
+    libtool_validate_options_result=$func_quote_result
 }
 func_add_hook func_validate_options libtool_validate_options
 
@@ -3418,8 +3606,8 @@ func_mode_compile ()
       esac
     done
 
-    func_quote_for_eval "$libobj"
-    test "X$libobj" != "X$func_quote_for_eval_result" \
+    func_quote_arg pretty "$libobj"
+    test "X$libobj" != "X$func_quote_arg_result" \
       && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"'   &()|`$[]' \
       && func_warning "libobj name '$libobj' may not contain shell special characters."
     func_dirname_and_basename "$obj" "/" ""
@@ -3492,8 +3680,8 @@ compiler."
 
     func_to_tool_file "$srcfile" func_convert_file_msys_to_w32
     srcfile=$func_to_tool_file_result
-    func_quote_for_eval "$srcfile"
-    qsrcfile=$func_quote_for_eval_result
+    func_quote_arg pretty "$srcfile"
+    qsrcfile=$func_quote_arg_result
 
     # Only build a PIC object if we are building libtool libraries.
     if test yes = "$build_libtool_libs"; then
@@ -4096,8 +4284,8 @@ func_mode_install ()
        case $nonopt in *shtool*) :;; *) false;; esac
     then
       # Aesthetically quote it.
-      func_quote_for_eval "$nonopt"
-      install_prog="$func_quote_for_eval_result "
+      func_quote_arg pretty "$nonopt"
+      install_prog="$func_quote_arg_result "
       arg=$1
       shift
     else
@@ -4107,8 +4295,8 @@ func_mode_install ()
 
     # The real first argument should be the name of the installation program.
     # Aesthetically quote it.
-    func_quote_for_eval "$arg"
-    func_append install_prog "$func_quote_for_eval_result"
+    func_quote_arg pretty "$arg"
+    func_append install_prog "$func_quote_arg_result"
     install_shared_prog=$install_prog
     case " $install_prog " in
       *[\\\ /]cp\ *) install_cp=: ;;
@@ -4165,12 +4353,12 @@ func_mode_install ()
       esac
 
       # Aesthetically quote the argument.
-      func_quote_for_eval "$arg"
-      func_append install_prog " $func_quote_for_eval_result"
+      func_quote_arg pretty "$arg"
+      func_append install_prog " $func_quote_arg_result"
       if test -n "$arg2"; then
-       func_quote_for_eval "$arg2"
+       func_quote_arg pretty "$arg2"
       fi
-      func_append install_shared_prog " $func_quote_for_eval_result"
+      func_append install_shared_prog " $func_quote_arg_result"
     done
 
     test -z "$install_prog" && \
@@ -4181,8 +4369,8 @@ func_mode_install ()
 
     if test -n "$install_override_mode" && $no_mode; then
       if $install_cp; then :; else
-       func_quote_for_eval "$install_override_mode"
-       func_append install_shared_prog " -m $func_quote_for_eval_result"
+       func_quote_arg pretty "$install_override_mode"
+       func_append install_shared_prog " -m $func_quote_arg_result"
       fi
     fi
 
@@ -4478,8 +4666,8 @@ func_mode_install ()
                relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'`
 
                $opt_quiet || {
-                 func_quote_for_expand "$relink_command"
-                 eval "func_echo $func_quote_for_expand_result"
+                 func_quote_arg expand,pretty "$relink_command"
+                 eval "func_echo $func_quote_arg_result"
                }
                if eval "$relink_command"; then :
                  else
@@ -5258,7 +5446,8 @@ else
   if test \"\$libtool_execute_magic\" != \"$magic\"; then
     file=\"\$0\""
 
-    qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"`
+    func_quote_arg pretty "$ECHO"
+    qECHO=$func_quote_arg_result
     $ECHO "\
 
 # A function that is used when there is no print builtin or printf.
@@ -5268,7 +5457,7 @@ func_fallback_echo ()
 \$1
 _LTECHO_EOF'
 }
-    ECHO=\"$qECHO\"
+    ECHO=$qECHO
   fi
 
 # Very basic option parsing. These options are (a) specific to
@@ -6611,9 +6800,9 @@ func_mode_link ()
     while test "$#" -gt 0; do
       arg=$1
       shift
-      func_quote_for_eval "$arg"
-      qarg=$func_quote_for_eval_unquoted_result
-      func_append libtool_args " $func_quote_for_eval_result"
+      func_quote_arg pretty,unquoted "$arg"
+      qarg=$func_quote_arg_unquoted_result
+      func_append libtool_args " $func_quote_arg_result"
 
       # If the previous option needs an argument, assign it.
       if test -n "$prev"; then
@@ -7211,9 +7400,9 @@ func_mode_link ()
        save_ifs=$IFS; IFS=,
        for flag in $args; do
          IFS=$save_ifs
-          func_quote_for_eval "$flag"
-         func_append arg " $func_quote_for_eval_result"
-         func_append compiler_flags " $func_quote_for_eval_result"
+          func_quote_arg pretty "$flag"
+         func_append arg " $func_quote_arg_result"
+         func_append compiler_flags " $func_quote_arg_result"
        done
        IFS=$save_ifs
        func_stripname ' ' '' "$arg"
@@ -7227,10 +7416,10 @@ func_mode_link ()
        save_ifs=$IFS; IFS=,
        for flag in $args; do
          IFS=$save_ifs
-          func_quote_for_eval "$flag"
-         func_append arg " $wl$func_quote_for_eval_result"
-         func_append compiler_flags " $wl$func_quote_for_eval_result"
-         func_append linker_flags " $func_quote_for_eval_result"
+          func_quote_arg pretty "$flag"
+         func_append arg " $wl$func_quote_arg_result"
+         func_append compiler_flags " $wl$func_quote_arg_result"
+         func_append linker_flags " $func_quote_arg_result"
        done
        IFS=$save_ifs
        func_stripname ' ' '' "$arg"
@@ -7254,8 +7443,8 @@ func_mode_link ()
 
       # -msg_* for osf cc
       -msg_*)
-       func_quote_for_eval "$arg"
-       arg=$func_quote_for_eval_result
+       func_quote_arg pretty "$arg"
+       arg=$func_quote_arg_result
        ;;
 
       # Flags to be passed through unchanged, with rationale:
@@ -7279,8 +7468,8 @@ func_mode_link ()
       -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \
       -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \
       -specs=*|-fsanitize=*)
-        func_quote_for_eval "$arg"
-       arg=$func_quote_for_eval_result
+        func_quote_arg pretty "$arg"
+       arg=$func_quote_arg_result
         func_append compile_command " $arg"
         func_append finalize_command " $arg"
         func_append compiler_flags " $arg"
@@ -7301,15 +7490,15 @@ func_mode_link ()
          continue
         else
          # Otherwise treat like 'Some other compiler flag' below
-         func_quote_for_eval "$arg"
-         arg=$func_quote_for_eval_result
+         func_quote_arg pretty "$arg"
+         arg=$func_quote_arg_result
         fi
        ;;
 
       # Some other compiler flag.
       -* | +*)
-        func_quote_for_eval "$arg"
-       arg=$func_quote_for_eval_result
+        func_quote_arg pretty "$arg"
+       arg=$func_quote_arg_result
        ;;
 
       *.$objext)
@@ -7429,8 +7618,8 @@ func_mode_link ()
       *)
        # Unknown arguments in both finalize_command and compile_command need
        # to be aesthetically quoted because they are evaled later.
-       func_quote_for_eval "$arg"
-       arg=$func_quote_for_eval_result
+       func_quote_arg pretty "$arg"
+       arg=$func_quote_arg_result
        ;;
       esac # arg
 
@@ -9942,8 +10131,8 @@ EOF
            for cmd in $concat_cmds; do
              IFS=$save_ifs
              $opt_quiet || {
-                 func_quote_for_expand "$cmd"
-                 eval "func_echo $func_quote_for_expand_result"
+                 func_quote_arg expand,pretty "$cmd"
+                 eval "func_echo $func_quote_arg_result"
              }
              $opt_dry_run || eval "$cmd" || {
                lt_exit=$?
@@ -10036,8 +10225,8 @@ EOF
          eval cmd=\"$cmd\"
          IFS=$save_ifs
          $opt_quiet || {
-           func_quote_for_expand "$cmd"
-           eval "func_echo $func_quote_for_expand_result"
+           func_quote_arg expand,pretty "$cmd"
+           eval "func_echo $func_quote_arg_result"
          }
          $opt_dry_run || eval "$cmd" || {
            lt_exit=$?
@@ -10511,12 +10700,12 @@ EOF
          elif eval var_value=\$$var; test -z "$var_value"; then
            relink_command="$var=; export $var; $relink_command"
          else
-           func_quote_for_eval "$var_value"
-           relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+           func_quote_arg pretty "$var_value"
+           relink_command="$var=$func_quote_arg_result; export $var; $relink_command"
          fi
        done
-       relink_command="(cd `pwd`; $relink_command)"
-       relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
+       func_quote_arg pretty,unquoted "(cd `pwd`; $relink_command)"
+       relink_command=$func_quote_arg_unquoted_result
       fi
 
       # Only actually do things if not in dry run mode.
@@ -10756,13 +10945,14 @@ EOF
        elif eval var_value=\$$var; test -z "$var_value"; then
          relink_command="$var=; export $var; $relink_command"
        else
-         func_quote_for_eval "$var_value"
-         relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+         func_quote_arg pretty,unquoted "$var_value"
+         relink_command="$var=$func_quote_arg_unquoted_result; export $var; $relink_command"
        fi
       done
       # Quote the link command for shipping.
       relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)"
-      relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
+      func_quote_arg pretty,unquoted "$relink_command"
+      relink_command=$func_quote_arg_unquoted_result
       if test yes = "$hardcode_automatic"; then
        relink_command=
       fi
index ee80844..e67ed69 100644 (file)
@@ -6438,7 +6438,7 @@ if test yes != "$_lt_caught_CXX_error"; then
       # Commands to make compiler produce verbose output that lists
       # what "hidden" libraries, object files and flags are used when
       # linking a shared library.
-      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"'
 
     else
       GXX=no
@@ -6813,7 +6813,7 @@ if test yes != "$_lt_caught_CXX_error"; then
             # explicitly linking system object files so we need to strip them
             # from the output so that they don't get included in the library
             # dependencies.
-            output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+            output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
             ;;
           *)
             if test yes = "$GXX"; then
@@ -6878,7 +6878,7 @@ if test yes != "$_lt_caught_CXX_error"; then
            # explicitly linking system object files so we need to strip them
            # from the output so that they don't get included in the library
            # dependencies.
-           output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+           output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
            ;;
           *)
            if test yes = "$GXX"; then
@@ -7217,7 +7217,7 @@ if test yes != "$_lt_caught_CXX_error"; then
              # Commands to make compiler produce verbose output that lists
              # what "hidden" libraries, object files and flags are used when
              # linking a shared library.
-             output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+             output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"'
 
            else
              # FIXME: insert proper C++ library support
@@ -7301,7 +7301,7 @@ if test yes != "$_lt_caught_CXX_error"; then
                # Commands to make compiler produce verbose output that lists
                # what "hidden" libraries, object files and flags are used when
                # linking a shared library.
-               output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+               output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"'
              else
                # g++ 2.7 appears to require '-G' NOT '-shared' on this
                # platform.
@@ -7312,7 +7312,7 @@ if test yes != "$_lt_caught_CXX_error"; then
                # Commands to make compiler produce verbose output that lists
                # what "hidden" libraries, object files and flags are used when
                # linking a shared library.
-               output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+               output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"'
              fi
 
              _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir'
diff --git a/missing b/missing
index f62bbae..625aeb1 100755 (executable)
--- a/missing
+++ b/missing
@@ -1,9 +1,9 @@
 #! /bin/sh
 # Common wrapper for a few potentially missing GNU programs.
 
-scriptversion=2013-10-28.13; # UTC
+scriptversion=2018-03-07.03; # UTC
 
-# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+# Copyright (C) 1996-2018 Free Software Foundation, Inc.
 # Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
 
 # This program is free software; you can redistribute it and/or modify
@@ -17,7 +17,7 @@ scriptversion=2013-10-28.13; # UTC
 # GNU General Public License for more details.
 
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 # As a special exception to the GNU General Public License, if you
 # distribute this file as part of a program that contains a
@@ -101,9 +101,9 @@ else
   exit $st
 fi
 
-perl_URL=http://www.perl.org/
-flex_URL=http://flex.sourceforge.net/
-gnu_software_URL=http://www.gnu.org/software
+perl_URL=https://www.perl.org/
+flex_URL=https://github.com/westes/flex
+gnu_software_URL=https://www.gnu.org/software
 
 program_details ()
 {
@@ -207,9 +207,9 @@ give_advice "$1" | sed -e '1s/^/WARNING: /' \
 exit $st
 
 # Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
+# eval: (add-hook 'before-save-hook 'time-stamp)
 # time-stamp-start: "scriptversion="
 # time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-time-zone: "UTC"
+# time-stamp-time-zone: "UTC0"
 # time-stamp-end: "; # UTC"
 # End:
index 95a1dd0..09ba804 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -354,8 +354,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
          *config.status*) \
            cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
          *) \
-           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
-           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
        esac;
 
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -381,7 +381,10 @@ ctags CTAGS:
 cscope cscopelist:
 
 
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
        @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        list='$(DISTFILES)'; \
@@ -446,8 +449,8 @@ distclean-generic:
 maintainer-clean-generic:
        @echo "This command is intended for maintainers to use"
        @echo "it deletes files that may require special tools to rebuild."
-@ENABLE_PYTHON_BINDINGS_FALSE@install-exec-local:
 @ENABLE_PYTHON_BINDINGS_FALSE@clean-local:
+@ENABLE_PYTHON_BINDINGS_FALSE@install-exec-local:
 @ENABLE_PYTHON_BINDINGS_FALSE@uninstall-local:
 clean: clean-am
 
index d6f5803..9372bb7 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -385,8 +385,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
          *config.status*) \
            cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
          *) \
-           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
-           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
        esac;
 
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -445,7 +445,10 @@ ctags CTAGS:
 cscope cscopelist:
 
 
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
        @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        list='$(DISTFILES)'; \
index fc2f2e5..1e84688 100644 (file)
@@ -32,7 +32,7 @@
 
 #ifdef HAVE_LIBXML2
 
-#include <libxml/HTMLparser.h>
+#  include <libxml/HTMLparser.h>
 
 #endif // HAVE_LIBXML2
 
index b3e35ef..8dcf580 100644 (file)
 
 #include <sys/stat.h>
 #ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
+#  include <sys/socket.h>
 #endif // HAVE_SYS_SOCKET_H
 #ifdef HAVE_NETDB_H
-#include <netdb.h>
+#  include <netdb.h>
 #endif // HAVE_NETDB_H
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 #ifdef HAVE_FCNTL_H
-#include <fcntl.h>
+#  include <fcntl.h>
 #endif // HAVE_FCNTL_H
 #ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
+#  include <netinet/in.h>
 #endif // HAVE_NETINET_IN_H
 #include <netinet/tcp.h>
 #ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
+#  include <arpa/inet.h>
 #endif // HAVE_ARPA_INET_H
 
 #include <cassert>
@@ -64,7 +64,7 @@
 #include "template.h"
 
 #ifndef O_BINARY
-#define O_BINARY (0)
+#  define O_BINARY (0)
 #endif // O_BINARY
 
 namespace nghttp2 {
@@ -888,7 +888,9 @@ int Http2Handler::verify_npn_result() {
   const unsigned char *next_proto = nullptr;
   unsigned int next_proto_len;
   // Check the negotiated protocol in NPN or ALPN
+#ifndef OPENSSL_NO_NEXTPROTONEG
   SSL_get0_next_proto_negotiated(ssl_, &next_proto, &next_proto_len);
+#endif // !OPENSSL_NO_NEXTPROTONEG
   for (int i = 0; i < 2; ++i) {
     if (next_proto) {
       auto proto = StringRef{next_proto, next_proto_len};
@@ -1982,6 +1984,7 @@ HttpServer::HttpServer(const Config *config) : config_(config) {
   };
 }
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
 namespace {
 int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
                   void *arg) {
@@ -1991,6 +1994,7 @@ int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
   return SSL_TLSEXT_ERR_OK;
 }
 } // namespace
+#endif // !OPENSSL_NO_NEXTPROTONEG
 
 namespace {
 int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
@@ -2205,7 +2209,9 @@ int HttpServer::run() {
 
     next_proto = util::get_default_alpn();
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
     SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, &next_proto);
+#endif // !OPENSSL_NO_NEXTPROTONEG
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
     // ALPN selection callback
     SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, this);
index 88c31c4..769f9b9 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -155,7 +155,44 @@ mkinstalldirs = $(install_sh) -d
 CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES = libnghttp2_asio.pc
 CONFIG_CLEAN_VPATH_FILES =
+@ENABLE_APP_TRUE@am__EXEEXT_1 = nghttp$(EXEEXT) nghttpd$(EXEEXT) \
+@ENABLE_APP_TRUE@      nghttpx$(EXEEXT) h2load$(EXEEXT)
+@ENABLE_HPACK_TOOLS_TRUE@am__EXEEXT_2 = inflatehd$(EXEEXT) \
+@ENABLE_HPACK_TOOLS_TRUE@      deflatehd$(EXEEXT)
+am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" \
+       "$(DESTDIR)$(pkgconfigdir)"
+@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@am__EXEEXT_3 =  \
+@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@     nghttpx-unittest$(EXEEXT)
+PROGRAMS = $(bin_PROGRAMS)
 LIBRARIES = $(noinst_LIBRARIES)
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+LTLIBRARIES = $(lib_LTLIBRARIES)
 ARFLAGS = cru
 AM_V_AR = $(am__v_AR_@AM_V@)
 am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
@@ -253,36 +290,6 @@ am__libnghttpx_a_SOURCES_DIST = util.cc util.h http2.cc http2.h \
 @ENABLE_APP_TRUE@      $(am__objects_1)
 @ENABLE_APP_TRUE@am_libnghttpx_a_OBJECTS = $(am__objects_2)
 libnghttpx_a_OBJECTS = $(am_libnghttpx_a_OBJECTS)
-am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
-am__vpath_adj = case $$p in \
-    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
-    *) f=$$p;; \
-  esac;
-am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
-am__install_max = 40
-am__nobase_strip_setup = \
-  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
-am__nobase_strip = \
-  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
-am__nobase_list = $(am__nobase_strip_setup); \
-  for p in $$list; do echo "$$p $$p"; done | \
-  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
-  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
-    if (++n[$$2] == $(am__install_max)) \
-      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
-    END { for (dir in files) print dir, files[dir] }'
-am__base_list = \
-  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
-  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
-am__uninstall_files_from_dir = { \
-  test -z "$$files" \
-    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
-    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
-         $(am__cd) "$$dir" && rm -f $$files; }; \
-  }
-am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \
-       "$(DESTDIR)$(pkgconfigdir)"
-LTLIBRARIES = $(lib_LTLIBRARIES)
 am__DEPENDENCIES_1 =
 @ENABLE_ASIO_LIB_TRUE@libnghttp2_asio_la_DEPENDENCIES =  \
 @ENABLE_ASIO_LIB_TRUE@ $(top_builddir)/lib/libnghttp2.la \
@@ -352,13 +359,6 @@ libnghttp2_asio_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
        $(AM_CXXFLAGS) $(CXXFLAGS) $(libnghttp2_asio_la_LDFLAGS) \
        $(LDFLAGS) -o $@
 @ENABLE_ASIO_LIB_TRUE@am_libnghttp2_asio_la_rpath = -rpath $(libdir)
-@ENABLE_APP_TRUE@am__EXEEXT_1 = nghttp$(EXEEXT) nghttpd$(EXEEXT) \
-@ENABLE_APP_TRUE@      nghttpx$(EXEEXT) h2load$(EXEEXT)
-@ENABLE_HPACK_TOOLS_TRUE@am__EXEEXT_2 = inflatehd$(EXEEXT) \
-@ENABLE_HPACK_TOOLS_TRUE@      deflatehd$(EXEEXT)
-@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@am__EXEEXT_3 =  \
-@ENABLE_APP_TRUE@@HAVE_CUNIT_TRUE@     nghttpx-unittest$(EXEEXT)
-PROGRAMS = $(bin_PROGRAMS)
 am__deflatehd_SOURCES_DIST = deflatehd.cc comp_helper.c comp_helper.h
 @ENABLE_HPACK_TOOLS_TRUE@am__objects_3 = comp_helper.$(OBJEXT)
 @ENABLE_HPACK_TOOLS_TRUE@am_deflatehd_OBJECTS = deflatehd.$(OBJEXT) \
@@ -468,7 +468,104 @@ am__v_at_0 = @
 am__v_at_1 = 
 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/HtmlParser.Po \
+       ./$(DEPDIR)/HttpServer.Po ./$(DEPDIR)/app_helper.Po \
+       ./$(DEPDIR)/comp_helper.Po ./$(DEPDIR)/deflatehd.Po \
+       ./$(DEPDIR)/h2load.Po ./$(DEPDIR)/h2load_http1_session.Po \
+       ./$(DEPDIR)/h2load_http2_session.Po ./$(DEPDIR)/http2.Po \
+       ./$(DEPDIR)/inflatehd.Po \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_client_request.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_client_request_impl.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_client_response.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_client_response_impl.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_client_session.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_impl.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_tcp_impl.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_tls_impl.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_client_stream.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_client_tls_context.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_common.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_io_service_pool.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_server.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2_handler.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2_impl.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_server_request.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_server_request_handler.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_server_request_impl.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_server_response.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_server_response_impl.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_server_serve_mux.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_server_stream.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-asio_server_tls_context.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-http2.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-timegm.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-tls.Plo \
+       ./$(DEPDIR)/libnghttp2_asio_la-util.Plo \
+       ./$(DEPDIR)/libnghttpx_a-app_helper.Po \
+       ./$(DEPDIR)/libnghttpx_a-http2.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_accept_handler.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_api_downstream_connection.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_client_handler.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_config.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_connect_blocker.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_connection.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_connection_handler.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_dns_resolver.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_dns_tracker.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_downstream.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_downstream_connection.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_downstream_connection_pool.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_downstream_queue.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_dual_dns_resolver.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_exec.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_health_monitor_downstream_connection.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_http.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_http2_downstream_connection.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_http2_session.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_http2_upstream.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_http_downstream_connection.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_https_upstream.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_io_control.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_live_check.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_log.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_log_config.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_memcached_connection.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_memcached_dispatcher.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_mruby.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_env.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_request.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_response.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_rate_limit.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_router.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_signal.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_tls.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_worker.Po \
+       ./$(DEPDIR)/libnghttpx_a-shrpx_worker_process.Po \
+       ./$(DEPDIR)/libnghttpx_a-timegm.Po \
+       ./$(DEPDIR)/libnghttpx_a-tls.Po \
+       ./$(DEPDIR)/libnghttpx_a-util.Po \
+       ./$(DEPDIR)/libnghttpx_a-xsi_strerror.Po ./$(DEPDIR)/nghttp.Po \
+       ./$(DEPDIR)/nghttp2_gzip.Po ./$(DEPDIR)/nghttpd.Po \
+       ./$(DEPDIR)/nghttpx-shrpx.Po \
+       ./$(DEPDIR)/nghttpx_unittest-base64_test.Po \
+       ./$(DEPDIR)/nghttpx_unittest-buffer_test.Po \
+       ./$(DEPDIR)/nghttpx_unittest-http2_test.Po \
+       ./$(DEPDIR)/nghttpx_unittest-memchunk_test.Po \
+       ./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po \
+       ./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po \
+       ./$(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po \
+       ./$(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po \
+       ./$(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po \
+       ./$(DEPDIR)/nghttpx_unittest-shrpx_http_test.Po \
+       ./$(DEPDIR)/nghttpx_unittest-shrpx_router_test.Po \
+       ./$(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Po \
+       ./$(DEPDIR)/nghttpx_unittest-shrpx_worker_test.Po \
+       ./$(DEPDIR)/nghttpx_unittest-template_test.Po \
+       ./$(DEPDIR)/nghttpx_unittest-util_test.Po \
+       ./$(DEPDIR)/timegm.Po ./$(DEPDIR)/tls.Po ./$(DEPDIR)/util.Po
 am__mv = mv -f
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
        $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -537,7 +634,7 @@ am__recursive_targets = \
   $(RECURSIVE_CLEAN_TARGETS) \
   $(am__extra_recursive_targets)
 AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
-       check recheck distdir
+       check recheck distdir distdir-am
 am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
 # Read a list of newline-separated strings from the standard input,
 # and print each of them once, without duplicates.  Input order is
@@ -1171,8 +1268,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
          *config.status*) \
            cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
          *) \
-           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
-           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
        esac;
 
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -1185,52 +1282,6 @@ $(ACLOCAL_M4):  $(am__aclocal_m4_deps)
 $(am__aclocal_m4_deps):
 libnghttp2_asio.pc: $(top_builddir)/config.status $(srcdir)/libnghttp2_asio.pc.in
        cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
-
-clean-noinstLIBRARIES:
-       -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
-
-libnghttpx.a: $(libnghttpx_a_OBJECTS) $(libnghttpx_a_DEPENDENCIES) $(EXTRA_libnghttpx_a_DEPENDENCIES) 
-       $(AM_V_at)-rm -f libnghttpx.a
-       $(AM_V_AR)$(libnghttpx_a_AR) libnghttpx.a $(libnghttpx_a_OBJECTS) $(libnghttpx_a_LIBADD)
-       $(AM_V_at)$(RANLIB) libnghttpx.a
-
-install-libLTLIBRARIES: $(lib_LTLIBRARIES)
-       @$(NORMAL_INSTALL)
-       @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
-       list2=; for p in $$list; do \
-         if test -f $$p; then \
-           list2="$$list2 $$p"; \
-         else :; fi; \
-       done; \
-       test -z "$$list2" || { \
-         echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
-         $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
-         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
-         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
-       }
-
-uninstall-libLTLIBRARIES:
-       @$(NORMAL_UNINSTALL)
-       @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
-       for p in $$list; do \
-         $(am__strip_dir) \
-         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
-         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
-       done
-
-clean-libLTLIBRARIES:
-       -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
-       @list='$(lib_LTLIBRARIES)'; \
-       locs=`for p in $$list; do echo $$p; done | \
-             sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
-             sort -u`; \
-       test -z "$$locs" || { \
-         echo rm -f $${locs}; \
-         rm -f $${locs}; \
-       }
-
-libnghttp2_asio.la: $(libnghttp2_asio_la_OBJECTS) $(libnghttp2_asio_la_DEPENDENCIES) $(EXTRA_libnghttp2_asio_la_DEPENDENCIES) 
-       $(AM_V_CXXLD)$(libnghttp2_asio_la_LINK) $(am_libnghttp2_asio_la_rpath) $(libnghttp2_asio_la_OBJECTS) $(libnghttp2_asio_la_LIBADD) $(LIBS)
 install-binPROGRAMS: $(bin_PROGRAMS)
        @$(NORMAL_INSTALL)
        @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
@@ -1290,6 +1341,52 @@ clean-checkPROGRAMS:
        echo " rm -f" $$list; \
        rm -f $$list
 
+clean-noinstLIBRARIES:
+       -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+       @$(NORMAL_INSTALL)
+       @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+       list2=; for p in $$list; do \
+         if test -f $$p; then \
+           list2="$$list2 $$p"; \
+         else :; fi; \
+       done; \
+       test -z "$$list2" || { \
+         echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+         $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+       }
+
+uninstall-libLTLIBRARIES:
+       @$(NORMAL_UNINSTALL)
+       @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+       for p in $$list; do \
+         $(am__strip_dir) \
+         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+       done
+
+clean-libLTLIBRARIES:
+       -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+       @list='$(lib_LTLIBRARIES)'; \
+       locs=`for p in $$list; do echo $$p; done | \
+             sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+             sort -u`; \
+       test -z "$$locs" || { \
+         echo rm -f $${locs}; \
+         rm -f $${locs}; \
+       }
+
+libnghttpx.a: $(libnghttpx_a_OBJECTS) $(libnghttpx_a_DEPENDENCIES) $(EXTRA_libnghttpx_a_DEPENDENCIES) 
+       $(AM_V_at)-rm -f libnghttpx.a
+       $(AM_V_AR)$(libnghttpx_a_AR) libnghttpx.a $(libnghttpx_a_OBJECTS) $(libnghttpx_a_LIBADD)
+       $(AM_V_at)$(RANLIB) libnghttpx.a
+
+libnghttp2_asio.la: $(libnghttp2_asio_la_OBJECTS) $(libnghttp2_asio_la_DEPENDENCIES) $(EXTRA_libnghttp2_asio_la_DEPENDENCIES) 
+       $(AM_V_CXXLD)$(libnghttp2_asio_la_LINK) $(am_libnghttp2_asio_la_rpath) $(libnghttp2_asio_la_OBJECTS) $(libnghttp2_asio_la_LIBADD) $(LIBS)
+
 deflatehd$(EXEEXT): $(deflatehd_OBJECTS) $(deflatehd_DEPENDENCIES) $(EXTRA_deflatehd_DEPENDENCIES) 
        @rm -f deflatehd$(EXEEXT)
        $(AM_V_CXXLD)$(CXXLINK) $(deflatehd_OBJECTS) $(deflatehd_LDADD) $(LIBS)
@@ -1324,111 +1421,117 @@ mostlyclean-compile:
 distclean-compile:
        -rm -f *.tab.c
 
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HtmlParser.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpServer.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app_helper.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/comp_helper.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/deflatehd.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load_http1_session.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load_http2_session.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http2.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inflatehd.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_request.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_request_impl.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_response.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_response_impl.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_impl.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_tcp_impl.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_tls_impl.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_stream.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_tls_context.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_common.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_io_service_pool.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2_handler.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2_impl.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_request.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_request_handler.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_request_impl.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_response.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_response_impl.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_serve_mux.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_stream.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_tls_context.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-http2.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-timegm.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-tls.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-util.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-app_helper.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-http2.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_accept_handler.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_api_downstream_connection.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_client_handler.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_config.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_connect_blocker.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_connection.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_connection_handler.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_dns_resolver.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_dns_tracker.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_downstream.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_downstream_connection.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_downstream_connection_pool.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_downstream_queue.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_dual_dns_resolver.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_exec.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_health_monitor_downstream_connection.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_http.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_http2_downstream_connection.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_http2_session.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_http2_upstream.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_http_downstream_connection.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_https_upstream.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_io_control.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_live_check.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_log.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_log_config.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_memcached_connection.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_memcached_dispatcher.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_mruby.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_env.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_request.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_response.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_rate_limit.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_router.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_signal.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_tls.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_worker.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_worker_process.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-timegm.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-tls.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-util.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-xsi_strerror.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_gzip.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpd.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx-shrpx.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-base64_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-buffer_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-http2_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-memchunk_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_http_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_router_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_worker_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-template_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-util_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timegm.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HtmlParser.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HttpServer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app_helper.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/comp_helper.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/deflatehd.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load_http1_session.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/h2load_http2_session.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http2.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inflatehd.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_request.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_request_impl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_response.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_response_impl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_impl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_tcp_impl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_tls_impl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_stream.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_client_tls_context.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_io_service_pool.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2_handler.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2_impl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_request.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_request_handler.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_request_impl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_response.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_response_impl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_serve_mux.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_stream.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-asio_server_tls_context.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-http2.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-timegm.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-tls.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttp2_asio_la-util.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-app_helper.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-http2.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_accept_handler.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_api_downstream_connection.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_client_handler.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_config.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_connect_blocker.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_connection.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_connection_handler.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_dns_resolver.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_dns_tracker.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_downstream.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_downstream_connection.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_downstream_connection_pool.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_downstream_queue.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_dual_dns_resolver.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_exec.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_health_monitor_downstream_connection.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_http.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_http2_downstream_connection.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_http2_session.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_http2_upstream.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_http_downstream_connection.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_https_upstream.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_io_control.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_live_check.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_log.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_log_config.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_memcached_connection.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_memcached_dispatcher.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_mruby.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_env.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_request.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_response.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_rate_limit.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_router.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_signal.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_tls.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_worker.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-shrpx_worker_process.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-timegm.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-tls.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-util.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnghttpx_a-xsi_strerror.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_gzip.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpd.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx-shrpx.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-base64_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-buffer_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-http2_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-memchunk_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_http_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_router_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-shrpx_worker_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-template_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttpx_unittest-util_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timegm.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+       @$(MKDIR_P) $(@D)
+       @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
 
 .c.o:
 @am__fastdepCC_TRUE@   $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
@@ -2774,7 +2877,7 @@ $(TEST_SUITE_LOG): $(TEST_LOGS)
        fi;                                                             \
        $$success || exit 1
 
-check-TESTS:
+check-TESTS: $(check_PROGRAMS)
        @list='$(RECHECK_LOGS)';           test -z "$$list" || rm -f $$list
        @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
        @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
@@ -2817,7 +2920,10 @@ nghttpx-unittest.log: nghttpx-unittest$(EXEEXT)
 @am__EXEEXT_TRUE@      $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
 @am__EXEEXT_TRUE@      "$$tst" $(AM_TESTS_FD_REDIRECT)
 
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
        @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        list='$(DISTFILES)'; \
@@ -2876,12 +2982,12 @@ check-am: all-am
        $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
        $(MAKE) $(AM_MAKEFLAGS) check-TESTS
 check: check-recursive
-all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) $(PROGRAMS) $(DATA)
+all-am: Makefile $(PROGRAMS) $(LIBRARIES) $(LTLIBRARIES) $(DATA)
 install-binPROGRAMS: install-libLTLIBRARIES
 
 installdirs: installdirs-recursive
 installdirs-am:
-       for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgconfigdir)"; do \
+       for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)"; do \
          test -z "$$dir" || $(MKDIR_P) "$$dir"; \
        done
 install: install-recursive
@@ -2925,7 +3031,111 @@ clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \
        mostlyclean-am
 
 distclean: distclean-recursive
-       -rm -rf ./$(DEPDIR)
+               -rm -f ./$(DEPDIR)/HtmlParser.Po
+       -rm -f ./$(DEPDIR)/HttpServer.Po
+       -rm -f ./$(DEPDIR)/app_helper.Po
+       -rm -f ./$(DEPDIR)/comp_helper.Po
+       -rm -f ./$(DEPDIR)/deflatehd.Po
+       -rm -f ./$(DEPDIR)/h2load.Po
+       -rm -f ./$(DEPDIR)/h2load_http1_session.Po
+       -rm -f ./$(DEPDIR)/h2load_http2_session.Po
+       -rm -f ./$(DEPDIR)/http2.Po
+       -rm -f ./$(DEPDIR)/inflatehd.Po
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_request.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_request_impl.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_response.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_response_impl.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_session.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_impl.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_tcp_impl.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_tls_impl.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_stream.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_tls_context.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_common.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_io_service_pool.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2_handler.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2_impl.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_request.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_request_handler.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_request_impl.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_response.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_response_impl.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_serve_mux.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_stream.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_tls_context.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-http2.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-timegm.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-tls.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-util.Plo
+       -rm -f ./$(DEPDIR)/libnghttpx_a-app_helper.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-http2.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_accept_handler.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_api_downstream_connection.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_client_handler.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_config.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_connect_blocker.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_connection.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_connection_handler.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_dns_resolver.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_dns_tracker.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_downstream.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_downstream_connection.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_downstream_connection_pool.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_downstream_queue.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_dual_dns_resolver.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_exec.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_health_monitor_downstream_connection.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http2_downstream_connection.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http2_session.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http2_upstream.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http_downstream_connection.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_https_upstream.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_io_control.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_live_check.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_log.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_log_config.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_memcached_connection.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_memcached_dispatcher.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_env.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_request.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_response.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_rate_limit.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_router.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_signal.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_tls.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_worker.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_worker_process.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-timegm.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-tls.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-util.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-xsi_strerror.Po
+       -rm -f ./$(DEPDIR)/nghttp.Po
+       -rm -f ./$(DEPDIR)/nghttp2_gzip.Po
+       -rm -f ./$(DEPDIR)/nghttpd.Po
+       -rm -f ./$(DEPDIR)/nghttpx-shrpx.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-base64_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-buffer_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-http2_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-memchunk_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_http_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_router_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_worker_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-template_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-util_test.Po
+       -rm -f ./$(DEPDIR)/timegm.Po
+       -rm -f ./$(DEPDIR)/tls.Po
+       -rm -f ./$(DEPDIR)/util.Po
        -rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
        distclean-tags
@@ -2971,7 +3181,111 @@ install-ps-am:
 installcheck-am:
 
 maintainer-clean: maintainer-clean-recursive
-       -rm -rf ./$(DEPDIR)
+               -rm -f ./$(DEPDIR)/HtmlParser.Po
+       -rm -f ./$(DEPDIR)/HttpServer.Po
+       -rm -f ./$(DEPDIR)/app_helper.Po
+       -rm -f ./$(DEPDIR)/comp_helper.Po
+       -rm -f ./$(DEPDIR)/deflatehd.Po
+       -rm -f ./$(DEPDIR)/h2load.Po
+       -rm -f ./$(DEPDIR)/h2load_http1_session.Po
+       -rm -f ./$(DEPDIR)/h2load_http2_session.Po
+       -rm -f ./$(DEPDIR)/http2.Po
+       -rm -f ./$(DEPDIR)/inflatehd.Po
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_request.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_request_impl.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_response.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_response_impl.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_session.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_impl.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_tcp_impl.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_session_tls_impl.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_stream.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_client_tls_context.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_common.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_io_service_pool.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2_handler.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_http2_impl.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_request.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_request_handler.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_request_impl.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_response.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_response_impl.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_serve_mux.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_stream.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-asio_server_tls_context.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-http2.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-timegm.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-tls.Plo
+       -rm -f ./$(DEPDIR)/libnghttp2_asio_la-util.Plo
+       -rm -f ./$(DEPDIR)/libnghttpx_a-app_helper.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-http2.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_accept_handler.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_api_downstream_connection.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_client_handler.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_config.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_connect_blocker.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_connection.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_connection_handler.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_dns_resolver.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_dns_tracker.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_downstream.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_downstream_connection.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_downstream_connection_pool.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_downstream_queue.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_dual_dns_resolver.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_exec.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_health_monitor_downstream_connection.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http2_downstream_connection.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http2_session.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http2_upstream.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_http_downstream_connection.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_https_upstream.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_io_control.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_live_check.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_log.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_log_config.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_memcached_connection.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_memcached_dispatcher.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_env.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_request.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_mruby_module_response.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_rate_limit.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_router.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_signal.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_tls.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_worker.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-shrpx_worker_process.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-timegm.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-tls.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-util.Po
+       -rm -f ./$(DEPDIR)/libnghttpx_a-xsi_strerror.Po
+       -rm -f ./$(DEPDIR)/nghttp.Po
+       -rm -f ./$(DEPDIR)/nghttp2_gzip.Po
+       -rm -f ./$(DEPDIR)/nghttpd.Po
+       -rm -f ./$(DEPDIR)/nghttpx-shrpx.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-base64_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-buffer_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-http2_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-memchunk_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-nghttp2_gzip_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx-unittest.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_config_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_downstream_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_http_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_router_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_tls_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-shrpx_worker_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-template_test.Po
+       -rm -f ./$(DEPDIR)/nghttpx_unittest-util_test.Po
+       -rm -f ./$(DEPDIR)/timegm.Po
+       -rm -f ./$(DEPDIR)/tls.Po
+       -rm -f ./$(DEPDIR)/util.Po
        -rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
@@ -2993,23 +3307,23 @@ uninstall-am: uninstall-binPROGRAMS uninstall-libLTLIBRARIES \
 
 .MAKE: $(am__recursive_targets) check-am install-am install-strip
 
-.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
-       check-TESTS check-am clean clean-binPROGRAMS \
-       clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \
-       clean-libtool clean-noinstLIBRARIES cscopelist-am ctags \
-       ctags-am distclean distclean-compile distclean-generic \
-       distclean-libtool distclean-tags distdir dvi dvi-am html \
-       html-am info info-am install install-am install-binPROGRAMS \
-       install-data install-data-am install-dvi install-dvi-am \
-       install-exec install-exec-am install-html install-html-am \
-       install-info install-info-am install-libLTLIBRARIES \
-       install-man install-pdf install-pdf-am install-pkgconfigDATA \
-       install-ps install-ps-am install-strip installcheck \
-       installcheck-am installdirs installdirs-am maintainer-clean \
-       maintainer-clean-generic mostlyclean mostlyclean-compile \
-       mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
-       recheck tags tags-am uninstall uninstall-am \
-       uninstall-binPROGRAMS uninstall-libLTLIBRARIES \
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+       am--depfiles check check-TESTS check-am clean \
+       clean-binPROGRAMS clean-checkPROGRAMS clean-generic \
+       clean-libLTLIBRARIES clean-libtool clean-noinstLIBRARIES \
+       cscopelist-am ctags ctags-am distclean distclean-compile \
+       distclean-generic distclean-libtool distclean-tags distdir dvi \
+       dvi-am html html-am info info-am install install-am \
+       install-binPROGRAMS install-data install-data-am install-dvi \
+       install-dvi-am install-exec install-exec-am install-html \
+       install-html-am install-info install-info-am \
+       install-libLTLIBRARIES install-man install-pdf install-pdf-am \
+       install-pkgconfigDATA install-ps install-ps-am install-strip \
+       installcheck installcheck-am installdirs installdirs-am \
+       maintainer-clean maintainer-clean-generic mostlyclean \
+       mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+       pdf pdf-am ps ps-am recheck tags tags-am uninstall \
+       uninstall-am uninstall-binPROGRAMS uninstall-libLTLIBRARIES \
        uninstall-pkgconfigDATA
 
 .PRECIOUS: Makefile
index 32a7426..fa0de3a 100644 (file)
@@ -28,7 +28,7 @@
 #include "nghttp2_config.h"
 
 #ifndef _WIN32
-#include <sys/uio.h>
+#  include <sys/uio.h>
 #endif // !_WIN32
 
 #include <cassert>
index e61c579..107423b 100644 (file)
  */
 #include <sys/types.h>
 #ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
+#  include <sys/socket.h>
 #endif // HAVE_SYS_SOCKET_H
 #ifdef HAVE_NETDB_H
-#include <netdb.h>
+#  include <netdb.h>
 #endif // HAVE_NETDB_H
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 #ifdef HAVE_FCNTL_H
-#include <fcntl.h>
+#  include <fcntl.h>
 #endif // HAVE_FCNTL_H
 #ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
+#  include <netinet/in.h>
 #endif // HAVE_NETINET_IN_H
 #include <netinet/tcp.h>
 #include <poll.h>
@@ -77,6 +77,8 @@ const char *strsettingsid(int32_t id) {
     return "SETTINGS_MAX_FRAME_SIZE";
   case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
     return "SETTINGS_MAX_HEADER_LIST_SIZE";
+  case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
+    return "SETTINGS_ENABLE_CONNECT_PROTOCOL";
   default:
     return "UNKNOWN";
   }
@@ -106,6 +108,8 @@ std::string strframetype(uint8_t type) {
     return "WINDOW_UPDATE";
   case NGHTTP2_ALTSVC:
     return "ALTSVC";
+  case NGHTTP2_ORIGIN:
+    return "ORIGIN";
   }
 
   std::string s = "extension(0x";
@@ -351,6 +355,15 @@ void print_frame(print_type ptype, const nghttp2_frame *frame) {
             static_cast<int>(altsvc->field_value_len), altsvc->field_value);
     break;
   }
+  case NGHTTP2_ORIGIN: {
+    auto origin = static_cast<nghttp2_ext_origin *>(frame->ext.payload);
+    for (size_t i = 0; i < origin->nov; ++i) {
+      auto ent = &origin->ov[i];
+      print_frame_attr_indent();
+      fprintf(outfile, "[%.*s]\n", (int)ent->origin_len, ent->origin);
+    }
+    break;
+  }
   default:
     break;
   }
index 2e1ef53..5424054 100644 (file)
@@ -30,7 +30,7 @@
 #include <cinttypes>
 #include <cstdlib>
 #ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
+#  include <sys/time.h>
 #endif // HAVE_SYS_TIME_H
 #include <poll.h>
 
index e142ded..a5acffc 100644 (file)
@@ -44,6 +44,15 @@ session::session(boost::asio::io_service &io_service, const std::string &host,
   impl_->start_resolve(host, service);
 }
 
+session::session(boost::asio::io_service &io_service,
+                 const boost::asio::ip::tcp::endpoint &local_endpoint,
+                 const std::string &host, const std::string &service)
+    : impl_(std::make_shared<session_tcp_impl>(
+          io_service, local_endpoint, host, service,
+          boost::posix_time::seconds(60))) {
+  impl_->start_resolve(host, service);
+}
+
 session::session(boost::asio::io_service &io_service, const std::string &host,
                  const std::string &service,
                  const boost::posix_time::time_duration &connect_timeout)
@@ -53,6 +62,15 @@ session::session(boost::asio::io_service &io_service, const std::string &host,
 }
 
 session::session(boost::asio::io_service &io_service,
+                 const boost::asio::ip::tcp::endpoint &local_endpoint,
+                 const std::string &host, const std::string &service,
+                 const boost::posix_time::time_duration &connect_timeout)
+    : impl_(std::make_shared<session_tcp_impl>(io_service, local_endpoint, host,
+                                               service, connect_timeout)) {
+  impl_->start_resolve(host, service);
+}
+
+session::session(boost::asio::io_service &io_service,
                  boost::asio::ssl::context &tls_ctx, const std::string &host,
                  const std::string &service)
     : impl_(std::make_shared<session_tls_impl>(
index 77aa676..8fdf211 100644 (file)
@@ -34,24 +34,35 @@ session_tcp_impl::session_tcp_impl(
     const boost::posix_time::time_duration &connect_timeout)
     : session_impl(io_service, connect_timeout), socket_(io_service) {}
 
+session_tcp_impl::session_tcp_impl(
+    boost::asio::io_service &io_service,
+    const boost::asio::ip::tcp::endpoint &local_endpoint,
+    const std::string &host, const std::string &service,
+    const boost::posix_time::time_duration &connect_timeout)
+    : session_impl(io_service, connect_timeout), socket_(io_service) {
+  socket_.open(local_endpoint.protocol());
+  boost::asio::socket_base::reuse_address option(true);
+  socket_.set_option(option);
+  socket_.bind(local_endpoint);
+}
+
 session_tcp_impl::~session_tcp_impl() {}
 
 void session_tcp_impl::start_connect(tcp::resolver::iterator endpoint_it) {
   auto self = shared_from_this();
-  boost::asio::async_connect(socket_, endpoint_it,
-                             [self](const boost::system::error_code &ec,
-                                    tcp::resolver::iterator endpoint_it) {
-                               if (self->stopped()) {
-                                 return;
-                               }
+  socket_.async_connect(
+      *endpoint_it, [self, endpoint_it](const boost::system::error_code &ec) {
+        if (self->stopped()) {
+          return;
+        }
 
-                               if (ec) {
-                                 self->not_connected(ec);
-                                 return;
-                               }
+        if (ec) {
+          self->not_connected(ec);
+          return;
+        }
 
-                               self->connected(endpoint_it);
-                             });
+        self->connected(endpoint_it);
+      });
 }
 
 tcp::socket &session_tcp_impl::socket() { return socket_; }
index 0d1a48d..0b6ae93 100644 (file)
@@ -40,6 +40,10 @@ public:
   session_tcp_impl(boost::asio::io_service &io_service, const std::string &host,
                    const std::string &service,
                    const boost::posix_time::time_duration &connect_timeout);
+  session_tcp_impl(boost::asio::io_service &io_service,
+                   const boost::asio::ip::tcp::endpoint &local_endpoint,
+                   const std::string &host, const std::string &service,
+                   const boost::posix_time::time_duration &connect_timeout);
   virtual ~session_tcp_impl();
 
   virtual void start_connect(tcp::resolver::iterator endpoint_it);
index f1c2133..377886c 100644 (file)
@@ -38,6 +38,10 @@ session_tls_impl::session_tls_impl(
   // ssl::context::set_verify_mode(boost::asio::ssl::verify_peer) is
   // not used, which is what we want.
   socket_.set_verify_callback(boost::asio::ssl::rfc2818_verification(host));
+  auto ssl = socket_.native_handle();
+  if (!util::numeric_host(host.c_str())) {
+    SSL_set_tlsext_host_name(ssl, host.c_str());
+  }
 }
 
 session_tls_impl::~session_tls_impl() {}
index 3291885..eaa9b8b 100644 (file)
@@ -35,6 +35,7 @@ namespace nghttp2 {
 namespace asio_http2 {
 namespace client {
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
 namespace {
 int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
                                 unsigned char *outlen, const unsigned char *in,
@@ -46,6 +47,7 @@ int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
   return SSL_TLSEXT_ERR_OK;
 }
 } // namespace
+#endif // !OPENSSL_NO_NEXTPROTONEG
 
 boost::system::error_code
 configure_tls_context(boost::system::error_code &ec,
@@ -54,7 +56,9 @@ configure_tls_context(boost::system::error_code &ec,
 
   auto ctx = tls_ctx.native_handle();
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
   SSL_CTX_set_next_proto_select_cb(ctx, client_select_next_proto_cb, nullptr);
+#endif // !OPENSSL_NO_NEXTPROTONEG
 
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
   auto proto_list = util::get_default_alpn();
index 590e931..90762d3 100644 (file)
@@ -177,7 +177,9 @@ bool tls_h2_negotiated(ssl_socket &socket) {
   const unsigned char *next_proto = nullptr;
   unsigned int next_proto_len = 0;
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
   SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len);
+#endif // !OPENSSL_NO_NEXTPROTONEG
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
   if (next_proto == nullptr) {
     SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len);
index aa73cc5..0e33441 100644 (file)
@@ -35,12 +35,14 @@ namespace nghttp2 {
 namespace asio_http2 {
 namespace server {
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
 namespace {
 std::vector<unsigned char> &get_alpn_token() {
   static auto alpn_token = util::get_default_alpn();
   return alpn_token;
 }
 } // namespace
+#endif // !OPENSSL_NO_NEXTPROTONEG
 
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
 namespace {
@@ -82,6 +84,7 @@ configure_tls_context_easy(boost::system::error_code &ec,
   }
 #endif /* OPENSSL_NO_EC */
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
   SSL_CTX_set_next_protos_advertised_cb(
       ctx,
       [](SSL *s, const unsigned char **data, unsigned int *len, void *arg) {
@@ -93,6 +96,7 @@ configure_tls_context_easy(boost::system::error_code &ec,
         return SSL_TLSEXT_ERR_OK;
       },
       nullptr);
+#endif // !OPENSSL_NO_NEXTPROTONEG
 
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
   // ALPN selection callback
index c8b16e8..1bd51af 100644 (file)
@@ -36,14 +36,17 @@ namespace nghttp2 {
 
 namespace base64 {
 
+namespace {
+constexpr char B64_CHARS[] = {
+    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',
+};
+} // namespace
+
 template <typename InputIt> std::string encode(InputIt first, InputIt last) {
-  static constexpr char CHAR_TABLE[] = {
-      'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
-      'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
-      'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
-      'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
-      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',
-  };
   std::string res;
   size_t len = last - first;
   if (len == 0) {
@@ -57,29 +60,72 @@ template <typename InputIt> std::string encode(InputIt first, InputIt last) {
     uint32_t n = static_cast<uint8_t>(*first++) << 16;
     n += static_cast<uint8_t>(*first++) << 8;
     n += static_cast<uint8_t>(*first++);
-    *p++ = CHAR_TABLE[n >> 18];
-    *p++ = CHAR_TABLE[(n >> 12) & 0x3fu];
-    *p++ = CHAR_TABLE[(n >> 6) & 0x3fu];
-    *p++ = CHAR_TABLE[n & 0x3fu];
+    *p++ = B64_CHARS[n >> 18];
+    *p++ = B64_CHARS[(n >> 12) & 0x3fu];
+    *p++ = B64_CHARS[(n >> 6) & 0x3fu];
+    *p++ = B64_CHARS[n & 0x3fu];
   }
 
   if (r == 2) {
     uint32_t n = static_cast<uint8_t>(*first++) << 16;
     n += static_cast<uint8_t>(*first++) << 8;
-    *p++ = CHAR_TABLE[n >> 18];
-    *p++ = CHAR_TABLE[(n >> 12) & 0x3fu];
-    *p++ = CHAR_TABLE[(n >> 6) & 0x3fu];
+    *p++ = B64_CHARS[n >> 18];
+    *p++ = B64_CHARS[(n >> 12) & 0x3fu];
+    *p++ = B64_CHARS[(n >> 6) & 0x3fu];
     *p++ = '=';
   } else if (r == 1) {
     uint32_t n = static_cast<uint8_t>(*first++) << 16;
-    *p++ = CHAR_TABLE[n >> 18];
-    *p++ = CHAR_TABLE[(n >> 12) & 0x3fu];
+    *p++ = B64_CHARS[n >> 18];
+    *p++ = B64_CHARS[(n >> 12) & 0x3fu];
     *p++ = '=';
     *p++ = '=';
   }
   return res;
 }
 
+constexpr size_t encode_length(size_t n) { return (n + 2) / 3 * 4; }
+
+template <typename InputIt, typename OutputIt>
+OutputIt encode(InputIt first, InputIt last, OutputIt d_first) {
+  size_t len = last - first;
+  if (len == 0) {
+    return d_first;
+  }
+  auto r = len % 3;
+  auto j = last - r;
+  auto p = d_first;
+  while (first != j) {
+    uint32_t n = static_cast<uint8_t>(*first++) << 16;
+    n += static_cast<uint8_t>(*first++) << 8;
+    n += static_cast<uint8_t>(*first++);
+    *p++ = B64_CHARS[n >> 18];
+    *p++ = B64_CHARS[(n >> 12) & 0x3fu];
+    *p++ = B64_CHARS[(n >> 6) & 0x3fu];
+    *p++ = B64_CHARS[n & 0x3fu];
+  }
+
+  switch (r) {
+  case 2: {
+    uint32_t n = static_cast<uint8_t>(*first++) << 16;
+    n += static_cast<uint8_t>(*first++) << 8;
+    *p++ = B64_CHARS[n >> 18];
+    *p++ = B64_CHARS[(n >> 12) & 0x3fu];
+    *p++ = B64_CHARS[(n >> 6) & 0x3fu];
+    *p++ = '=';
+    break;
+  }
+  case 1: {
+    uint32_t n = static_cast<uint8_t>(*first++) << 16;
+    *p++ = B64_CHARS[n >> 18];
+    *p++ = B64_CHARS[(n >> 12) & 0x3fu];
+    *p++ = '=';
+    *p++ = '=';
+    break;
+  }
+  }
+  return p;
+}
+
 template <typename InputIt>
 InputIt next_decode_input(InputIt first, InputIt last, const int *tbl) {
   for (; first != last; ++first) {
index 8fd0e49..8bdb84f 100644 (file)
@@ -26,7 +26,7 @@
 #define BASE64_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif // HAVE_CONFIG_H
 
 namespace nghttp2 {
index abdc987..6789aa3 100644 (file)
@@ -26,7 +26,7 @@
 #define BUFFER_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif // HAVE_CONFIG_H
 
 namespace nghttp2 {
index 0e48a75..131ed21 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_COMP_HELPER_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <jansson.h>
index 2db8981..5d28e24 100644 (file)
  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif // HAVE_CONFIG_H
 
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 #include <getopt.h>
 
index 5f7789c..9e2592a 100644 (file)
 #include <getopt.h>
 #include <signal.h>
 #ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
+#  include <netinet/in.h>
 #endif // HAVE_NETINET_IN_H
 #include <netinet/tcp.h>
 #include <sys/stat.h>
 #ifdef HAVE_FCNTL_H
-#include <fcntl.h>
+#  include <fcntl.h>
 #endif // HAVE_FCNTL_H
 
 #include <cstdio>
@@ -58,7 +58,7 @@
 #include "template.h"
 
 #ifndef O_BINARY
-#define O_BINARY (0)
+#  define O_BINARY (0)
 #endif // O_BINARY
 
 using namespace nghttp2;
@@ -857,7 +857,9 @@ int Client::connection_made() {
     const unsigned char *next_proto = nullptr;
     unsigned int next_proto_len;
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
     SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len);
+#endif // !OPENSSL_NO_NEXTPROTONEG
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
     if (next_proto == nullptr) {
       SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len);
@@ -1563,6 +1565,7 @@ std::string get_reqline(const char *uri, const http_parser_url &u) {
 }
 } // namespace
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
 namespace {
 int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
                                 unsigned char *outlen, const unsigned char *in,
@@ -1577,6 +1580,7 @@ int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
   return SSL_TLSEXT_ERR_NOACK;
 }
 } // namespace
+#endif // !OPENSSL_NO_NEXTPROTONEG
 
 namespace {
 constexpr char UNIX_PATH_PREFIX[] = "unix:";
@@ -1795,10 +1799,12 @@ Options:
   -c, --clients=<N>
               Number  of concurrent  clients.   With  -r option,  this
               specifies the maximum number of connections to be made.
-              Default: )" << config.nclients << R"(
+              Default: )"
+      << config.nclients << R"(
   -t, --threads=<N>
               Number of native threads.
-              Default: )" << config.nthreads << R"(
+              Default: )"
+      << config.nthreads << R"(
   -i, --input-file=<PATH>
               Path of a file with multiple URIs are separated by EOLs.
               This option will disable URIs getting from command-line.
@@ -1821,7 +1827,8 @@ Options:
   -W, --connection-window-bits=<N>
               Sets  the  connection  level   initial  window  size  to
               (2**<N>)-1.
-              Default: )" << config.connection_window_bits << R"(
+              Default: )"
+      << config.connection_window_bits << R"(
   -H, --header=<HEADER>
               Add/Override a header to the requests.
   --ciphers=<SUITE>
@@ -1832,8 +1839,8 @@ Options:
   -p, --no-tls-proto=<PROTOID>
               Specify ALPN identifier of the  protocol to be used when
               accessing http URI without SSL/TLS.
-              Available protocols: )" << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID
-      << R"( and )" << NGHTTP2_H1_1 << R"(
+              Available protocols: )"
+      << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"( and )" << NGHTTP2_H1_1 << R"(
               Default: )"
       << NGHTTP2_CLEARTEXT_PROTO_VERSION_ID << R"(
   -d, --data=<PATH>
@@ -1852,7 +1859,7 @@ Options:
               connections per period.  When the rate is 0, the program
               will run  as it  normally does, creating  connections at
               whatever variable rate it  wants.  The default value for
-              this option is 0.
+              this option is 0.  -r and -D are mutually exclusive.
   --rate-period=<DURATION>
               Specifies the time  period between creating connections.
               The period  must be a positive  number, representing the
@@ -1861,7 +1868,8 @@ Options:
               option is 1s.
   -D, --duration=<N>
               Specifies the main duration for the measurements in case
-              of timing-based benchmarking.
+              of timing-based  benchmarking.  -D  and -r  are mutually
+              exclusive.
   --warm-up-time=<DURATION>
               Specifies the  time  period  before  starting the actual
               measurements, in  case  of  timing-based benchmarking.
@@ -1916,7 +1924,8 @@ Options:
               NPN.  The parameter must be  delimited by a single comma
               only  and any  white spaces  are  treated as  a part  of
               protocol string.
-              Default: )" << DEFAULT_NPN_LIST << R"(
+              Default: )"
+      << DEFAULT_NPN_LIST << R"(
   --h1        Short        hand         for        --npn-list=http/1.1
               --no-tls-proto=http/1.1,    which   effectively    force
               http/1.1 for both http and https URI.
@@ -1944,7 +1953,8 @@ Options:
   The <DURATION> argument is an integer and an optional unit (e.g., 1s
   is 1 second and 500ms is 500 milliseconds).  Units are h, m, s or ms
   (hours, minutes, seconds and milliseconds, respectively).  If a unit
-  is omitted, a second is used as unit.)" << std::endl;
+  is omitted, a second is used as unit.)"
+      << std::endl;
 }
 } // namespace
 
@@ -2294,6 +2304,11 @@ int main(int argc, char **argv) {
     exit(EXIT_FAILURE);
   }
 
+  if (config.is_timing_based_mode() && config.is_rate_mode()) {
+    std::cerr << "-r, -D: they are mutually exclusive." << std::endl;
+    exit(EXIT_FAILURE);
+  }
+
   if (config.nreqs == 0 && !config.is_timing_based_mode()) {
     std::cerr << "-n: the number of requests must be strictly greater than 0 "
                  "if timing-based test is not being run."
@@ -2399,8 +2414,10 @@ int main(int argc, char **argv) {
     exit(EXIT_FAILURE);
   }
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
   SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb,
                                    nullptr);
+#endif // !OPENSSL_NO_NEXTPROTONEG
 
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
   std::vector<unsigned char> proto_list;
@@ -2674,14 +2691,14 @@ int main(int argc, char **argv) {
 finished in )"
             << util::format_duration(duration) << ", " << rps << " req/s, "
             << util::utos_funit(bps) << R"(B/s
-requests: )" << stats.req_todo << " total, "
-            << stats.req_started << " started, " << stats.req_done << " done, "
-            << stats.req_status_success << " succeeded, " << stats.req_failed
-            << " failed, " << stats.req_error << " errored, "
-            << stats.req_timedout << R"( timeout
-status codes: )" << stats.status[2] << " 2xx, "
-            << stats.status[3] << " 3xx, " << stats.status[4] << " 4xx, "
-            << stats.status[5] << R"( 5xx
+requests: )" << stats.req_todo
+            << " total, " << stats.req_started << " started, " << stats.req_done
+            << " done, " << stats.req_status_success << " succeeded, "
+            << stats.req_failed << " failed, " << stats.req_error
+            << " errored, " << stats.req_timedout << R"( timeout
+status codes: )"
+            << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, "
+            << stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx
 traffic: )" << util::utos_funit(stats.bytes_total)
             << "B (" << stats.bytes_total << ") total, "
             << util::utos_funit(stats.bytes_head) << "B (" << stats.bytes_head
@@ -2689,12 +2706,12 @@ traffic: )" << util::utos_funit(stats.bytes_total)
             << "%), " << util::utos_funit(stats.bytes_body) << "B ("
             << stats.bytes_body << R"() data
                      min         max         mean         sd        +/- sd
-time for request: )" << std::setw(10)
-            << util::format_duration(ts.request.min) << "  " << std::setw(10)
-            << util::format_duration(ts.request.max) << "  " << std::setw(10)
-            << util::format_duration(ts.request.mean) << "  " << std::setw(10)
-            << util::format_duration(ts.request.sd) << std::setw(9)
-            << util::dtos(ts.request.within_sd) << "%"
+time for request: )"
+            << std::setw(10) << util::format_duration(ts.request.min) << "  "
+            << std::setw(10) << util::format_duration(ts.request.max) << "  "
+            << std::setw(10) << util::format_duration(ts.request.mean) << "  "
+            << std::setw(10) << util::format_duration(ts.request.sd)
+            << std::setw(9) << util::dtos(ts.request.within_sd) << "%"
             << "\ntime for connect: " << std::setw(10)
             << util::format_duration(ts.connect.min) << "  " << std::setw(10)
             << util::format_duration(ts.connect.max) << "  " << std::setw(10)
index 4f6739d..bde36e8 100644 (file)
 
 #include <sys/types.h>
 #ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
+#  include <sys/socket.h>
 #endif // HAVE_SYS_SOCKET_H
 #ifdef HAVE_NETDB_H
-#include <netdb.h>
+#  include <netdb.h>
 #endif // HAVE_NETDB_H
 #include <sys/un.h>
 
index 6db5974..566cfb0 100644 (file)
@@ -107,6 +107,9 @@ StringRef get_reason_phrase(unsigned int status_code) {
     return StringRef::from_lit("Expectation Failed");
   case 421:
     return StringRef::from_lit("Misdirected Request");
+  case 425:
+    // https://tools.ietf.org/html/rfc8470
+    return StringRef::from_lit("Too Early");
   case 426:
     return StringRef::from_lit("Upgrade Required");
   case 428:
@@ -386,6 +389,21 @@ void copy_headers_to_nva_internal(std::vector<nghttp2_nv> &nva,
     case HD_TRANSFER_ENCODING:
     case HD_UPGRADE:
       continue;
+    case HD_EARLY_DATA:
+      if (flags & HDOP_STRIP_EARLY_DATA) {
+        continue;
+      }
+      break;
+    case HD_SEC_WEBSOCKET_ACCEPT:
+      if (flags & HDOP_STRIP_SEC_WEBSOCKET_ACCEPT) {
+        continue;
+      }
+      break;
+    case HD_SEC_WEBSOCKET_KEY:
+      if (flags & HDOP_STRIP_SEC_WEBSOCKET_KEY) {
+        continue;
+      }
+      break;
     case HD_FORWARDED:
       if (flags & HDOP_STRIP_FORWARDED) {
         continue;
@@ -480,6 +498,11 @@ void build_http1_headers_from_headers(DefaultMemchunks *buf,
     case HD_SERVER:
     case HD_UPGRADE:
       continue;
+    case HD_EARLY_DATA:
+      if (flags & HDOP_STRIP_EARLY_DATA) {
+        continue;
+      }
+      break;
     case HD_FORWARDED:
       if (flags & HDOP_STRIP_FORWARDED) {
         continue;
@@ -821,10 +844,20 @@ int lookup_token(const uint8_t *name, size_t namelen) {
         return HD_FORWARDED;
       }
       break;
+    case 'l':
+      if (util::streq_l(":protoco", name, 8)) {
+        return HD__PROTOCOL;
+      }
+      break;
     }
     break;
   case 10:
     switch (name[9]) {
+    case 'a':
+      if (util::streq_l("early-dat", name, 9)) {
+        return HD_EARLY_DATA;
+      }
+      break;
     case 'e':
       if (util::streq_l("keep-aliv", name, 9)) {
         return HD_KEEP_ALIVE;
@@ -924,6 +957,20 @@ int lookup_token(const uint8_t *name, size_t namelen) {
         return HD_X_FORWARDED_PROTO;
       }
       break;
+    case 'y':
+      if (util::streq_l("sec-websocket-ke", name, 16)) {
+        return HD_SEC_WEBSOCKET_KEY;
+      }
+      break;
+    }
+    break;
+  case 20:
+    switch (name[19]) {
+    case 't':
+      if (util::streq_l("sec-websocket-accep", name, 19)) {
+        return HD_SEC_WEBSOCKET_ACCEPT;
+      }
+      break;
     }
     break;
   }
@@ -1313,7 +1360,8 @@ std::string path_join(const StringRef &base_path, const StringRef &base_query,
 }
 
 bool expect_response_body(int status_code) {
-  return status_code / 100 != 1 && status_code != 304 && status_code != 204;
+  return status_code == 101 ||
+         (status_code / 100 != 1 && status_code != 304 && status_code != 204);
 }
 
 bool expect_response_body(const std::string &method, int status_code) {
@@ -1811,6 +1859,21 @@ bool contains_trailers(const StringRef &s) {
   }
 }
 
+StringRef make_websocket_accept_token(uint8_t *dest, const StringRef &key) {
+  static constexpr uint8_t magic[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+  std::array<uint8_t, base64::encode_length(16) + str_size(magic)> s;
+  auto p = std::copy(std::begin(key), std::end(key), std::begin(s));
+  std::copy_n(magic, str_size(magic), p);
+
+  std::array<uint8_t, 20> h;
+  if (util::sha1(h.data(), StringRef{std::begin(s), std::end(s)}) != 0) {
+    return StringRef{};
+  }
+
+  auto end = base64::encode(std::begin(h), std::end(h), dest);
+  return StringRef{dest, end};
+}
+
 } // namespace http2
 
 } // namespace nghttp2
index eb6a41d..9e7d749 100644 (file)
@@ -41,6 +41,7 @@
 #include "memchunk.h"
 #include "template.h"
 #include "allocator.h"
+#include "base64.h"
 
 namespace nghttp2 {
 
@@ -203,9 +204,19 @@ enum HeaderBuildOp {
   // Via header fields must be stripped.  If this flag is not set, all
   // Via header fields other than last one are added.
   HDOP_STRIP_VIA = 1 << 3,
+  // Early-Data header fields must be stripped.  If this flag is not
+  // set, all Early-Data header fields are added.
+  HDOP_STRIP_EARLY_DATA = 1 << 4,
   // Strip above all header fields.
   HDOP_STRIP_ALL = HDOP_STRIP_FORWARDED | HDOP_STRIP_X_FORWARDED_FOR |
-                   HDOP_STRIP_X_FORWARDED_PROTO | HDOP_STRIP_VIA,
+                   HDOP_STRIP_X_FORWARDED_PROTO | HDOP_STRIP_VIA |
+                   HDOP_STRIP_EARLY_DATA,
+  // Sec-WebSocket-Accept header field must be stripped.  If this flag
+  // is not set, all Sec-WebSocket-Accept header fields are added.
+  HDOP_STRIP_SEC_WEBSOCKET_ACCEPT = 1 << 5,
+  // Sec-WebSocket-Key header field must be stripped.  If this flag is
+  // not set, all Sec-WebSocket-Key header fields are added.
+  HDOP_STRIP_SEC_WEBSOCKET_KEY = 1 << 6,
 };
 
 // Appends headers in |headers| to |nv|.  |headers| must be indexed
@@ -293,6 +304,7 @@ enum {
   HD__HOST,
   HD__METHOD,
   HD__PATH,
+  HD__PROTOCOL,
   HD__SCHEME,
   HD__STATUS,
   HD_ACCEPT_ENCODING,
@@ -304,6 +316,7 @@ enum {
   HD_CONTENT_TYPE,
   HD_COOKIE,
   HD_DATE,
+  HD_EARLY_DATA,
   HD_EXPECT,
   HD_FORWARDED,
   HD_HOST,
@@ -313,6 +326,8 @@ enum {
   HD_LINK,
   HD_LOCATION,
   HD_PROXY_CONNECTION,
+  HD_SEC_WEBSOCKET_ACCEPT,
+  HD_SEC_WEBSOCKET_KEY,
   HD_SERVER,
   HD_TE,
   HD_TRAILER,
@@ -416,6 +431,12 @@ StringRef copy_lower(BlockAllocator &balloc, const StringRef &src);
 // Returns true if te header field value |s| contains "trailers".
 bool contains_trailers(const StringRef &s);
 
+// Creates Sec-WebSocket-Accept value for |key|.  The capacity of
+// buffer pointed by |dest| must have at least 24 bytes (base64
+// encoded length of 16 bytes data).  It returns empty string in case
+// of error.
+StringRef make_websocket_accept_token(uint8_t *dest, const StringRef &key);
+
 } // namespace http2
 
 } // namespace nghttp2
index bd7ddc0..0d63020 100644 (file)
@@ -26,7 +26,7 @@
 #define SHRPX_HTTP2_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif // HAVE_CONFIG_H
 
 namespace shrpx {
index 0c1905c..6424663 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -407,8 +407,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
          *config.status*) \
            cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
          *) \
-           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
-           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
        esac;
 
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -502,7 +502,10 @@ cscopelist-am: $(am__tagged_files)
 distclean-tags:
        -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
 
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
        @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        list='$(DISTFILES)'; \
index c6c1947..7b256a7 100644 (file)
@@ -150,6 +150,11 @@ public:
   session(boost::asio::io_service &io_service, const std::string &host,
           const std::string &service);
 
+  // Same as previous but with pegged local endpoint
+  session(boost::asio::io_service &io_service,
+          const boost::asio::ip::tcp::endpoint &local_endpoint,
+          const std::string &host, const std::string &service);
+
   // Starts HTTP/2 session by connecting to |host| and |service|
   // (e.g., "80") using clear text TCP connection with given connect
   // timeout.
@@ -157,6 +162,12 @@ public:
           const std::string &service,
           const boost::posix_time::time_duration &connect_timeout);
 
+  // Same as previous but with pegged local endpoint
+  session(boost::asio::io_service &io_service,
+          const boost::asio::ip::tcp::endpoint &local_endpoint,
+          const std::string &host, const std::string &service,
+          const boost::posix_time::time_duration &connect_timeout);
+
   // Starts HTTP/2 session by connecting to |host| and |service|
   // (e.g., "443") using encrypted SSL/TLS connection with connect
   // timeout 60 seconds.
index 4f9bf5a..9a159ad 100644 (file)
  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif // HAVE_CONFIG_H
 
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 #include <getopt.h>
 
index 6f76b49..d373d06 100644 (file)
@@ -35,7 +35,7 @@ struct iovec {
   size_t iov_len; /* Length of data.  */
 };
 #else // !_WIN32
-#include <sys/uio.h>
+#  include <sys/uio.h>
 #endif // !_WIN32
 
 #include <cassert>
@@ -52,9 +52,9 @@ namespace nghttp2 {
 #define DEFAULT_WR_IOVCNT 16
 
 #if defined(IOV_MAX) && IOV_MAX < DEFAULT_WR_IOVCNT
-#define MAX_WR_IOVCNT IOV_MAX
+#  define MAX_WR_IOVCNT IOV_MAX
 #else // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT
-#define MAX_WR_IOVCNT DEFAULT_WR_IOVCNT
+#  define MAX_WR_IOVCNT DEFAULT_WR_IOVCNT
 #endif // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT
 
 template <size_t N> struct Memchunk {
@@ -251,6 +251,29 @@ template <typename Memchunk> struct Memchunks {
 
     return count - left;
   }
+  size_t remove(Memchunks &dest) {
+    assert(pool == dest.pool);
+
+    if (head == nullptr) {
+      return 0;
+    }
+
+    auto n = len;
+
+    if (dest.tail == nullptr) {
+      dest.head = head;
+    } else {
+      dest.tail->next = head;
+    }
+
+    dest.tail = tail;
+    dest.len += len;
+
+    head = tail = nullptr;
+    len = 0;
+
+    return n;
+  }
   size_t drain(size_t count) {
     auto ndata = count;
     auto m = head;
index 068f825..7d677e7 100644 (file)
@@ -26,7 +26,7 @@
 #define MEMCHUNK_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif // HAVE_CONFIG_H
 
 namespace nghttp2 {
index 02dda09..45311d8 100644 (file)
 #define NETWORK_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif // HAVE_CONFIG_H
 
 #include <sys/types.h>
 #ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
+#  include <sys/socket.h>
 #endif // HAVE_SYS_SOCKET_H
 #ifdef _WIN32
-#include <ws2tcpip.h>
+#  include <ws2tcpip.h>
 #else // !_WIN32
-#include <sys/un.h>
+#  include <sys/un.h>
 #endif // !_WIN32
 #ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
+#  include <netinet/in.h>
 #endif // HAVE_NETINET_IN_H
 #ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
+#  include <arpa/inet.h>
 #endif // HAVE_ARPA_INET_H
 
 namespace nghttp2 {
index 7c29b3c..7aaa65b 100644 (file)
 
 #include <sys/stat.h>
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 #ifdef HAVE_FCNTL_H
-#include <fcntl.h>
+#  include <fcntl.h>
 #endif // HAVE_FCNTL_H
 #ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
+#  include <netinet/in.h>
 #endif // HAVE_NETINET_IN_H
 #include <netinet/tcp.h>
 #include <getopt.h>
@@ -50,7 +50,7 @@
 #include <openssl/err.h>
 
 #ifdef HAVE_JANSSON
-#include <jansson.h>
+#  include <jansson.h>
 #endif // HAVE_JANSSON
 
 #include "app_helper.h"
 #include "base64.h"
 #include "tls.h"
 #include "template.h"
+#include "ssl_compat.h"
 
 #ifndef O_BINARY
-#define O_BINARY (0)
+#  define O_BINARY (0)
 #endif // O_BINARY
 
 namespace nghttp2 {
@@ -126,6 +127,7 @@ Config::Config()
   nghttp2_option_set_peer_max_concurrent_streams(http2_option,
                                                  peer_max_concurrent_streams);
   nghttp2_option_set_builtin_recv_extension_type(http2_option, NGHTTP2_ALTSVC);
+  nghttp2_option_set_builtin_recv_extension_type(http2_option, NGHTTP2_ORIGIN);
 }
 
 Config::~Config() { nghttp2_option_del(http2_option); }
@@ -680,15 +682,16 @@ int HttpClient::initiate_connection() {
       const auto &host_string =
           config.host_override.empty() ? host : config.host_override;
 
-#if (!defined(LIBRESSL_VERSION_NUMBER) &&                                      \
-     OPENSSL_VERSION_NUMBER >= 0x10002000L) ||                                 \
+#if LIBRESSL_2_7_API ||                                                        \
+    (!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L) ||             \
     defined(OPENSSL_IS_BORINGSSL)
       auto param = SSL_get0_param(ssl);
       X509_VERIFY_PARAM_set_hostflags(param, 0);
       X509_VERIFY_PARAM_set1_host(param, host_string.c_str(),
                                   host_string.size());
-#endif // (!defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >=
-       // 0x10002000L) || defined(OPENSSL_IS_BORINGSSL)
+#endif // LIBRESSL_2_7_API || (!LIBRESSL_IN_USE &&
+       // OPENSSL_VERSION_NUMBER >= 0x10002000L) ||
+       // defined(OPENSSL_IS_BORINGSSL)
       SSL_set_verify(ssl, SSL_VERIFY_PEER, verify_cb);
 
       if (!util::numeric_host(host_string.c_str())) {
@@ -1095,7 +1098,9 @@ int HttpClient::connection_made() {
     // Check NPN or ALPN result
     const unsigned char *next_proto = nullptr;
     unsigned int next_proto_len;
+#ifndef OPENSSL_NO_NEXTPROTONEG
     SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len);
+#endif // !OPENSSL_NO_NEXTPROTONEG
     for (int i = 0; i < 2; ++i) {
       if (next_proto) {
         auto proto = StringRef{next_proto, next_proto_len};
@@ -2220,6 +2225,7 @@ id  responseEnd requestStart  process code size request path)"
 }
 } // namespace
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
 namespace {
 int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
                                 unsigned char *outlen, const unsigned char *in,
@@ -2243,6 +2249,7 @@ int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
   return SSL_TLSEXT_ERR_OK;
 }
 } // namespace
+#endif // !OPENSSL_NO_NEXTPROTONEG
 
 namespace {
 int communicate(
@@ -2308,8 +2315,10 @@ int communicate(
         goto fin;
       }
     }
+#ifndef OPENSSL_NO_NEXTPROTONEG
     SSL_CTX_set_next_proto_select_cb(ssl_ctx, client_select_next_proto_cb,
                                      nullptr);
+#endif // !OPENSSL_NO_NEXTPROTONEG
 
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
     auto proto_list = util::get_default_alpn();
index 3d970f0..120eb74 100644 (file)
 
 #include <sys/types.h>
 #ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
+#  include <sys/socket.h>
 #endif // HAVE_SYS_SOCKET_H
 #ifdef HAVE_NETDB_H
-#include <netdb.h>
+#  include <netdb.h>
 #endif // HAVE_NETDB_H
 
 #include <string>
index 7fbd365..8e6cd05 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_CONFIG_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif // HAVE_CONFIG_H
 
 #endif // NGHTTP2_CONFIG_H
index 2fa905a..a40352c 100644 (file)
  */
 #ifndef NGHTTP2_GZIP_H
 
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif /* HAVE_CONFIG_H */
-#include <zlib.h>
+#  ifdef HAVE_CONFIG_H
+#    include <config.h>
+#  endif /* HAVE_CONFIG_H */
+#  include <zlib.h>
 
-#include <nghttp2/nghttp2.h>
+#  include <nghttp2/nghttp2.h>
 
-#ifdef __cplusplus
+#  ifdef __cplusplus
 extern "C" {
-#endif
+#  endif
 
 /**
  * @struct
@@ -115,8 +115,8 @@ int nghttp2_gzip_inflate(nghttp2_gzip *inflater, uint8_t *out,
  */
 int nghttp2_gzip_inflate_finished(nghttp2_gzip *inflater);
 
-#ifdef __cplusplus
+#  ifdef __cplusplus
 }
-#endif
+#  endif
 
 #endif /* NGHTTP2_GZIP_H */
index 2defcdc..8d554f7 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_GZIP_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #ifdef __cplusplus
index c7b64b5..efa0110 100644 (file)
 #include "nghttp2_config.h"
 
 #ifdef __sgi
-#define daemon _daemonize
+#  define daemon _daemonize
 #endif
 
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 #include <signal.h>
 #include <getopt.h>
@@ -174,7 +174,8 @@ Options:
   --mime-types-file=<PATH>
               Path  to file  that contains  MIME media  types and  the
               extensions that represent them.
-              Default: )" << config.mime_types_file << R"(
+              Default: )"
+      << config.mime_types_file << R"(
   --no-content-length
               Don't send content-length header field.
   --version   Display version information and exit.
index 7f9bd80..dd4fadf 100644 (file)
@@ -23,7 +23,7 @@
  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif // HAVE_CONFIG_H
 
 #include <stdio.h>
index c9010dd..5809deb 100644 (file)
 #include <sys/wait.h>
 #include <sys/stat.h>
 #ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
+#  include <sys/socket.h>
 #endif // HAVE_SYS_SOCKET_H
 #include <sys/un.h>
 #ifdef HAVE_NETDB_H
-#include <netdb.h>
+#  include <netdb.h>
 #endif // HAVE_NETDB_H
 #include <signal.h>
 #ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
+#  include <netinet/in.h>
 #endif // HAVE_NETINET_IN_H
 #include <netinet/tcp.h>
 #ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
+#  include <arpa/inet.h>
 #endif // HAVE_ARPA_INET_H
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 #include <getopt.h>
 #ifdef HAVE_SYSLOG_H
-#include <syslog.h>
+#  include <syslog.h>
 #endif // HAVE_SYSLOG_H
 #ifdef HAVE_LIMITS_H
-#include <limits.h>
+#  include <limits.h>
 #endif // HAVE_LIMITS_H
 #ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
+#  include <sys/time.h>
 #endif // HAVE_SYS_TIME_H
 #include <sys/resource.h>
 #ifdef HAVE_LIBSYSTEMD
-#include <systemd/sd-daemon.h>
+#  include <systemd/sd-daemon.h>
 #endif // HAVE_LIBSYSTEMD
 
 #include <cinttypes>
@@ -131,16 +131,16 @@ constexpr auto ENV_ACCEPT_PREFIX = StringRef::from_lit("NGHTTPX_ACCEPT_");
 constexpr auto ENV_ORIG_PID = StringRef::from_lit("NGHTTPX_ORIG_PID");
 
 #ifndef _KERNEL_FASTOPEN
-#define _KERNEL_FASTOPEN
+#  define _KERNEL_FASTOPEN
 // conditional define for TCP_FASTOPEN mostly on ubuntu
-#ifndef TCP_FASTOPEN
-#define TCP_FASTOPEN 23
-#endif
+#  ifndef TCP_FASTOPEN
+#    define TCP_FASTOPEN 23
+#  endif
 
 // conditional define for SOL_TCP mostly on ubuntu
-#ifndef SOL_TCP
-#define SOL_TCP 6
-#endif
+#  ifndef SOL_TCP
+#    define SOL_TCP 6
+#  endif
 #endif
 
 // This configuration is fixed at the first startup of the main
@@ -1140,15 +1140,15 @@ int call_daemon() {
 #ifdef __sgi
   return _daemonize(0, 0, 0, 0);
 #else // !__sgi
-#ifdef HAVE_LIBSYSTEMD
+#  ifdef HAVE_LIBSYSTEMD
   if (sd_booted() && (getenv("NOTIFY_SOCKET") != nullptr)) {
     LOG(NOTICE) << "Daemonising disabled under systemd";
     chdir("/");
     return 0;
   }
-#endif // HAVE_LIBSYSTEMD
+#  endif // HAVE_LIBSYSTEMD
   return daemon(0, 0);
-#endif // !__sgi
+#endif   // !__sgi
 }
 } // namespace
 
@@ -1459,12 +1459,17 @@ void fill_default_config(Config *config) {
 
   tlsconf.session_timeout = std::chrono::hours(12);
   tlsconf.ciphers = StringRef::from_lit(nghttp2::tls::DEFAULT_CIPHER_LIST);
+  tlsconf.tls13_ciphers =
+      StringRef::from_lit(nghttp2::tls::DEFAULT_TLS13_CIPHER_LIST);
   tlsconf.client.ciphers =
       StringRef::from_lit(nghttp2::tls::DEFAULT_CIPHER_LIST);
+  tlsconf.client.tls13_ciphers =
+      StringRef::from_lit(nghttp2::tls::DEFAULT_TLS13_CIPHER_LIST);
   tlsconf.min_proto_version =
       tls::proto_version_from_string(DEFAULT_TLS_MIN_PROTO_VERSION);
   tlsconf.max_proto_version =
       tls::proto_version_from_string(DEFAULT_TLS_MAX_PROTO_VERSION);
+  tlsconf.max_early_data = 16_k;
 #if OPENSSL_1_1_API || defined(OPENSSL_IS_BORINGSSL)
   tlsconf.ecdh_curves = StringRef::from_lit("X25519:P-256:P-384:P-521");
 #else  // !OPENSSL_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
@@ -1482,6 +1487,7 @@ void fill_default_config(Config *config) {
   httpconf.max_requests = std::numeric_limits<size_t>::max();
   httpconf.xfp.add = true;
   httpconf.xfp.strip_incoming = true;
+  httpconf.early_data.strip_incoming = true;
 
   auto &http2conf = config->http2;
   {
@@ -1729,12 +1735,15 @@ Connections:
               The  parameters are  delimited  by  ";".  The  available
               parameters       are:      "proto=<PROTO>",       "tls",
               "sni=<SNI_HOST>",         "fall=<N>",        "rise=<N>",
-              "affinity=<METHOD>",  "dns", and  "redirect-if-not-tls".
-              The  parameter  consists   of  keyword,  and  optionally
-              followed by  "=" and value.  For  example, the parameter
-              "proto=h2"  consists of  the keyword  "proto" and  value
-              "h2".  The parameter "tls" consists of the keyword "tls"
-              without value.  Each parameter is described as follows.
+              "affinity=<METHOD>",    "dns",    "redirect-if-not-tls",
+              "upgrade-scheme",                        "mruby=<PATH>",
+              "read-timeout=<DURATION>",                           and
+              "write-timeout=<DURATION>".   The parameter  consists of
+              keyword, and optionally followed  by "=" and value.  For
+              example,  the  parameter   "proto=h2"  consists  of  the
+              keyword  "proto" and  value "h2".   The parameter  "tls"
+              consists  of  the  keyword "tls"  without  value.   Each
+              parameter is described as follows.
 
               The backend application protocol  can be specified using
               optional  "proto"   parameter,  and   in  the   form  of
@@ -1827,6 +1836,19 @@ Connections:
               server  which  requires  "https" :scheme  pseudo  header
               field on TLS encrypted connection.
 
+              "mruby=<PATH>"  parameter  specifies  a  path  to  mruby
+              script  file  which  is  invoked when  this  pattern  is
+              matched.  All backends which share the same pattern must
+              have the same mruby path.
+
+              "read-timeout=<DURATION>" and "write-timeout=<DURATION>"
+              parameters  specify the  read and  write timeout  of the
+              backend connection  when this  pattern is  matched.  All
+              backends which share the same pattern must have the same
+              timeouts.  If these timeouts  are entirely omitted for a
+              pattern,            --backend-read-timeout           and
+              --backend-write-timeout are used.
+
               Since ";" and ":" are  used as delimiter, <PATTERN> must
               not  contain these  characters.  Since  ";" has  special
               meaning in shell, the option value must be quoted.
@@ -1897,7 +1919,8 @@ Connections:
 Performance:
   -n, --workers=<N>
               Set the number of worker threads.
-              Default: )" << config->num_worker << R"(
+              Default: )"
+      << config->num_worker << R"(
   --single-thread
               Run everything in one  thread inside the worker process.
               This   feature   is   provided  for   better   debugging
@@ -2073,12 +2096,31 @@ SSL/TLS:
   --ciphers=<SUITE>
               Set allowed  cipher list  for frontend  connection.  The
               format of the string is described in OpenSSL ciphers(1).
+              This option  sets cipher suites for  TLSv1.2 or earlier.
+              Use --tls13-ciphers for TLSv1.3.
               Default: )"
       << config->tls.ciphers << R"(
+  --tls13-ciphers=<SUITE>
+              Set allowed  cipher list  for frontend  connection.  The
+              format of the string is described in OpenSSL ciphers(1).
+              This  option  sets  cipher   suites  for  TLSv1.3.   Use
+              --ciphers for TLSv1.2 or earlier.
+              Default: )"
+      << config->tls.tls13_ciphers << R"(
   --client-ciphers=<SUITE>
               Set  allowed cipher  list for  backend connection.   The
               format of the string is described in OpenSSL ciphers(1).
-              Default: )" << config->tls.client.ciphers << R"(
+              This option  sets cipher suites for  TLSv1.2 or earlier.
+              Use --tls13-client-ciphers for TLSv1.3.
+              Default: )"
+      << config->tls.client.ciphers << R"(
+  --tls13-client-ciphers=<SUITE>
+              Set  allowed cipher  list for  backend connection.   The
+              format of the string is described in OpenSSL ciphers(1).
+              This  option  sets  cipher   suites  for  TLSv1.3.   Use
+              --tls13-client-ciphers for TLSv1.2 or earlier.
+              Default: )"
+      << config->tls.client.tls13_ciphers << R"(
   --ecdh-curves=<LIST>
               Set  supported  curve  list  for  frontend  connections.
               <LIST> is a  colon separated list of curve  NID or names
@@ -2134,7 +2176,8 @@ SSL/TLS:
               NPN.  The parameter must be  delimited by a single comma
               only  and any  white spaces  are  treated as  a part  of
               protocol string.
-              Default: )" << DEFAULT_NPN_LIST
+              Default: )"
+      << DEFAULT_NPN_LIST
       << R"(
   --verify-client
               Require and verify client certificate.
@@ -2361,6 +2404,18 @@ SSL/TLS:
               HTTP/2.   To  use  those   cipher  suites  with  HTTP/2,
               consider   to  use   --client-no-http2-cipher-black-list
               option.  But be aware its implications.
+  --tls-no-postpone-early-data
+              By default,  nghttpx postpones forwarding  HTTP requests
+              sent in early data, including those sent in partially in
+              it, until TLS handshake finishes.  If all backend server
+              recognizes "Early-Data" header  field, using this option
+              makes nghttpx  not postpone  forwarding request  and get
+              full potential of 0-RTT data.
+  --tls-max-early-data=<SIZE>
+              Sets  the  maximum  amount  of 0-RTT  data  that  server
+              accepts.
+              Default: )"
+      << util::utos_unit(config->tls.max_early_data) << R"(
 
 HTTP/2:
   -c, --frontend-http2-max-concurrent-streams=<N>
@@ -2539,7 +2594,8 @@ Logging:
               Set path to write error  log.  To reopen file, send USR1
               signal  to nghttpx.   stderr will  be redirected  to the
               error log file unless --errorlog-syslog is used.
-              Default: )" << config->logging.error.file << R"(
+              Default: )"
+      << config->logging.error.file << R"(
   --errorlog-syslog
               Send  error log  to  syslog.  If  this  option is  used,
               --errorlog-file option is ignored.
@@ -2599,6 +2655,9 @@ HTTP:
               Default: obfuscated
   --no-via    Don't append to  Via header field.  If  Via header field
               is received, it is left unaltered.
+  --no-strip-incoming-early-data
+              Don't strip Early-Data header  field from inbound client
+              requests.
   --no-location-rewrite
               Don't  rewrite location  header field  in default  mode.
               When --http2-proxy  is used, location header  field will
@@ -2671,8 +2730,8 @@ HTTP:
               Specify the port number which appears in Location header
               field  when  redirect  to  HTTPS  URI  is  made  due  to
               "redirect-if-not-tls" parameter in --backend option.
-              Default: )" << config->http.redirect_https_port
-      << R"(
+              Default: )"
+      << config->http.redirect_https_port << R"(
 
 API:
   --api-max-request-body=<SIZE>
@@ -2745,13 +2804,18 @@ Process:
 Scripting:
   --mruby-file=<PATH>
               Set mruby script file
+  --ignore-per-pattern-mruby-error
+              Ignore mruby compile error  for per-pattern mruby script
+              file.  If error  occurred, it is treated as  if no mruby
+              file were specified for the pattern.
 
 Misc:
   --conf=<PATH>
               Load  configuration  from   <PATH>.   Please  note  that
               nghttpx always  tries to read the  default configuration
               file if --conf is not given.
-              Default: )" << config->conf_path << R"(
+              Default: )"
+      << config->conf_path << R"(
   --include=<PATH>
               Load additional configurations from <PATH>.  File <PATH>
               is  read  when  configuration  parser  encountered  this
@@ -2769,7 +2833,8 @@ Misc:
   The <DURATION> argument is an integer and an optional unit (e.g., 1s
   is 1 second and 500ms is 500 milliseconds).  Units are h, m, s or ms
   (hours, minutes, seconds and milliseconds, respectively).  If a unit
-  is omitted, a second is used as unit.)" << std::endl;
+  is omitted, a second is used as unit.)"
+      << std::endl;
 }
 } // namespace
 
@@ -3418,6 +3483,14 @@ int main(int argc, char **argv) {
         {SHRPX_OPT_SINGLE_PROCESS.c_str(), no_argument, &flag, 159},
         {SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED.c_str(), no_argument, &flag,
          160},
+        {SHRPX_OPT_IGNORE_PER_PATTERN_MRUBY_ERROR.c_str(), no_argument, &flag,
+         161},
+        {SHRPX_OPT_TLS_NO_POSTPONE_EARLY_DATA.c_str(), no_argument, &flag, 162},
+        {SHRPX_OPT_TLS_MAX_EARLY_DATA.c_str(), required_argument, &flag, 163},
+        {SHRPX_OPT_TLS13_CIPHERS.c_str(), required_argument, &flag, 164},
+        {SHRPX_OPT_TLS13_CLIENT_CIPHERS.c_str(), required_argument, &flag, 165},
+        {SHRPX_OPT_NO_STRIP_INCOMING_EARLY_DATA.c_str(), no_argument, &flag,
+         166},
         {nullptr, 0, nullptr, 0}};
 
     int option_index = 0;
@@ -4184,6 +4257,33 @@ int main(int argc, char **argv) {
         cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED,
                              StringRef::from_lit("yes"));
         break;
+      case 161:
+        // --ignore-per-pattern-mruby-error
+        cmdcfgs.emplace_back(SHRPX_OPT_IGNORE_PER_PATTERN_MRUBY_ERROR,
+                             StringRef::from_lit("yes"));
+        break;
+      case 162:
+        // --tls-no-postpone-early-data
+        cmdcfgs.emplace_back(SHRPX_OPT_TLS_NO_POSTPONE_EARLY_DATA,
+                             StringRef::from_lit("yes"));
+        break;
+      case 163:
+        // --tls-max-early-data
+        cmdcfgs.emplace_back(SHRPX_OPT_TLS_MAX_EARLY_DATA, StringRef{optarg});
+        break;
+      case 164:
+        // --tls13-ciphers
+        cmdcfgs.emplace_back(SHRPX_OPT_TLS13_CIPHERS, StringRef{optarg});
+        break;
+      case 165:
+        // --tls13-client-ciphers
+        cmdcfgs.emplace_back(SHRPX_OPT_TLS13_CLIENT_CIPHERS, StringRef{optarg});
+        break;
+      case 166:
+        // --no-strip-incoming-early-data
+        cmdcfgs.emplace_back(SHRPX_OPT_NO_STRIP_INCOMING_EARLY_DATA,
+                             StringRef::from_lit("yes"));
+        break;
       default:
         break;
       }
index c12064b..60e873d 100644 (file)
 #define SHRPX_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif // HAVE_CONFIG_H
 
 #include <sys/types.h>
 #ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
+#  include <sys/socket.h>
 #endif // HAVE_SYS_SOCKET_H
 
 #include <cassert>
 
 #ifndef HAVE__EXIT
-#define nghttp2_Exit(status) _exit(status)
+#  define nghttp2_Exit(status) _exit(status)
 #else // HAVE__EXIT
-#define nghttp2_Exit(status) _Exit(status)
+#  define nghttp2_Exit(status) _Exit(status)
 #endif // HAVE__EXIT
 
 #define DIE() nghttp2_Exit(EXIT_FAILURE)
index 3f41b83..01b6415 100644 (file)
@@ -25,7 +25,7 @@
 #include "shrpx_accept_handler.h"
 
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 
 #include <cerrno>
index 4d56ecf..c45f92b 100644 (file)
@@ -265,6 +265,11 @@ int APIDownstreamConnection::push_request_headers() {
   }
   }
 
+  downstream_->set_request_header_sent(true);
+  auto src = downstream_->get_blocked_request_buf();
+  auto dest = downstream_->get_request_buf();
+  src->remove(*dest);
+
   return 0;
 }
 
index 21430dd..d718edd 100644 (file)
 #include "shrpx_client_handler.h"
 
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 #ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
+#  include <sys/socket.h>
 #endif // HAVE_SYS_SOCKET_H
 #ifdef HAVE_NETDB_H
-#include <netdb.h>
+#  include <netdb.h>
 #endif // HAVE_NETDB_H
 
 #include <cerrno>
@@ -549,7 +549,9 @@ int ClientHandler::validate_next_proto() {
   // First set callback for catch all cases
   on_read_ = &ClientHandler::upstream_read;
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
   SSL_get0_next_proto_negotiated(conn_.tls.ssl, &next_proto, &next_proto_len);
+#endif // !OPENSSL_NO_NEXTPROTONEG
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
   if (next_proto == nullptr) {
     SSL_get0_alpn_selected(conn_.tls.ssl, &next_proto, &next_proto_len);
@@ -936,7 +938,8 @@ uint32_t ClientHandler::get_affinity_cookie(Downstream *downstream,
 }
 
 std::unique_ptr<DownstreamConnection>
-ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
+ClientHandler::get_downstream_connection(int &err, Downstream *downstream,
+                                         shrpx_proto pref_proto) {
   size_t group_idx;
   auto &downstreamconf = *worker_->get_downstream_config();
   auto &routerconf = downstreamconf.router;
@@ -976,7 +979,7 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
     StringRef path;
     // CONNECT method does not have path.  But we requires path in
     // host-path mapping.  As workaround, we assume that path is "/".
-    if (req.method != HTTP_CONNECT) {
+    if (!req.regular_connect_method()) {
       path = req.path;
     }
 
@@ -1039,7 +1042,8 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
           i = 0;
         }
         addr = &shared_addr->addrs[shared_addr->affinity_hash[i].idx];
-        if (addr->connect_blocker->blocked()) {
+        if (addr->connect_blocker->blocked() ||
+            (pref_proto != PROTO_NONE && pref_proto != addr->proto)) {
           continue;
         }
         break;
@@ -1079,7 +1083,15 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
 
   auto proto = PROTO_NONE;
 
-  if (http1_weight > 0 && http2_weight > 0) {
+  if (pref_proto == PROTO_HTTP1) {
+    if (http1_weight > 0) {
+      proto = PROTO_HTTP1;
+    }
+  } else if (pref_proto == PROTO_HTTP2) {
+    if (http2_weight > 0) {
+      proto = PROTO_HTTP2;
+    }
+  } else if (http1_weight > 0 && http2_weight > 0) {
     // We only advance cycle if both weight has nonzero to keep its
     // distance under WEIGHT_MAX.
     if (pri_less(shared_addr->http1_pri, shared_addr->http2_pri)) {
index 8d4073b..9e1153b 100644 (file)
@@ -102,9 +102,11 @@ public:
   // Returns DownstreamConnection object based on request path.  This
   // function returns non-null DownstreamConnection, and assigns 0 to
   // |err| if it succeeds, or returns nullptr, and assigns negative
-  // error code to |err|.
+  // error code to |err|.  If |pref_proto| is not PROTO_NONE, choose
+  // backend whose protocol is |pref_proto|.
   std::unique_ptr<DownstreamConnection>
-  get_downstream_connection(int &err, Downstream *downstream);
+  get_downstream_connection(int &err, Downstream *downstream,
+                            shrpx_proto pref_proto = PROTO_NONE);
   MemchunkPool *get_mcpool();
   SSL *get_ssl() const;
   // Call this function when HTTP/2 connection header is received at
index e7efed2..b79a799 100644 (file)
 #include "shrpx_config.h"
 
 #ifdef HAVE_PWD_H
-#include <pwd.h>
+#  include <pwd.h>
 #endif // HAVE_PWD_H
 #ifdef HAVE_NETDB_H
-#include <netdb.h>
+#  include <netdb.h>
 #endif // HAVE_NETDB_H
 #ifdef HAVE_SYSLOG_H
-#include <syslog.h>
+#  include <syslog.h>
 #endif // HAVE_SYSLOG_H
 #include <sys/types.h>
 #include <sys/stat.h>
 #ifdef HAVE_FCNTL_H
-#include <fcntl.h>
+#  include <fcntl.h>
 #endif // HAVE_FCNTL_H
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 #include <dirent.h>
 
@@ -55,6 +55,9 @@
 #include "shrpx_log.h"
 #include "shrpx_tls.h"
 #include "shrpx_http.h"
+#ifdef HAVE_MRUBY
+#  include "shrpx_mruby.h"
+#endif // HAVE_MRUBY
 #include "util.h"
 #include "base64.h"
 #include "ssl_compat.h"
@@ -807,7 +810,10 @@ int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
 
 struct DownstreamParams {
   StringRef sni;
+  StringRef mruby;
   AffinityConfig affinity;
+  ev_tstamp read_timeout;
+  ev_tstamp write_timeout;
   size_t fall;
   size_t rise;
   shrpx_proto proto;
@@ -818,6 +824,22 @@ struct DownstreamParams {
 };
 
 namespace {
+// Parses |value| of parameter named |name| as duration.  This
+// function returns 0 if it succeeds and the parsed value is assigned
+// to |dest|, or -1.
+int parse_downstream_param_duration(ev_tstamp &dest, const StringRef &name,
+                                    const StringRef &value) {
+  auto t = util::parse_duration_with_unit(value);
+  if (t == std::numeric_limits<double>::infinity()) {
+    LOG(ERROR) << "backend: " << name << ": bad value: '" << value << "'";
+    return -1;
+  }
+  dest = t;
+  return 0;
+}
+} // namespace
+
+namespace {
 // Parses downstream configuration parameter |src_params|, and stores
 // parsed results into |out|.  This function returns 0 if it succeeds,
 // or -1.
@@ -921,6 +943,21 @@ int parse_downstream_params(DownstreamParams &out,
       out.redirect_if_not_tls = true;
     } else if (util::strieq_l("upgrade-scheme", param)) {
       out.upgrade_scheme = true;
+    } else if (util::istarts_with_l(param, "mruby=")) {
+      auto valstr = StringRef{first + str_size("mruby="), end};
+      out.mruby = valstr;
+    } else if (util::istarts_with_l(param, "read-timeout=")) {
+      if (parse_downstream_param_duration(
+              out.read_timeout, StringRef::from_lit("read-timeout"),
+              StringRef{first + str_size("read-timeout="), end}) == -1) {
+        return -1;
+      }
+    } else if (util::istarts_with_l(param, "write-timeout=")) {
+      if (parse_downstream_param_duration(
+              out.write_timeout, StringRef::from_lit("write-timeout"),
+              StringRef{first + str_size("write-timeout="), end}) == -1) {
+        return -1;
+      }
     } else if (!param.empty()) {
       LOG(ERROR) << "backend: " << param << ": unknown keyword";
       return -1;
@@ -1045,6 +1082,43 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
       if (params.redirect_if_not_tls) {
         g.redirect_if_not_tls = true;
       }
+      // All backends in the same group must have the same mruby path.
+      // If some backends do not specify mruby file, and there is at
+      // least one backend with mruby file, it is used for all
+      // backends in the group.
+      if (!params.mruby.empty()) {
+        if (g.mruby_file.empty()) {
+          g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby);
+        } else if (g.mruby_file != params.mruby) {
+          LOG(ERROR) << "backend: mruby: multiple different mruby file found "
+                        "in a single group";
+          return -1;
+        }
+      }
+      // All backends in the same group must have the same read/write
+      // timeout.  If some backends do not specify read/write timeout,
+      // and there is at least one backend with read/write timeout, it
+      // is used for all backends in the group.
+      if (params.read_timeout > 1e-9) {
+        if (g.timeout.read < 1e-9) {
+          g.timeout.read = params.read_timeout;
+        } else if (fabs(g.timeout.read - params.read_timeout) > 1e-9) {
+          LOG(ERROR)
+              << "backend: read-timeout: multiple different read-timeout "
+                 "found in a single group";
+          return -1;
+        }
+      }
+      if (params.write_timeout > 1e-9) {
+        if (g.timeout.write < 1e-9) {
+          g.timeout.write = params.write_timeout;
+        } else if (fabs(g.timeout.write - params.write_timeout) > 1e-9) {
+          LOG(ERROR) << "backend: write-timeout: multiple different "
+                        "write-timeout found in a single group";
+          return -1;
+        }
+      }
+
       g.addrs.push_back(addr);
       continue;
     }
@@ -1065,6 +1139,9 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
       g.affinity.cookie.secure = params.affinity.cookie.secure;
     }
     g.redirect_if_not_tls = params.redirect_if_not_tls;
+    g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby);
+    g.timeout.read = params.read_timeout;
+    g.timeout.write = params.write_timeout;
 
     if (pattern[0] == '*') {
       // wildcard pattern
@@ -1222,7 +1299,7 @@ int parse_subcert_params(SubcertParams &out, const StringRef &src_params) {
     auto param = StringRef{first, end};
 
     if (util::istarts_with_l(param, "sct-dir=")) {
-#if !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
+#if !LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L
       auto sct_dir =
           StringRef{std::begin(param) + str_size("sct-dir="), std::end(param)};
       if (sct_dir.empty()) {
@@ -1230,9 +1307,9 @@ int parse_subcert_params(SubcertParams &out, const StringRef &src_params) {
         return -1;
       }
       out.sct_dir = sct_dir;
-#else  // !(!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L)
+#else  // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
       LOG(WARN) << "subcert: sct-dir requires OpenSSL >= 1.0.2";
-#endif // !(!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L)
+#endif // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
     } else if (!param.empty()) {
       LOG(ERROR) << "subcert: " << param << ": unknown keyword";
       return -1;
@@ -1364,7 +1441,7 @@ int read_tls_sct_from_dir(std::vector<uint8_t> &dst, const StringRef &opt,
 }
 } // namespace
 
-#if !LIBRESSL_IN_USE
+#if !LIBRESSL_LEGACY_API
 namespace {
 // Reads PSK secrets from path, and parses each line.  The result is
 // directly stored into config->tls.psk_secrets.  This function
@@ -1428,9 +1505,9 @@ int parse_psk_secrets(Config *config, const StringRef &path) {
   return 0;
 }
 } // namespace
-#endif // !LIBRESSL_IN_USE
+#endif // !LIBRESSL_LEGACY_API
 
-#if !LIBRESSL_IN_USE
+#if !LIBRESSL_LEGACY_API
 namespace {
 // Reads PSK secrets from path, and parses each line.  The result is
 // directly stored into config->tls.client.psk.  This function returns
@@ -1490,7 +1567,7 @@ int parse_client_psk_secrets(Config *config, const StringRef &path) {
   return 0;
 }
 } // namespace
-#endif // !LIBRESSL_IN_USE
+#endif // !LIBRESSL_LEGACY_API
 
 // generated by gennghttpxfun.py
 int option_lookup_token(const char *name, size_t namelen) {
@@ -1739,6 +1816,11 @@ int option_lookup_token(const char *name, size_t namelen) {
         return SHRPX_OPTID_FORWARDED_FOR;
       }
       break;
+    case 's':
+      if (util::strieq_l("tls13-cipher", name, 12)) {
+        return SHRPX_OPTID_TLS13_CIPHERS;
+      }
+      break;
     case 't':
       if (util::strieq_l("verify-clien", name, 12)) {
         return SHRPX_OPTID_VERIFY_CLIENT;
@@ -1863,6 +1945,11 @@ int option_lookup_token(const char *name, size_t namelen) {
     break;
   case 18:
     switch (name[17]) {
+    case 'a':
+      if (util::strieq_l("tls-max-early-dat", name, 17)) {
+        return SHRPX_OPTID_TLS_MAX_EARLY_DATA;
+      }
+      break;
     case 'r':
       if (util::strieq_l("add-request-heade", name, 17)) {
         return SHRPX_OPTID_ADD_REQUEST_HEADER;
@@ -1931,6 +2018,11 @@ int option_lookup_token(const char *name, size_t namelen) {
         return SHRPX_OPTID_OCSP_UPDATE_INTERVAL;
       }
       break;
+    case 's':
+      if (util::strieq_l("tls13-client-cipher", name, 19)) {
+        return SHRPX_OPTID_TLS13_CLIENT_CIPHERS;
+      }
+      break;
     case 't':
       if (util::strieq_l("backend-read-timeou", name, 19)) {
         return SHRPX_OPTID_BACKEND_READ_TIMEOUT;
@@ -2094,6 +2186,11 @@ int option_lookup_token(const char *name, size_t namelen) {
     break;
   case 26:
     switch (name[25]) {
+    case 'a':
+      if (util::strieq_l("tls-no-postpone-early-dat", name, 25)) {
+        return SHRPX_OPTID_TLS_NO_POSTPONE_EARLY_DATA;
+      }
+      break;
     case 'e':
       if (util::strieq_l("frontend-http2-window-siz", name, 25)) {
         return SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE;
@@ -2146,6 +2243,11 @@ int option_lookup_token(const char *name, size_t namelen) {
     break;
   case 28:
     switch (name[27]) {
+    case 'a':
+      if (util::strieq_l("no-strip-incoming-early-dat", name, 27)) {
+        return SHRPX_OPTID_NO_STRIP_INCOMING_EARLY_DATA;
+      }
+      break;
     case 'd':
       if (util::strieq_l("tls-dyn-rec-warmup-threshol", name, 27)) {
         return SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD;
@@ -2179,6 +2281,9 @@ int option_lookup_token(const char *name, size_t namelen) {
       }
       break;
     case 'r':
+      if (util::strieq_l("ignore-per-pattern-mruby-erro", name, 29)) {
+        return SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR;
+      }
       if (util::strieq_l("strip-incoming-x-forwarded-fo", name, 29)) {
         return SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR;
       }
@@ -2800,6 +2905,10 @@ int parse_config(Config *config, int optid, const StringRef &opt,
     config->tls.ciphers = make_string_ref(config->balloc, optarg);
 
     return 0;
+  case SHRPX_OPTID_TLS13_CIPHERS:
+    config->tls.tls13_ciphers = make_string_ref(config->balloc, optarg);
+
+    return 0;
   case SHRPX_OPTID_CLIENT:
     LOG(ERROR) << opt
                << ": deprecated.  Use frontend=<addr>,<port>;no-tls, "
@@ -3454,19 +3563,19 @@ int parse_config(Config *config, int optid, const StringRef &opt,
     return parse_uint_with_unit(
         &config->http2.downstream.decoder_dynamic_table_size, opt, optarg);
   case SHRPX_OPTID_ECDH_CURVES:
-#if !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
+#if !LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L
     config->tls.ecdh_curves = make_string_ref(config->balloc, optarg);
-#else  // !(!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L)
+#else  // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
     LOG(WARN) << opt << ": This option requires OpenSSL >= 1.0.2";
-#endif // !(!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L)
+#endif // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
     return 0;
   case SHRPX_OPTID_TLS_SCT_DIR:
-#if !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
+#if !LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L
     return read_tls_sct_from_dir(config->tls.sct_data, opt, optarg);
-#else  // !(!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L)
+#else  // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
     LOG(WARN) << opt << ": This option requires OpenSSL >= 1.0.2";
     return 0;
-#endif // !(!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L)
+#endif // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
   case SHRPX_OPTID_DNS_CACHE_TIMEOUT:
     return parse_duration(&config->dns.timeout.cache, opt, optarg);
   case SHRPX_OPTID_DNS_LOOKUP_TIMEOUT:
@@ -3489,23 +3598,23 @@ int parse_config(Config *config, int optid, const StringRef &opt,
     return parse_duration(&config->conn.upstream.timeout.idle_read, opt,
                           optarg);
   case SHRPX_OPTID_PSK_SECRETS:
-#if !LIBRESSL_IN_USE
+#if !LIBRESSL_LEGACY_API
     return parse_psk_secrets(config, optarg);
-#else  // LIBRESSL_IN_USE
+#else  // LIBRESSL_LEGACY_API
     LOG(WARN)
         << opt
         << ": ignored because underlying TLS library does not support PSK";
     return 0;
-#endif // LIBRESSL_IN_USE
+#endif // LIBRESSL_LEGACY_API
   case SHRPX_OPTID_CLIENT_PSK_SECRETS:
-#if !LIBRESSL_IN_USE
+#if !LIBRESSL_LEGACY_API
     return parse_client_psk_secrets(config, optarg);
-#else  // LIBRESSL_IN_USE
+#else  // LIBRESSL_LEGACY_API
     LOG(WARN)
         << opt
         << ": ignored because underlying TLS library does not support PSK";
     return 0;
-#endif // LIBRESSL_IN_USE
+#endif // LIBRESSL_LEGACY_API
   case SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST:
     config->tls.client.no_http2_cipher_black_list =
         util::strieq_l("yes", optarg);
@@ -3515,6 +3624,10 @@ int parse_config(Config *config, int optid, const StringRef &opt,
     config->tls.client.ciphers = make_string_ref(config->balloc, optarg);
 
     return 0;
+  case SHRPX_OPTID_TLS13_CLIENT_CIPHERS:
+    config->tls.client.tls13_ciphers = make_string_ref(config->balloc, optarg);
+
+    return 0;
   case SHRPX_OPTID_ACCESSLOG_WRITE_EARLY:
     config->logging.access.write_early = util::strieq_l("yes", optarg);
 
@@ -3564,6 +3677,21 @@ int parse_config(Config *config, int optid, const StringRef &opt,
     config->tls.client_verify.tolerate_expired = util::strieq_l("yes", optarg);
 
     return 0;
+  case SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR:
+    config->ignore_per_pattern_mruby_error = util::strieq_l("yes", optarg);
+
+    return 0;
+  case SHRPX_OPTID_TLS_NO_POSTPONE_EARLY_DATA:
+    config->tls.no_postpone_early_data = util::strieq_l("yes", optarg);
+
+    return 0;
+  case SHRPX_OPTID_TLS_MAX_EARLY_DATA: {
+    return parse_uint_with_unit(&config->tls.max_early_data, opt, optarg);
+  }
+  case SHRPX_OPTID_NO_STRIP_INCOMING_EARLY_DATA:
+    config->http.early_data.strip_incoming = !util::strieq_l("yes", optarg);
+
+    return 0;
   case SHRPX_OPTID_CONF:
     LOG(WARN) << "conf: ignored";
 
@@ -3854,8 +3982,33 @@ int configure_downstream_group(Config *config, bool http2_proxy,
                   << (addr.tls ? ", tls" : "");
       }
     }
+#ifdef HAVE_MRUBY
+    // Try compile mruby script and catch compile error early.
+    if (!g.mruby_file.empty()) {
+      if (mruby::create_mruby_context(g.mruby_file) == nullptr) {
+        LOG(config->ignore_per_pattern_mruby_error ? ERROR : FATAL)
+            << "backend: Could not compile mruby flie for pattern "
+            << g.pattern;
+        if (!config->ignore_per_pattern_mruby_error) {
+          return -1;
+        }
+        g.mruby_file = StringRef{};
+      }
+    }
+#endif // HAVE_MRUBY
   }
 
+#ifdef HAVE_MRUBY
+  // Try compile mruby script (--mruby-file) here to catch compile
+  // error early.
+  if (!config->mruby_file.empty()) {
+    if (mruby::create_mruby_context(config->mruby_file) == nullptr) {
+      LOG(FATAL) << "mruby-file: Could not compile mruby file";
+      return -1;
+    }
+  }
+#endif // HAVE_MRUBY
+
   if (catch_all_group == -1) {
     LOG(FATAL) << "backend: No catch-all backend address is configured";
     return -1;
@@ -3950,6 +4103,14 @@ int configure_downstream_group(Config *config, bool http2_proxy,
                   return lhs.hash < rhs.hash;
                 });
     }
+
+    auto &timeout = g.timeout;
+    if (timeout.read < 1e-9) {
+      timeout.read = downstreamconf.timeout.read;
+    }
+    if (timeout.write < 1e-9) {
+      timeout.write = downstreamconf.timeout.write;
+    }
   }
 
   return 0;
index d631a56..3b60e77 100644 (file)
 
 #include <sys/types.h>
 #ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
+#  include <sys/socket.h>
 #endif // HAVE_SYS_SOCKET_H
 #include <sys/un.h>
 #ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
+#  include <netinet/in.h>
 #endif // HAVE_NETINET_IN_H
 #ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
+#  include <arpa/inet.h>
 #endif // HAVE_ARPA_INET_H
 #include <cinttypes>
 #include <cstdio>
@@ -345,6 +345,17 @@ constexpr auto SHRPX_OPT_OCSP_STARTUP = StringRef::from_lit("ocsp-startup");
 constexpr auto SHRPX_OPT_NO_VERIFY_OCSP = StringRef::from_lit("no-verify-ocsp");
 constexpr auto SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED =
     StringRef::from_lit("verify-client-tolerate-expired");
+constexpr auto SHRPX_OPT_IGNORE_PER_PATTERN_MRUBY_ERROR =
+    StringRef::from_lit("ignore-per-pattern-mruby-error");
+constexpr auto SHRPX_OPT_TLS_NO_POSTPONE_EARLY_DATA =
+    StringRef::from_lit("tls-no-postpone-early-data");
+constexpr auto SHRPX_OPT_TLS_MAX_EARLY_DATA =
+    StringRef::from_lit("tls-max-early-data");
+constexpr auto SHRPX_OPT_TLS13_CIPHERS = StringRef::from_lit("tls13-ciphers");
+constexpr auto SHRPX_OPT_TLS13_CLIENT_CIPHERS =
+    StringRef::from_lit("tls13-client-ciphers");
+constexpr auto SHRPX_OPT_NO_STRIP_INCOMING_EARLY_DATA =
+    StringRef::from_lit("no-strip-incoming-early-data");
 
 constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
 
@@ -483,6 +494,7 @@ struct DownstreamAddrGroupConfig {
       : pattern(pattern), affinity{AFFINITY_NONE}, redirect_if_not_tls(false) {}
 
   StringRef pattern;
+  StringRef mruby_file;
   std::vector<DownstreamAddrConfig> addrs;
   // Bunch of session affinity hash.  Only used if affinity ==
   // AFFINITY_IP.
@@ -492,6 +504,11 @@ struct DownstreamAddrGroupConfig {
   // true if this group requires that client connection must be TLS,
   // and the request must be redirected to https URI.
   bool redirect_if_not_tls;
+  // Timeouts for backend connection.
+  struct {
+    ev_tstamp read;
+    ev_tstamp write;
+  } timeout;
 };
 
 struct TicketKey {
@@ -619,6 +636,7 @@ struct TLSConfig {
     StringRef private_key_file;
     StringRef cert_file;
     StringRef ciphers;
+    StringRef tls13_ciphers;
     bool no_http2_cipher_black_list;
   } client;
 
@@ -645,14 +663,20 @@ struct TLSConfig {
   StringRef cert_file;
   StringRef dh_param_file;
   StringRef ciphers;
+  StringRef tls13_ciphers;
   StringRef ecdh_curves;
   StringRef cacert;
+  // The maximum amount of 0-RTT data that server accepts.
+  uint32_t max_early_data;
   // The minimum and maximum TLS version.  These values are defined in
   // OpenSSL header file.
   int min_proto_version;
   int max_proto_version;
   bool insecure;
   bool no_http2_cipher_black_list;
+  // true if forwarding requests included in TLS early data should not
+  // be postponed until TLS handshake finishes.
+  bool no_postpone_early_data;
 };
 
 // custom error page
@@ -687,6 +711,9 @@ struct HttpConfig {
     bool add;
     bool strip_incoming;
   } xfp;
+  struct {
+    bool strip_incoming;
+  } early_data;
   std::vector<AltSvc> altsvcs;
   std::vector<ErrorPage> error_pages;
   HeaderRefs add_request_headers;
@@ -915,6 +942,7 @@ struct Config {
         http2_proxy{false},
         single_process{false},
         single_thread{false},
+        ignore_per_pattern_mruby_error{false},
         ev_loop_flags{0} {}
   ~Config();
 
@@ -959,6 +987,8 @@ struct Config {
   // handling is omitted.
   bool single_process;
   bool single_thread;
+  // Ignore mruby compile error for per-pattern mruby script.
+  bool ignore_per_pattern_mruby_error;
   // flags passed to ev_default_loop() and ev_loop_new()
   int ev_loop_flags;
 };
@@ -1063,6 +1093,7 @@ enum {
   SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS,
   SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING,
   SHRPX_OPTID_HTTP2_PROXY,
+  SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR,
   SHRPX_OPTID_INCLUDE,
   SHRPX_OPTID_INSECURE,
   SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT,
@@ -1079,6 +1110,7 @@ enum {
   SHRPX_OPTID_NO_OCSP,
   SHRPX_OPTID_NO_SERVER_PUSH,
   SHRPX_OPTID_NO_SERVER_REWRITE,
+  SHRPX_OPTID_NO_STRIP_INCOMING_EARLY_DATA,
   SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO,
   SHRPX_OPTID_NO_VERIFY_OCSP,
   SHRPX_OPTID_NO_VIA,
@@ -1107,8 +1139,10 @@ enum {
   SHRPX_OPTID_SYSLOG_FACILITY,
   SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT,
   SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD,
+  SHRPX_OPTID_TLS_MAX_EARLY_DATA,
   SHRPX_OPTID_TLS_MAX_PROTO_VERSION,
   SHRPX_OPTID_TLS_MIN_PROTO_VERSION,
+  SHRPX_OPTID_TLS_NO_POSTPONE_EARLY_DATA,
   SHRPX_OPTID_TLS_PROTO_LIST,
   SHRPX_OPTID_TLS_SCT_DIR,
   SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED,
@@ -1126,6 +1160,8 @@ enum {
   SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY,
   SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE,
   SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS,
+  SHRPX_OPTID_TLS13_CIPHERS,
+  SHRPX_OPTID_TLS13_CLIENT_CIPHERS,
   SHRPX_OPTID_USER,
   SHRPX_OPTID_VERIFY_CLIENT,
   SHRPX_OPTID_VERIFY_CLIENT_CACERT,
index 7531971..b970ccb 100644 (file)
@@ -25,7 +25,7 @@
 #include "shrpx_config_test.h"
 
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 
 #include <cstdlib>
index 3e86a4b..a30de41 100644 (file)
@@ -26,7 +26,7 @@
 #define SHRPX_CONFIG_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif // HAVE_CONFIG_H
 
 namespace shrpx {
index 06ad958..335e4e4 100644 (file)
@@ -25,7 +25,7 @@
 #include "shrpx_connection.h"
 
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 #include <netinet/tcp.h>
 
@@ -44,13 +44,13 @@ using namespace nghttp2;
 
 namespace shrpx {
 
-#if !OPENSSL_1_1_API
+#if !LIBRESSL_2_7_API && !OPENSSL_1_1_API
 
 void *BIO_get_data(BIO *bio) { return bio->ptr; }
 void BIO_set_data(BIO *bio, void *ptr) { bio->ptr = ptr; }
 void BIO_set_init(BIO *bio, int init) { bio->init = init; }
 
-#endif // !OPENSSL_1_1_API
+#endif // !LIBRESSL_2_7_API && !OPENSSL_1_1_API
 
 Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
                        MemchunkPool *mcpool, ev_tstamp write_timeout,
@@ -60,7 +60,8 @@ Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl,
                        IOCb readcb, TimerCb timeoutcb, void *data,
                        size_t tls_dyn_rec_warmup_threshold,
                        ev_tstamp tls_dyn_rec_idle_timeout, shrpx_proto proto)
-    : tls{DefaultMemchunks(mcpool), DefaultPeekMemchunks(mcpool)},
+    : tls{DefaultMemchunks(mcpool), DefaultPeekMemchunks(mcpool),
+          DefaultMemchunks(mcpool)},
       wlimit(loop, &wev, write_limit.rate, write_limit.burst),
       rlimit(loop, &rev, read_limit.rate, read_limit.burst, this),
       loop(loop),
@@ -120,10 +121,11 @@ void Connection::disconnect() {
     tls.warmup_writelen = 0;
     tls.last_writelen = 0;
     tls.last_readlen = 0;
-    tls.handshake_state = 0;
+    tls.handshake_state = TLS_CONN_NORMAL;
     tls.initial_handshake_done = false;
     tls.reneg_started = false;
     tls.sct_requested = false;
+    tls.early_data_finish = false;
   }
 
   if (fd != -1) {
@@ -141,7 +143,11 @@ void Connection::disconnect() {
   wlimit.stopw();
 }
 
-void Connection::prepare_client_handshake() { SSL_set_connect_state(tls.ssl); }
+void Connection::prepare_client_handshake() {
+  SSL_set_connect_state(tls.ssl);
+  // This prevents SSL_read_early_data from being called.
+  tls.early_data_finish = true;
+}
 
 void Connection::prepare_server_handshake() {
   SSL_set_accept_state(tls.ssl);
@@ -327,8 +333,9 @@ int Connection::tls_handshake() {
   wlimit.stopw();
   ev_timer_stop(loop, &wt);
 
+  std::array<uint8_t, 16_k> buf;
+
   if (ev_is_active(&rev)) {
-    std::array<uint8_t, 8_k> buf;
     auto nread = read_clear(buf.data(), buf.size());
     if (nread < 0) {
       if (LOG_ENABLED(INFO)) {
@@ -381,9 +388,65 @@ int Connection::tls_handshake() {
     break;
   }
 
+  int rv;
+
   ERR_clear_error();
 
-  auto rv = SSL_do_handshake(tls.ssl);
+#if OPENSSL_1_1_1_API
+  if (!tls.server_handshake || tls.early_data_finish) {
+    rv = SSL_do_handshake(tls.ssl);
+  } else {
+    auto &tlsconf = get_config()->tls;
+    for (;;) {
+      size_t nread;
+
+      rv = SSL_read_early_data(tls.ssl, buf.data(), buf.size(), &nread);
+      if (rv == SSL_READ_EARLY_DATA_ERROR) {
+        // If we have early data, and server sends ServerHello, assume
+        // that handshake is completed in server side, and start
+        // processing request.  If we don't exit handshake code here,
+        // server waits for EndOfEarlyData and Finished message from
+        // client, which voids the purpose of 0-RTT data.  The left
+        // over of handshake is done through write_tls or read_tls.
+        if (tlsconf.no_postpone_early_data &&
+            (tls.handshake_state == TLS_CONN_WRITE_STARTED ||
+             tls.wbuf.rleft()) &&
+            tls.earlybuf.rleft()) {
+          rv = 1;
+        }
+
+        break;
+      }
+
+      if (LOG_ENABLED(INFO)) {
+        LOG(INFO) << "tls: read early data " << nread << " bytes";
+      }
+
+      tls.earlybuf.append(buf.data(), nread);
+
+      if (rv == SSL_READ_EARLY_DATA_FINISH) {
+        if (LOG_ENABLED(INFO)) {
+          LOG(INFO) << "tls: read all early data; total "
+                    << tls.earlybuf.rleft() << " bytes";
+        }
+        tls.early_data_finish = true;
+        // The same reason stated above.
+        if (tlsconf.no_postpone_early_data &&
+            (tls.handshake_state == TLS_CONN_WRITE_STARTED ||
+             tls.wbuf.rleft()) &&
+            tls.earlybuf.rleft()) {
+          rv = 1;
+        } else {
+          ERR_clear_error();
+          rv = SSL_do_handshake(tls.ssl);
+        }
+        break;
+      }
+    }
+  }
+#else  // !OPENSSL_1_1_1_API
+  rv = SSL_do_handshake(tls.ssl);
+#endif // !OPENSSL_1_1_1_API
 
   if (rv <= 0) {
     auto err = SSL_get_error(tls.ssl, rv);
@@ -398,12 +461,21 @@ int Connection::tls_handshake() {
       break;
     case SSL_ERROR_WANT_WRITE:
       break;
-    case SSL_ERROR_SSL:
+    case SSL_ERROR_SSL: {
       if (LOG_ENABLED(INFO)) {
         LOG(INFO) << "tls: handshake libssl error: "
                   << ERR_error_string(ERR_get_error(), nullptr);
       }
+
+      struct iovec iov;
+      auto iovcnt = tls.wbuf.riovec(&iov, 1);
+      auto nwrite = writev_clear(&iov, iovcnt);
+      if (nwrite > 0) {
+        tls.wbuf.drain(nwrite);
+      }
+
       return SHRPX_ERR_NETWORK;
+    }
     default:
       if (LOG_ENABLED(INFO)) {
         LOG(INFO) << "tls: handshake libssl error " << err;
@@ -523,7 +595,9 @@ int Connection::check_http2_requirement() {
   const unsigned char *next_proto = nullptr;
   unsigned int next_proto_len;
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
   SSL_get0_next_proto_negotiated(tls.ssl, &next_proto, &next_proto_len);
+#endif // !OPENSSL_NO_NEXTPROTONEG
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
   if (next_proto == nullptr) {
     SSL_get0_alpn_selected(tls.ssl, &next_proto, &next_proto_len);
@@ -619,7 +693,21 @@ ssize_t Connection::write_tls(const void *data, size_t len) {
 
   ERR_clear_error();
 
+#if OPENSSL_1_1_1_API
+  int rv;
+  if (SSL_is_init_finished(tls.ssl)) {
+    rv = SSL_write(tls.ssl, data, len);
+  } else {
+    size_t nwrite;
+    rv = SSL_write_early_data(tls.ssl, data, len, &nwrite);
+    // Use the same semantics with SSL_write.
+    if (rv == 1) {
+      rv = nwrite;
+    }
+  }
+#else  // !OPENSSL_1_1_1_API
   auto rv = SSL_write(tls.ssl, data, len);
+#endif // !OPENSSL_1_1_1_API
 
   if (rv <= 0) {
     auto err = SSL_get_error(tls.ssl, rv);
@@ -654,6 +742,14 @@ ssize_t Connection::write_tls(const void *data, size_t len) {
 }
 
 ssize_t Connection::read_tls(void *data, size_t len) {
+  ERR_clear_error();
+
+#if OPENSSL_1_1_1_API
+  if (tls.earlybuf.rleft()) {
+    return tls.earlybuf.remove(data, len);
+  }
+#endif // OPENSSL_1_1_1_API
+
   // SSL_read requires the same arguments (buf pointer and its
   // length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
   // rlimit_.avail() or rlimit_.avail() may return different length
@@ -671,7 +767,46 @@ ssize_t Connection::read_tls(void *data, size_t len) {
     tls.last_readlen = 0;
   }
 
-  ERR_clear_error();
+#if OPENSSL_1_1_1_API
+  if (!tls.early_data_finish) {
+    // TLSv1.3 handshake is still going on.
+    size_t nread;
+    auto rv = SSL_read_early_data(tls.ssl, data, len, &nread);
+    if (rv == SSL_READ_EARLY_DATA_ERROR) {
+      auto err = SSL_get_error(tls.ssl, rv);
+      switch (err) {
+      case SSL_ERROR_WANT_READ:
+        tls.last_readlen = len;
+        return 0;
+      case SSL_ERROR_SSL:
+        if (LOG_ENABLED(INFO)) {
+          LOG(INFO) << "SSL_read: "
+                    << ERR_error_string(ERR_get_error(), nullptr);
+        }
+        return SHRPX_ERR_NETWORK;
+      default:
+        if (LOG_ENABLED(INFO)) {
+          LOG(INFO) << "SSL_read: SSL_get_error returned " << err;
+        }
+        return SHRPX_ERR_NETWORK;
+      }
+    }
+
+    if (LOG_ENABLED(INFO)) {
+      LOG(INFO) << "tls: read early data " << nread << " bytes";
+    }
+
+    if (rv == SSL_READ_EARLY_DATA_FINISH) {
+      if (LOG_ENABLED(INFO)) {
+        LOG(INFO) << "tls: read all early data";
+      }
+      tls.early_data_finish = true;
+      // We may have stopped write watcher in write_tls.
+      wlimit.startw();
+    }
+    return nread;
+  }
+#endif // OPENSSL_1_1_1_API
 
   auto rv = SSL_read(tls.ssl, data, len);
 
@@ -815,11 +950,11 @@ int Connection::get_tcp_hint(TCPHint *hint) const {
   // For TLSv1.3, AES-GCM and CHACHA20_POLY1305 overhead are now 22
   // bytes (5 (header) + 1 (ContentType) + 16 (tag)).
   size_t tls_overhead;
-#ifdef TLS1_3_VERSION
+#  ifdef TLS1_3_VERSION
   if (SSL_version(tls.ssl) == TLS1_3_VERSION) {
     tls_overhead = 22;
   } else
-#endif // TLS1_3_VERSION
+#  endif // TLS1_3_VERSION
   {
     tls_overhead = 29;
   }
index 441ff51..d9ca5c0 100644 (file)
@@ -56,6 +56,8 @@ enum {
 struct TLSConnection {
   DefaultMemchunks wbuf;
   DefaultPeekMemchunks rbuf;
+  // Stores TLSv1.3 early data.
+  DefaultMemchunks earlybuf;
   SSL *ssl;
   SSL_SESSION *cached_session;
   MemcachedRequest *cached_session_lookup_req;
@@ -74,6 +76,12 @@ struct TLSConnection {
   // true if ssl is initialized as server, and client requested
   // signed_certificate_timestamp extension.
   bool sct_requested;
+  // true if TLSv1.3 early data has been completely received.  Since
+  // SSL_read_early_data acts like SSL_do_handshake, this field may be
+  // true even if the negotiated TLS version is TLSv1.2 or earlier.
+  // This value is also true if this is client side connection for
+  // convenience.
+  bool early_data_finish;
 };
 
 struct TCPHint {
index 19985aa..05bbc3a 100644 (file)
@@ -25,7 +25,7 @@
 #include "shrpx_connection_handler.h"
 
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 #include <sys/types.h>
 #include <sys/wait.h>
@@ -115,6 +115,9 @@ ConnectionHandler::ConnectionHandler(struct ev_loop *loop, std::mt19937 &gen)
     : gen_(gen),
       single_worker_(nullptr),
       loop_(loop),
+#ifdef HAVE_NEVERBLEED
+      nb_(nullptr),
+#endif // HAVE_NEVERBLEED
       tls_ticket_key_memcached_get_retry_count_(0),
       tls_ticket_key_memcached_fail_count_(0),
       worker_round_robin_cnt_(get_config()->api.enabled ? 1 : 0),
@@ -205,12 +208,12 @@ int ConnectionHandler::create_single_worker() {
       all_ssl_ctx_, indexed_ssl_ctx_, cert_tree_.get()
 #ifdef HAVE_NEVERBLEED
                                           ,
-      nb_.get()
+      nb_
 #endif // HAVE_NEVERBLEED
   );
   auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context(
 #ifdef HAVE_NEVERBLEED
-      nb_.get()
+      nb_
 #endif // HAVE_NEVERBLEED
   );
 
@@ -227,7 +230,7 @@ int ConnectionHandler::create_single_worker() {
     if (memcachedconf.tls) {
       session_cache_ssl_ctx = tls::create_ssl_client_context(
 #ifdef HAVE_NEVERBLEED
-          nb_.get(),
+          nb_,
 #endif // HAVE_NEVERBLEED
           tlsconf.cacert, memcachedconf.cert_file,
           memcachedconf.private_key_file, nullptr);
@@ -254,15 +257,15 @@ int ConnectionHandler::create_worker_thread(size_t num) {
   cert_tree_ = tls::create_cert_lookup_tree();
   auto sv_ssl_ctx = tls::setup_server_ssl_context(
       all_ssl_ctx_, indexed_ssl_ctx_, cert_tree_.get()
-#ifdef HAVE_NEVERBLEED
+#  ifdef HAVE_NEVERBLEED
                                           ,
-      nb_.get()
-#endif // HAVE_NEVERBLEED
+      nb_
+#  endif // HAVE_NEVERBLEED
   );
   auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context(
-#ifdef HAVE_NEVERBLEED
-      nb_.get()
-#endif // HAVE_NEVERBLEED
+#  ifdef HAVE_NEVERBLEED
+      nb_
+#  endif // HAVE_NEVERBLEED
   );
 
   if (cl_ssl_ctx) {
@@ -284,9 +287,9 @@ int ConnectionHandler::create_worker_thread(size_t num) {
 
     if (memcachedconf.tls) {
       session_cache_ssl_ctx = tls::create_ssl_client_context(
-#ifdef HAVE_NEVERBLEED
-          nb_.get(),
-#endif // HAVE_NEVERBLEED
+#  ifdef HAVE_NEVERBLEED
+          nb_,
+#  endif // HAVE_NEVERBLEED
           tlsconf.cacert, memcachedconf.cert_file,
           memcachedconf.private_key_file, nullptr);
       all_ssl_ctx_.push_back(session_cache_ssl_ctx);
@@ -299,11 +302,11 @@ int ConnectionHandler::create_worker_thread(size_t num) {
     auto worker = make_unique<Worker>(
         loop, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree_.get(),
         ticket_keys_, this, config->conn.downstream);
-#ifdef HAVE_MRUBY
+#  ifdef HAVE_MRUBY
     if (worker->create_mruby_context() != 0) {
       return -1;
     }
-#endif // HAVE_MRUBY
+#  endif // HAVE_MRUBY
 
     workers_.push_back(std::move(worker));
     worker_loops_.push_back(loop);
@@ -610,8 +613,8 @@ void ConnectionHandler::handle_ocsp_complete() {
   auto status = WEXITSTATUS(rstatus);
   if (ocsp_.error || !WIFEXITED(rstatus) || status != 0) {
     LOG(WARN) << "ocsp query command for " << tls_ctx_data->cert_file
-              << " failed: error=" << ocsp_.error << ", rstatus=" << std::hex
-              << rstatus << std::dec << ", status=" << status;
+              << " failed: error=" << ocsp_.error << ", rstatus=" << log::hex
+              << rstatus << log::dec << ", status=" << status;
     ++ocsp_.next;
     proceed_next_cert_ocsp();
     return;
@@ -629,19 +632,19 @@ void ConnectionHandler::handle_ocsp_complete() {
       tls::verify_ocsp_response(ssl_ctx, ocsp_.resp.data(),
                                 ocsp_.resp.size()) == 0) {
 #ifndef OPENSSL_IS_BORINGSSL
-#ifdef HAVE_ATOMIC_STD_SHARED_PTR
+#  ifdef HAVE_ATOMIC_STD_SHARED_PTR
     std::atomic_store_explicit(
         &tls_ctx_data->ocsp_data,
         std::make_shared<std::vector<uint8_t>>(std::move(ocsp_.resp)),
         std::memory_order_release);
-#else  // !HAVE_ATOMIC_STD_SHARED_PTR
+#  else  // !HAVE_ATOMIC_STD_SHARED_PTR
     std::lock_guard<std::mutex> g(tls_ctx_data->mu);
     tls_ctx_data->ocsp_data =
         std::make_shared<std::vector<uint8_t>>(std::move(ocsp_.resp));
-#endif // !HAVE_ATOMIC_STD_SHARED_PTR
-#else  // OPENSSL_IS_BORINGSSL
+#  endif // !HAVE_ATOMIC_STD_SHARED_PTR
+#else    // OPENSSL_IS_BORINGSSL
     SSL_CTX_set_ocsp_response(ssl_ctx, ocsp_.resp.data(), ocsp_.resp.size());
-#endif // OPENSSL_IS_BORINGSSL
+#endif   // OPENSSL_IS_BORINGSSL
   }
 
   ++ocsp_.next;
@@ -802,7 +805,7 @@ SSL_CTX *ConnectionHandler::create_tls_ticket_key_memcached_ssl_ctx() {
 
   auto ssl_ctx = tls::create_ssl_client_context(
 #ifdef HAVE_NEVERBLEED
-      nb_.get(),
+      nb_,
 #endif // HAVE_NEVERBLEED
       tlsconf.cacert, memcachedconf.cert_file, memcachedconf.private_key_file,
       nullptr);
@@ -813,12 +816,7 @@ SSL_CTX *ConnectionHandler::create_tls_ticket_key_memcached_ssl_ctx() {
 }
 
 #ifdef HAVE_NEVERBLEED
-void ConnectionHandler::set_neverbleed(std::unique_ptr<neverbleed_t> nb) {
-  nb_ = std::move(nb);
-}
-
-neverbleed_t *ConnectionHandler::get_neverbleed() const { return nb_.get(); }
-
+void ConnectionHandler::set_neverbleed(neverbleed_t *nb) { nb_ = nb; }
 #endif // HAVE_NEVERBLEED
 
 void ConnectionHandler::handle_serial_event() {
index 4c5ce74..8a00244 100644 (file)
@@ -29,7 +29,7 @@
 
 #include <sys/types.h>
 #ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
+#  include <sys/socket.h>
 #endif // HAVE_SYS_SOCKET_H
 
 #include <mutex>
@@ -37,7 +37,7 @@
 #include <vector>
 #include <random>
 #ifndef NOTHREADS
-#include <future>
+#  include <future>
 #endif // NOTHREADS
 
 #include <openssl/ssl.h>
@@ -45,7 +45,7 @@
 #include <ev.h>
 
 #ifdef HAVE_NEVERBLEED
-#include <neverbleed.h>
+#  include <neverbleed.h>
 #endif // HAVE_NEVERBLEED
 
 #include "shrpx_downstream_connection_pool.h"
@@ -160,8 +160,7 @@ public:
   const std::vector<SSL_CTX *> &get_indexed_ssl_ctx(size_t idx) const;
 
 #ifdef HAVE_NEVERBLEED
-  void set_neverbleed(std::unique_ptr<neverbleed_t> nb);
-  neverbleed_t *get_neverbleed() const;
+  void set_neverbleed(neverbleed_t *nb);
 #endif // HAVE_NEVERBLEED
 
   // Send SerialEvent SEV_REPLACE_DOWNSTREAM to this object.
@@ -210,7 +209,7 @@ private:
   struct ev_loop *loop_;
   std::vector<std::unique_ptr<AcceptHandler>> acceptors_;
 #ifdef HAVE_NEVERBLEED
-  std::unique_ptr<neverbleed_t> nb_;
+  neverbleed_t *nb_;
 #endif // HAVE_NEVERBLEED
   ev_timer disable_acceptor_timer_;
   ev_timer ocsp_timer_;
index 360a9a9..2538aa9 100644 (file)
@@ -38,7 +38,7 @@
 #include "shrpx_http2_session.h"
 #include "shrpx_log.h"
 #ifdef HAVE_MRUBY
-#include "shrpx_mruby.h"
+#  include "shrpx_mruby.h"
 #endif // HAVE_MRUBY
 #include "util.h"
 #include "http2.h"
@@ -121,6 +121,7 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
       req_(balloc_),
       resp_(balloc_),
       request_start_time_(std::chrono::high_resolution_clock::now()),
+      blocked_request_buf_(mcpool),
       request_buf_(mcpool),
       response_buf_(mcpool),
       upstream_(upstream),
@@ -142,7 +143,8 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
       request_pending_(false),
       request_header_sent_(false),
       accesslog_written_(false),
-      new_affinity_cookie_(false) {
+      new_affinity_cookie_(false),
+      blocked_request_data_eof_(false) {
 
   auto &timeoutconf = get_config()->http2.timeout;
 
@@ -186,6 +188,16 @@ Downstream::~Downstream() {
 #endif // HAVE_MRUBY
   }
 
+#ifdef HAVE_MRUBY
+  if (dconn_) {
+    const auto &group = dconn_->get_downstream_addr_group();
+    if (group) {
+      const auto &mruby_ctx = group->mruby_ctx;
+      mruby_ctx->delete_downstream(this);
+    }
+  }
+#endif // HAVE_MRUBY
+
   // DownstreamConnection may refer to this object.  Delete it now
   // explicitly.
   dconn_.reset();
@@ -215,6 +227,14 @@ void Downstream::detach_downstream_connection() {
     return;
   }
 
+#ifdef HAVE_MRUBY
+  const auto &group = dconn_->get_downstream_addr_group();
+  if (group) {
+    const auto &mruby_ctx = group->mruby_ctx;
+    mruby_ctx->delete_downstream(this);
+  }
+#endif // HAVE_MRUBY
+
   dconn_->detach_downstream(this);
 
   auto handler = dconn_->get_client_handler();
@@ -228,6 +248,18 @@ DownstreamConnection *Downstream::get_downstream_connection() {
 }
 
 std::unique_ptr<DownstreamConnection> Downstream::pop_downstream_connection() {
+#ifdef HAVE_MRUBY
+  if (!dconn_) {
+    return nullptr;
+  }
+
+  const auto &group = dconn_->get_downstream_addr_group();
+  if (group) {
+    const auto &mruby_ctx = group->mruby_ctx;
+    mruby_ctx->delete_downstream(this);
+  }
+#endif // HAVE_MRUBY
+
   return std::unique_ptr<DownstreamConnection>(dconn_.release());
 }
 
@@ -584,7 +616,8 @@ bool Downstream::request_buf_full() {
 
   if (dconn_) {
     auto &downstreamconf = *worker->get_downstream_config();
-    return request_buf_.rleft() >= downstreamconf.request_buffer_size;
+    return blocked_request_buf_.rleft() + request_buf_.rleft() >=
+           downstreamconf.request_buffer_size;
   }
 
   return false;
@@ -605,6 +638,12 @@ int Downstream::push_request_headers() {
 int Downstream::push_upload_data_chunk(const uint8_t *data, size_t datalen) {
   req_.recv_body_length += datalen;
 
+  if (!request_header_sent_) {
+    blocked_request_buf_.append(data, datalen);
+    req_.unconsumed_body_length += datalen;
+    return 0;
+  }
+
   // Assumes that request headers have already been pushed to output
   // buffer using push_request_headers().
   if (!dconn_) {
@@ -621,6 +660,10 @@ int Downstream::push_upload_data_chunk(const uint8_t *data, size_t datalen) {
 }
 
 int Downstream::end_upload_data() {
+  if (!request_header_sent_) {
+    blocked_request_data_eof_ = true;
+    return 0;
+  }
   if (!dconn_) {
     DLOG(INFO, this) << "dconn_ is NULL";
     return -1;
@@ -720,9 +763,35 @@ bool Downstream::validate_response_recv_body_length() const {
   return true;
 }
 
-void Downstream::check_upgrade_fulfilled() {
+void Downstream::check_upgrade_fulfilled_http2() {
+  // This handles nonzero req_.connect_proto and h1 frontend requests
+  // WebSocket upgrade.
+  upgraded_ = (req_.method == HTTP_CONNECT ||
+               req_.connect_proto == CONNECT_PROTO_WEBSOCKET) &&
+              resp_.http_status / 100 == 2;
+}
+
+void Downstream::check_upgrade_fulfilled_http1() {
   if (req_.method == HTTP_CONNECT) {
-    upgraded_ = 200 <= resp_.http_status && resp_.http_status < 300;
+    if (req_.connect_proto == CONNECT_PROTO_WEBSOCKET) {
+      if (resp_.http_status != 101) {
+        return;
+      }
+
+      // This is done for HTTP/2 frontend only.
+      auto accept = resp_.fs.header(http2::HD_SEC_WEBSOCKET_ACCEPT);
+      if (!accept) {
+        return;
+      }
+
+      std::array<uint8_t, base64::encode_length(20)> accept_buf;
+      auto expected =
+          http2::make_websocket_accept_token(accept_buf.data(), ws_key_);
+
+      upgraded_ = expected != "" && expected == accept->value;
+    } else {
+      upgraded_ = resp_.http_status / 100 == 2;
+    }
 
     return;
   }
@@ -744,7 +813,7 @@ void Downstream::inspect_http2_request() {
 void Downstream::inspect_http1_request() {
   if (req_.method == HTTP_CONNECT) {
     req_.upgrade_request = true;
-  } else {
+  } else if (req_.http_minor > 0) {
     auto upgrade = req_.fs.header(http2::HD_UPGRADE);
     if (upgrade) {
       const auto &val = upgrade->value;
@@ -754,6 +823,12 @@ void Downstream::inspect_http1_request() {
         req_.http2_upgrade_seen = true;
       } else {
         req_.upgrade_request = true;
+
+        // TODO Should we check Sec-WebSocket-Key, and
+        // Sec-WebSocket-Version as well?
+        if (util::strieq_l("websocket", val)) {
+          req_.connect_proto = CONNECT_PROTO_WEBSOCKET;
+        }
       }
     }
   }
@@ -1052,4 +1127,14 @@ uint32_t Downstream::get_affinity_cookie_to_send() const {
   return 0;
 }
 
+DefaultMemchunks *Downstream::get_blocked_request_buf() {
+  return &blocked_request_buf_;
+}
+
+bool Downstream::get_blocked_request_data_eof() const {
+  return blocked_request_data_eof_;
+}
+
+void Downstream::set_ws_key(const StringRef &key) { ws_key_ = key; }
+
 } // namespace shrpx
index c81fcf6..4ea9df3 100644 (file)
@@ -134,6 +134,12 @@ private:
   bool trailer_key_prev_;
 };
 
+// Protocols allowed in HTTP/2 :protocol header field.
+enum shrpx_connect_proto {
+  CONNECT_PROTO_NONE,
+  CONNECT_PROTO_WEBSOCKET,
+};
+
 struct Request {
   Request(BlockAllocator &balloc)
       : fs(balloc, 16),
@@ -142,6 +148,7 @@ struct Request {
         method(-1),
         http_major(1),
         http_minor(1),
+        connect_proto(CONNECT_PROTO_NONE),
         upgrade_request(false),
         http2_upgrade_seen(false),
         connection_close(false),
@@ -153,6 +160,12 @@ struct Request {
     unconsumed_body_length -= len;
   }
 
+  bool regular_connect_method() const {
+    return method == HTTP_CONNECT && !connect_proto;
+  }
+
+  bool extended_connect_method() const { return connect_proto; }
+
   FieldStore fs;
   // Timestamp when all request header fields are received.
   std::shared_ptr<Timestamp> tstamp;
@@ -176,6 +189,10 @@ struct Request {
   int method;
   // HTTP major and minor version
   int http_major, http_minor;
+  // connect_proto specified in HTTP/2 :protocol pseudo header field
+  // which enables extended CONNECT method.  This field is also set if
+  // WebSocket upgrade is requested in h1 frontend for convenience.
+  int connect_proto;
   // Returns true if the request is HTTP upgrade (HTTP Upgrade or
   // CONNECT method).  Upgrade to HTTP/2 is excluded.  For HTTP/2
   // Upgrade, check get_http2_upgrade_request().
@@ -283,11 +300,14 @@ public:
   // Returns true if output buffer is full. If underlying dconn_ is
   // NULL, this function always returns false.
   bool request_buf_full();
-  // Returns true if upgrade (HTTP Upgrade or CONNECT) is succeeded.
-  // This should not depend on inspect_http1_response().
-  void check_upgrade_fulfilled();
+  // Returns true if upgrade (HTTP Upgrade or CONNECT) is succeeded in
+  // h1 backend.  This should not depend on inspect_http1_response().
+  void check_upgrade_fulfilled_http1();
+  // Returns true if upgrade (HTTP Upgrade or CONNECT) is succeeded in
+  // h2 backend.
+  void check_upgrade_fulfilled_http2();
   // Returns true if the upgrade is succeeded as a result of the call
-  // check_upgrade_fulfilled().  HTTP/2 Upgrade is excluded.
+  // check_upgrade_fulfilled_http*().  HTTP/2 Upgrade is excluded.
   bool get_upgraded() const;
   // Inspects HTTP/2 request.
   void inspect_http2_request();
@@ -356,6 +376,9 @@ public:
   // get_request_pending() returns false.
   bool request_submission_ready() const;
 
+  DefaultMemchunks *get_blocked_request_buf();
+  bool get_blocked_request_data_eof() const;
+
   // downstream response API
   const Response &response() const { return resp_; }
   Response &response() { return resp_; }
@@ -458,6 +481,8 @@ public:
   // field, returns 0.
   uint32_t get_affinity_cookie_to_send() const;
 
+  void set_ws_key(const StringRef &key);
+
   enum {
     EVENT_ERROR = 0x1,
     EVENT_TIMEOUT = 0x2,
@@ -491,9 +516,16 @@ private:
   // or not.
   StringRef request_downstream_host_;
 
+  // Data arrived in frontend before sending header fields to backend
+  // are stored in this buffer.
+  DefaultMemchunks blocked_request_buf_;
   DefaultMemchunks request_buf_;
   DefaultMemchunks response_buf_;
 
+  // The Sec-WebSocket-Key field sent to the peer.  This field is used
+  // if frontend uses RFC 8441 WebSocket bootstrapping via HTTP/2.
+  StringRef ws_key_;
+
   ev_timer upstream_rtimer_;
   ev_timer upstream_wtimer_;
 
@@ -547,6 +579,9 @@ private:
   bool accesslog_written_;
   // true if affinity cookie is generated for this request.
   bool new_affinity_cookie_;
+  // true if eof is received from client before sending header fields
+  // to backend.
+  bool blocked_request_data_eof_;
 };
 
 } // namespace shrpx
index d9153ee..ef06ea3 100644 (file)
@@ -26,7 +26,7 @@
 #define SHRPX_DOWNSTREAM_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif // HAVE_CONFIG_H
 
 namespace shrpx {
index ab590de..066a2af 100644 (file)
@@ -54,7 +54,14 @@ void HealthMonitorDownstreamConnection::detach_downstream(
   downstream_ = nullptr;
 }
 
-int HealthMonitorDownstreamConnection::push_request_headers() { return 0; }
+int HealthMonitorDownstreamConnection::push_request_headers() {
+  downstream_->set_request_header_sent(true);
+  auto src = downstream_->get_blocked_request_buf();
+  auto dest = downstream_->get_request_buf();
+  src->remove(*dest);
+
+  return 0;
+}
 
 int HealthMonitorDownstreamConnection::push_upload_data_chunk(
     const uint8_t *data, size_t datalen) {
index 70a4a05..1a4acd0 100644 (file)
@@ -25,7 +25,7 @@
 #include "shrpx_http2_downstream_connection.h"
 
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 
 #include "http-parser/http_parser.h"
@@ -41,6 +41,7 @@
 #include "shrpx_log.h"
 #include "http2.h"
 #include "util.h"
+#include "ssl_compat.h"
 
 using namespace nghttp2;
 
@@ -104,7 +105,7 @@ int Http2DownstreamConnection::attach_downstream(Downstream *downstream) {
   auto &req = downstream_->request();
 
   // HTTP/2 disables HTTP Upgrade.
-  if (req.method != HTTP_CONNECT) {
+  if (req.method != HTTP_CONNECT && !req.connect_proto) {
     req.upgrade_request = false;
   }
 
@@ -230,7 +231,7 @@ int Http2DownstreamConnection::push_request_headers() {
   if (!downstream_) {
     return 0;
   }
-  if (!http2session_->can_push_request()) {
+  if (!http2session_->can_push_request(downstream_)) {
     // The HTTP2 session to the backend has not been established or
     // connection is now being checked.  This function will be called
     // again just after it is established.
@@ -243,6 +244,10 @@ int Http2DownstreamConnection::push_request_headers() {
 
   const auto &req = downstream_->request();
 
+  if (req.connect_proto && !http2session_->get_allow_connect_proto()) {
+    return -1;
+  }
+
   auto &balloc = downstream_->get_block_allocator();
 
   auto config = get_config();
@@ -250,7 +255,7 @@ int Http2DownstreamConnection::push_request_headers() {
   auto &http2conf = config->http2;
 
   auto no_host_rewrite = httpconf.no_host_rewrite || config->http2_proxy ||
-                         req.method == HTTP_CONNECT;
+                         req.regular_connect_method();
 
   // http2session_ has already in CONNECTED state, so we can get
   // addr_idx here.
@@ -271,24 +276,31 @@ int Http2DownstreamConnection::push_request_headers() {
     num_cookies = downstream_->count_crumble_request_cookie();
   }
 
-  // 9 means:
+  // 11 means:
   // 1. :method
   // 2. :scheme
   // 3. :path
   // 4. :authority (or host)
-  // 5. via (optional)
-  // 6. x-forwarded-for (optional)
-  // 7. x-forwarded-proto (optional)
-  // 8. te (optional)
-  // 9. forwarded (optional)
+  // 5. :protocol (optional)
+  // 6. via (optional)
+  // 7. x-forwarded-for (optional)
+  // 8. x-forwarded-proto (optional)
+  // 9. te (optional)
+  // 10. forwarded (optional)
+  // 11. early-data (optional)
   auto nva = std::vector<nghttp2_nv>();
-  nva.reserve(req.fs.headers().size() + 9 + num_cookies +
+  nva.reserve(req.fs.headers().size() + 11 + num_cookies +
               httpconf.add_request_headers.size());
 
-  nva.push_back(
-      http2::make_nv_ls_nocopy(":method", http2::to_method_string(req.method)));
+  if (req.connect_proto == CONNECT_PROTO_WEBSOCKET) {
+    nva.push_back(http2::make_nv_ll(":method", "CONNECT"));
+    nva.push_back(http2::make_nv_ll(":protocol", "websocket"));
+  } else {
+    nva.push_back(http2::make_nv_ls_nocopy(
+        ":method", http2::to_method_string(req.method)));
+  }
 
-  if (req.method != HTTP_CONNECT) {
+  if (!req.regular_connect_method()) {
     assert(!req.scheme.empty());
 
     auto addr = http2session_->get_addr();
@@ -306,7 +318,7 @@ int Http2DownstreamConnection::push_request_headers() {
       nva.push_back(http2::make_nv_ls_nocopy(":path", req.path));
     }
 
-    if (!req.no_authority) {
+    if (!req.no_authority || req.connect_proto) {
       nva.push_back(http2::make_nv_ls_nocopy(":authority", authority));
     } else {
       nva.push_back(http2::make_nv_ls_nocopy("host", authority));
@@ -318,11 +330,14 @@ int Http2DownstreamConnection::push_request_headers() {
   auto &fwdconf = httpconf.forwarded;
   auto &xffconf = httpconf.xff;
   auto &xfpconf = httpconf.xfp;
+  auto &earlydataconf = httpconf.early_data;
 
   uint32_t build_flags =
       (fwdconf.strip_incoming ? http2::HDOP_STRIP_FORWARDED : 0) |
       (xffconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_FOR : 0) |
-      (xfpconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_PROTO : 0);
+      (xfpconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_PROTO : 0) |
+      (earlydataconf.strip_incoming ? http2::HDOP_STRIP_EARLY_DATA : 0) |
+      http2::HDOP_STRIP_SEC_WEBSOCKET_KEY;
 
   http2::copy_headers_to_nva_nocopy(nva, req.fs.headers(), build_flags);
 
@@ -333,13 +348,21 @@ int Http2DownstreamConnection::push_request_headers() {
   auto upstream = downstream_->get_upstream();
   auto handler = upstream->get_client_handler();
 
+#if OPENSSL_1_1_1_API
+  auto conn = handler->get_connection();
+
+  if (conn->tls.ssl && !SSL_is_init_finished(conn->tls.ssl)) {
+    nva.push_back(http2::make_nv_ll("early-data", "1"));
+  }
+#endif // OPENSSL_1_1_1_API
+
   auto fwd =
       fwdconf.strip_incoming ? nullptr : req.fs.header(http2::HD_FORWARDED);
 
   if (fwdconf.params) {
     auto params = fwdconf.params;
 
-    if (config->http2_proxy || req.method == HTTP_CONNECT) {
+    if (config->http2_proxy || req.regular_connect_method()) {
       params &= ~FORWARDED_PROTO;
     }
 
@@ -380,7 +403,7 @@ int Http2DownstreamConnection::push_request_headers() {
     nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-for", xff->value));
   }
 
-  if (!config->http2_proxy && req.method != HTTP_CONNECT) {
+  if (!config->http2_proxy && !req.regular_connect_method()) {
     auto xfp = xfpconf.strip_incoming
                    ? nullptr
                    : req.fs.header(http2::HD_X_FORWARDED_PROTO);
@@ -452,7 +475,7 @@ int Http2DownstreamConnection::push_request_headers() {
 
   // Add body as long as transfer-encoding is given even if
   // req.fs.content_length == 0 to forward trailer fields.
-  if (req.method == HTTP_CONNECT || transfer_encoding ||
+  if (req.method == HTTP_CONNECT || req.connect_proto || transfer_encoding ||
       req.fs.content_length > 0 || req.http2_expect_body) {
     // Request-body is expected.
     data_prd = {{}, http2_data_read_callback};
index 61c97ca..5342a14 100644 (file)
@@ -26,7 +26,7 @@
 
 #include <netinet/tcp.h>
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 
 #include <vector>
@@ -186,9 +186,9 @@ Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
     : dlnext(nullptr),
       dlprev(nullptr),
       conn_(loop, -1, nullptr, worker->get_mcpool(),
-            worker->get_downstream_config()->timeout.write,
-            worker->get_downstream_config()->timeout.read, {}, {}, writecb,
-            readcb, timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold,
+            group->shared_addr->timeout.write, group->shared_addr->timeout.read,
+            {}, {}, writecb, readcb, timeoutcb, this,
+            get_config()->tls.dyn_rec.warmup_threshold,
             get_config()->tls.dyn_rec.idle_timeout, PROTO_HTTP2),
       wb_(worker->get_mcpool()),
       worker_(worker),
@@ -199,7 +199,9 @@ Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
       raddr_(nullptr),
       state_(DISCONNECTED),
       connection_check_state_(CONNECTION_CHECK_NONE),
-      freelist_zone_(FREELIST_ZONE_NONE) {
+      freelist_zone_(FREELIST_ZONE_NONE),
+      settings_recved_(false),
+      allow_connect_proto_(false) {
   read_ = write_ = &Http2Session::noop;
 
   on_read_ = &Http2Session::read_noop;
@@ -618,7 +620,7 @@ int htp_hdrs_completecb(http_parser *htp) {
   http_parser_pause(htp, 1);
 
   // We just check status code here
-  if (htp->status_code == 200) {
+  if (htp->status_code / 100 == 2) {
     if (LOG_ENABLED(INFO)) {
       SSLOG(INFO, http2session) << "Tunneling success";
     }
@@ -1141,7 +1143,7 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
   }
 
   downstream->set_response_state(Downstream::HEADER_COMPLETE);
-  downstream->check_upgrade_fulfilled();
+  downstream->check_upgrade_fulfilled_http2();
 
   if (downstream->get_upgraded()) {
     resp.connection_close = true;
@@ -1308,6 +1310,7 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
   }
   case NGHTTP2_SETTINGS: {
     if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
+      http2session->on_settings_received(frame);
       return 0;
     }
 
@@ -1468,6 +1471,15 @@ int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
     if (frame->hd.type == NGHTTP2_HEADERS &&
         frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
       downstream->set_request_header_sent(true);
+      auto src = downstream->get_blocked_request_buf();
+      if (src->rleft()) {
+        auto dest = downstream->get_request_buf();
+        src->remove(*dest);
+        if (http2session->resume_data(sd->dconn) != 0) {
+          return NGHTTP2_ERR_CALLBACK_FAILURE;
+        }
+        downstream->ensure_downstream_wtimer();
+      }
     }
 
     if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
@@ -1649,7 +1661,9 @@ int Http2Session::connection_made() {
     const unsigned char *next_proto = nullptr;
     unsigned int next_proto_len = 0;
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
     SSL_get0_next_proto_negotiated(conn_.tls.ssl, &next_proto, &next_proto_len);
+#endif // !OPENSSL_NO_NEXTPROTONEG
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
     if (!next_proto) {
       SSL_get0_alpn_selected(conn_.tls.ssl, &next_proto, &next_proto_len);
@@ -1758,7 +1772,6 @@ int Http2Session::downstream_write() {
   for (;;) {
     const uint8_t *data;
     auto datalen = nghttp2_session_mem_send(session_, &data);
-
     if (datalen < 0) {
       SSLOG(ERROR, this) << "nghttp2_session_mem_send() returned error: "
                          << nghttp2_strerror(datalen);
@@ -1845,9 +1858,11 @@ int Http2Session::consume(int32_t stream_id, size_t len) {
   return 0;
 }
 
-bool Http2Session::can_push_request() const {
+bool Http2Session::can_push_request(const Downstream *downstream) const {
+  auto &req = downstream->request();
   return state_ == CONNECTED &&
-         connection_check_state_ == CONNECTION_CHECK_NONE;
+         connection_check_state_ == CONNECTION_CHECK_NONE &&
+         (!req.connect_proto || settings_recved_);
 }
 
 void Http2Session::start_checking_connection() {
@@ -1906,6 +1921,11 @@ void Http2Session::submit_pending_requests() {
       continue;
     }
 
+    auto &req = downstream->request();
+    if (req.connect_proto && !settings_recved_) {
+      continue;
+    }
+
     auto upstream = downstream->get_upstream();
 
     if (dconn->push_request_headers() != 0) {
@@ -1952,10 +1972,8 @@ int Http2Session::connected() {
     SSLOG(INFO, this) << "Connection established";
   }
 
-  auto &downstreamconf = *get_config()->conn.downstream;
-
   // Reset timeout for write.  Previously, we set timeout for connect.
-  conn_.wt.repeat = downstreamconf.timeout.write;
+  conn_.wt.repeat = group_->shared_addr->timeout.write;
   ev_timer_again(conn_.loop, &conn_.wt);
 
   conn_.rlimit.startw();
@@ -2394,4 +2412,28 @@ void Http2Session::check_retire() {
 
 const Address *Http2Session::get_raddr() const { return raddr_; }
 
+void Http2Session::on_settings_received(const nghttp2_frame *frame) {
+  // TODO This effectively disallows nghttpx to change its behaviour
+  // based on the 2nd SETTINGS.
+  if (settings_recved_) {
+    return;
+  }
+
+  settings_recved_ = true;
+
+  for (size_t i = 0; i < frame->settings.niv; ++i) {
+    auto &ent = frame->settings.iv[i];
+    if (ent.settings_id == NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL) {
+      allow_connect_proto_ = true;
+      break;
+    }
+  }
+
+  submit_pending_requests();
+}
+
+bool Http2Session::get_allow_connect_proto() const {
+  return allow_connect_proto_;
+}
+
 } // namespace shrpx
index ef2c0af..09b865b 100644 (file)
@@ -146,7 +146,7 @@ public:
   int consume(int32_t stream_id, size_t len);
 
   // Returns true if request can be issued on downstream connection.
-  bool can_push_request() const;
+  bool can_push_request(const Downstream *downstream) const;
   // Initiates the connection checking if downstream connection has
   // been established and connection checking is required.
   void start_checking_connection();
@@ -212,6 +212,12 @@ public:
   // Returns address used to connect to backend.  Could be nullptr.
   const Address *get_raddr() const;
 
+  // This is called when SETTINGS frame without ACK flag set is
+  // received.
+  void on_settings_received(const nghttp2_frame *frame);
+
+  bool get_allow_connect_proto() const;
+
   enum {
     // Disconnected
     DISCONNECTED,
@@ -280,6 +286,10 @@ private:
   int state_;
   int connection_check_state_;
   int freelist_zone_;
+  // true if SETTINGS without ACK is received from peer.
+  bool settings_recved_;
+  // true if peer enables RFC 8441 CONNECT protocol.
+  bool allow_connect_proto_;
 };
 
 nghttp2_session_callbacks *create_http2_downstream_callbacks();
index e67ea6c..ac0b943 100644 (file)
@@ -39,7 +39,7 @@
 #include "shrpx_http2_session.h"
 #include "shrpx_log.h"
 #ifdef HAVE_MRUBY
-#include "shrpx_mruby.h"
+#  include "shrpx_mruby.h"
 #endif // HAVE_MRUBY
 #include "http2.h"
 #include "util.h"
@@ -391,6 +391,17 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
     }
   }
 
+  auto connect_proto = req.fs.header(http2::HD__PROTOCOL);
+  if (connect_proto) {
+    if (connect_proto->value != "websocket") {
+      if (error_reply(downstream, 400) != 0) {
+        return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
+      }
+      return 0;
+    }
+    req.connect_proto = CONNECT_PROTO_WEBSOCKET;
+  }
+
   if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
     req.http2_expect_body = true;
   } else if (req.fs.content_length == -1) {
@@ -461,6 +472,9 @@ void Http2Upstream::initiate_downstream(Downstream *downstream) {
     return;
   }
 
+#ifdef HAVE_MRUBY
+  auto dconn_ptr = dconn.get();
+#endif // HAVE_MRUBY
   rv = downstream->attach_downstream_connection(std::move(dconn));
   if (rv != 0) {
     // downstream connection fails, send error page
@@ -474,6 +488,27 @@ void Http2Upstream::initiate_downstream(Downstream *downstream) {
 
     return;
   }
+
+#ifdef HAVE_MRUBY
+  const auto &group = dconn_ptr->get_downstream_addr_group();
+  if (group) {
+    const auto &mruby_ctx = group->mruby_ctx;
+    if (mruby_ctx->run_on_request_proc(downstream) != 0) {
+      if (error_reply(downstream, 500) != 0) {
+        rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
+      }
+
+      downstream_queue_.mark_failure(downstream);
+
+      return;
+    }
+
+    if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+      return;
+    }
+  }
+#endif // HAVE_MRUBY
+
   rv = downstream->push_request_headers();
   if (rv != 0) {
 
@@ -590,7 +625,7 @@ int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
   auto downstream = static_cast<Downstream *>(
       nghttp2_session_get_stream_user_data(session, stream_id));
 
-  if (!downstream || !downstream->get_downstream_connection()) {
+  if (!downstream) {
     if (upstream->consume(stream_id, len) != 0) {
       return NGHTTP2_ERR_CALLBACK_FAILURE;
     }
@@ -1001,7 +1036,7 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
   flow_control_ = true;
 
   // TODO Maybe call from outside?
-  std::array<nghttp2_settings_entry, 3> entry;
+  std::array<nghttp2_settings_entry, 4> entry;
   size_t nentry = 2;
 
   entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
@@ -1014,6 +1049,12 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
     entry[1].value = http2conf.upstream.window_size;
   }
 
+  if (!config->http2_proxy) {
+    entry[nentry].settings_id = NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL;
+    entry[nentry].value = 1;
+    ++nentry;
+  }
+
   if (http2conf.upstream.decoder_dynamic_table_size !=
       NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) {
     entry[nentry].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
@@ -1611,6 +1652,24 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
 
 #ifdef HAVE_MRUBY
   if (!downstream->get_non_final_response()) {
+    auto dconn = downstream->get_downstream_connection();
+    const auto &group = dconn->get_downstream_addr_group();
+    if (group) {
+      const auto &dmruby_ctx = group->mruby_ctx;
+
+      if (dmruby_ctx->run_on_response_proc(downstream) != 0) {
+        if (error_reply(downstream, 500) != 0) {
+          return -1;
+        }
+        // Returning -1 will signal deletion of dconn.
+        return -1;
+      }
+
+      if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+        return -1;
+      }
+    }
+
     auto worker = handler_->get_worker();
     auto mruby_ctx = worker->get_mruby_context();
 
@@ -1662,11 +1721,11 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
   nva.reserve(resp.fs.headers().size() + 5 +
               httpconf.add_response_headers.size());
 
-  auto response_status = http2::stringify_status(balloc, resp.http_status);
+  if (downstream->get_non_final_response()) {
+    auto response_status = http2::stringify_status(balloc, resp.http_status);
 
-  nva.push_back(http2::make_nv_ls_nocopy(":status", response_status));
+    nva.push_back(http2::make_nv_ls_nocopy(":status", response_status));
 
-  if (downstream->get_non_final_response()) {
     http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers(),
                                       http2::HDOP_STRIP_ALL);
 
@@ -1688,8 +1747,19 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
     return 0;
   }
 
-  http2::copy_headers_to_nva_nocopy(
-      nva, resp.fs.headers(), http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA);
+  auto striphd_flags = http2::HDOP_STRIP_ALL & ~http2::HDOP_STRIP_VIA;
+  StringRef response_status;
+
+  if (req.connect_proto == CONNECT_PROTO_WEBSOCKET && resp.http_status == 101) {
+    response_status = http2::stringify_status(balloc, 200);
+    striphd_flags |= http2::HDOP_STRIP_SEC_WEBSOCKET_ACCEPT;
+  } else {
+    response_status = http2::stringify_status(balloc, resp.http_status);
+  }
+
+  nva.push_back(http2::make_nv_ls_nocopy(":status", response_status));
+
+  http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers(), striphd_flags);
 
   if (!config->http2_proxy && !httpconf.no_server_rewrite) {
     nva.push_back(http2::make_nv_ls_nocopy("server", httpconf.server_name));
@@ -1700,7 +1770,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
     }
   }
 
-  if (req.method != HTTP_CONNECT || !downstream->get_upgraded()) {
+  if (!req.regular_connect_method() || !downstream->get_upgraded()) {
     auto affinity_cookie = downstream->get_affinity_cookie_to_send();
     if (affinity_cookie) {
       auto dconn = downstream->get_downstream_connection();
@@ -1874,7 +1944,7 @@ int Http2Upstream::on_downstream_abort_request_with_https_redirect(
 
 int Http2Upstream::redirect_to_https(Downstream *downstream) {
   auto &req = downstream->request();
-  if (req.method == HTTP_CONNECT || req.scheme != "http") {
+  if (req.regular_connect_method() || req.scheme != "http") {
     return error_reply(downstream, 400);
   }
 
index f50c0f4..31f48a2 100644 (file)
@@ -39,6 +39,7 @@
 #include "shrpx_log.h"
 #include "http2.h"
 #include "util.h"
+#include "ssl_compat.h"
 
 using namespace nghttp2;
 
@@ -87,7 +88,11 @@ void retry_downstream_connection(Downstream *downstream,
   downstream->pop_downstream_connection();
 
   int rv;
-  auto ndconn = handler->get_downstream_connection(rv, downstream);
+  // We have to use h1 backend for retry if we have already written h1
+  // request in request buffer.
+  auto ndconn = handler->get_downstream_connection(
+      rv, downstream,
+      downstream->get_request_header_sent() ? PROTO_HTTP1 : PROTO_NONE);
   if (ndconn) {
     if (downstream->attach_downstream_connection(std::move(ndconn)) == 0 &&
         downstream->push_request_headers() == 0) {
@@ -185,9 +190,8 @@ HttpDownstreamConnection::HttpDownstreamConnection(
     const std::shared_ptr<DownstreamAddrGroup> &group, size_t initial_addr_idx,
     struct ev_loop *loop, Worker *worker)
     : conn_(loop, -1, nullptr, worker->get_mcpool(),
-            worker->get_downstream_config()->timeout.write,
-            worker->get_downstream_config()->timeout.read, {}, {}, connectcb,
-            readcb, connect_timeoutcb, this,
+            group->shared_addr->timeout.write, group->shared_addr->timeout.read,
+            {}, {}, connectcb, readcb, connect_timeoutcb, this,
             get_config()->tls.dyn_rec.warmup_threshold,
             get_config()->tls.dyn_rec.idle_timeout, PROTO_HTTP1),
       on_read_(&HttpDownstreamConnection::noop),
@@ -454,11 +458,11 @@ int HttpDownstreamConnection::initiate_connection() {
   } else {
     // we may set read timer cb to idle_timeoutcb.  Reset again.
     ev_set_cb(&conn_.rt, timeoutcb);
-    if (conn_.read_timeout < downstreamconf.timeout.read) {
-      conn_.read_timeout = downstreamconf.timeout.read;
+    if (conn_.read_timeout < group_->shared_addr->timeout.read) {
+      conn_.read_timeout = group_->shared_addr->timeout.read;
       conn_.last_read = ev_now(conn_.loop);
     } else {
-      conn_.again_rt(downstreamconf.timeout.read);
+      conn_.again_rt(group_->shared_addr->timeout.read);
     }
 
     ev_set_cb(&conn_.rev, readcb);
@@ -484,7 +488,7 @@ int HttpDownstreamConnection::push_request_headers() {
 
   auto &balloc = downstream_->get_block_allocator();
 
-  auto connect_method = req.method == HTTP_CONNECT;
+  auto connect_method = req.regular_connect_method();
 
   auto config = get_config();
   auto &httpconf = config->http;
@@ -508,7 +512,8 @@ int HttpDownstreamConnection::push_request_headers() {
   auto buf = downstream_->get_request_buf();
 
   // Assume that method and request path do not contain \r\n.
-  auto meth = http2::to_method_string(req.method);
+  auto meth = http2::to_method_string(
+      req.connect_proto == CONNECT_PROTO_WEBSOCKET ? HTTP_GET : req.method);
   buf->append(meth);
   buf->append(' ');
 
@@ -535,11 +540,14 @@ int HttpDownstreamConnection::push_request_headers() {
   auto &fwdconf = httpconf.forwarded;
   auto &xffconf = httpconf.xff;
   auto &xfpconf = httpconf.xfp;
+  auto &earlydataconf = httpconf.early_data;
 
   uint32_t build_flags =
       (fwdconf.strip_incoming ? http2::HDOP_STRIP_FORWARDED : 0) |
       (xffconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_FOR : 0) |
-      (xfpconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_PROTO : 0);
+      (xfpconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_PROTO : 0) |
+      (earlydataconf.strip_incoming ? http2::HDOP_STRIP_EARLY_DATA : 0) |
+      (req.http_major == 2 ? http2::HDOP_STRIP_SEC_WEBSOCKET_KEY : 0);
 
   http2::build_http1_headers_from_headers(buf, req.fs.headers(), build_flags);
 
@@ -552,16 +560,30 @@ int HttpDownstreamConnection::push_request_headers() {
 
   // set transfer-encoding only when content-length is unknown and
   // request body is expected.
-  if (!connect_method && req.http2_expect_body && req.fs.content_length == -1) {
+  if (req.method != HTTP_CONNECT && req.http2_expect_body &&
+      req.fs.content_length == -1) {
     downstream_->set_chunked_request(true);
     buf->append("Transfer-Encoding: chunked\r\n");
   }
 
-  if (req.connection_close) {
-    buf->append("Connection: close\r\n");
-  }
+  if (req.connect_proto == CONNECT_PROTO_WEBSOCKET) {
+    if (req.http_major == 2) {
+      std::array<uint8_t, 16> nonce;
+      util::random_bytes(std::begin(nonce), std::end(nonce),
+                         worker_->get_randgen());
+      auto iov = make_byte_ref(balloc, base64::encode_length(nonce.size()) + 1);
+      auto p = base64::encode(std::begin(nonce), std::end(nonce), iov.base);
+      *p = '\0';
+      auto key = StringRef{iov.base, p};
+      downstream_->set_ws_key(key);
+
+      buf->append("Sec-Websocket-Key: ");
+      buf->append(key);
+      buf->append("\r\n");
+    }
 
-  if (!connect_method && req.upgrade_request) {
+    buf->append("Upgrade: websocket\r\nConnection: Upgrade\r\n");
+  } else if (!connect_method && req.upgrade_request) {
     auto connection = req.fs.header(http2::HD_CONNECTION);
     if (connection) {
       buf->append("Connection: ");
@@ -575,11 +597,21 @@ int HttpDownstreamConnection::push_request_headers() {
       buf->append((*upgrade).value);
       buf->append("\r\n");
     }
+  } else if (req.connection_close) {
+    buf->append("Connection: close\r\n");
   }
 
   auto upstream = downstream_->get_upstream();
   auto handler = upstream->get_client_handler();
 
+#if OPENSSL_1_1_1_API
+  auto conn = handler->get_connection();
+
+  if (conn->tls.ssl && !SSL_is_init_finished(conn->tls.ssl)) {
+    buf->append("Early-Data: 1\r\n");
+  }
+#endif // OPENSSL_1_1_1_API
+
   auto fwd =
       fwdconf.strip_incoming ? nullptr : req.fs.header(http2::HD_FORWARDED);
 
@@ -693,11 +725,38 @@ int HttpDownstreamConnection::push_request_headers() {
   // Don't call signal_write() if we anticipate request body.  We call
   // signal_write() when we received request body chunk, and it
   // enables us to send headers and data in one writev system call.
-  if (connect_method ||
+  if (req.method == HTTP_CONNECT ||
+      downstream_->get_blocked_request_buf()->rleft() ||
       (!req.http2_expect_body && req.fs.content_length == 0)) {
     signal_write();
   }
 
+  return process_blocked_request_buf();
+}
+
+int HttpDownstreamConnection::process_blocked_request_buf() {
+  auto src = downstream_->get_blocked_request_buf();
+
+  if (src->rleft()) {
+    auto dest = downstream_->get_request_buf();
+    auto chunked = downstream_->get_chunked_request();
+    if (chunked) {
+      auto chunk_size_hex = util::utox(src->rleft());
+      dest->append(chunk_size_hex);
+      dest->append("\r\n");
+    }
+
+    src->remove(*dest);
+
+    if (chunked) {
+      dest->append("\r\n");
+    }
+  }
+
+  if (downstream_->get_blocked_request_data_eof()) {
+    return end_upload_data();
+  }
+
   return 0;
 }
 
@@ -879,7 +938,7 @@ int htp_hdrs_completecb(http_parser *htp) {
 
   // Server MUST NOT send Transfer-Encoding with a status code 1xx or
   // 204.  Also server MUST NOT send Transfer-Encoding with a status
-  // code 200 to a CONNECT request.  Same holds true with
+  // code 2xx to a CONNECT request.  Same holds true with
   // Content-Length.
   if (resp.http_status == 204) {
     if (resp.fs.header(http2::HD_TRANSFER_ENCODING)) {
@@ -901,7 +960,7 @@ int htp_hdrs_completecb(http_parser *htp) {
       return -1;
     }
   } else if (resp.http_status / 100 == 1 ||
-             (resp.http_status == 200 && req.method == HTTP_CONNECT)) {
+             (resp.http_status / 100 == 2 && req.method == HTTP_CONNECT)) {
     if (resp.fs.header(http2::HD_CONTENT_LENGTH) ||
         resp.fs.header(http2::HD_TRANSFER_ENCODING)) {
       return -1;
@@ -913,7 +972,7 @@ int htp_hdrs_completecb(http_parser *htp) {
 
   // Check upgrade before processing non-final response, since if
   // upgrade succeeded, 101 response is treated as final in nghttpx.
-  downstream->check_upgrade_fulfilled();
+  downstream->check_upgrade_fulfilled_http1();
 
   if (downstream->get_non_final_response()) {
     // Reset content-length because we reuse same Downstream for the
@@ -1429,10 +1488,8 @@ int HttpDownstreamConnection::connected() {
     DCLOG(INFO, this) << "Connected to downstream host";
   }
 
-  auto &downstreamconf = *get_config()->conn.downstream;
-
   // Reset timeout for write.  Previously, we set timeout for connect.
-  conn_.wt.repeat = downstreamconf.timeout.write;
+  conn_.wt.repeat = group_->shared_addr->timeout.write;
   ev_timer_again(conn_.loop, &conn_.wt);
 
   conn_.rlimit.startw();
index baea026..554e9b9 100644 (file)
@@ -89,6 +89,8 @@ public:
 
   int noop();
 
+  int process_blocked_request_buf();
+
 private:
   Connection conn_;
   std::function<int(HttpDownstreamConnection &)> on_read_, on_write_,
index 0fdcc93..ba3fad9 100644 (file)
@@ -25,7 +25,7 @@
 #include "shrpx_http_test.h"
 
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 
 #include <cstdlib>
index 85d2947..8bd8395 100644 (file)
@@ -26,7 +26,7 @@
 #define SHRPX_HTTP_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif // HAVE_CONFIG_H
 
 namespace shrpx {
index 452ec90..b6953a7 100644 (file)
 #include "shrpx_http2_session.h"
 #include "shrpx_log.h"
 #ifdef HAVE_MRUBY
-#include "shrpx_mruby.h"
+#  include "shrpx_mruby.h"
 #endif // HAVE_MRUBY
 #include "http2.h"
 #include "util.h"
 #include "template.h"
+#include "base64.h"
 
 using namespace nghttp2;
 
@@ -431,12 +432,31 @@ int htp_hdrs_completecb(http_parser *htp) {
     return -1;
   }
 
+#ifdef HAVE_MRUBY
+  auto dconn_ptr = dconn.get();
+#endif // HAVE_MRUBY
   if (downstream->attach_downstream_connection(std::move(dconn)) != 0) {
     downstream->set_request_state(Downstream::CONNECT_FAIL);
 
     return -1;
   }
 
+#ifdef HAVE_MRUBY
+  const auto &group = dconn_ptr->get_downstream_addr_group();
+  if (group) {
+    const auto &dmruby_ctx = group->mruby_ctx;
+
+    if (dmruby_ctx->run_on_request_proc(downstream) != 0) {
+      resp.http_status = 500;
+      return -1;
+    }
+
+    if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+      return 0;
+    }
+  }
+#endif // HAVE_MRUBY
+
   rv = downstream->push_request_headers();
 
   if (rv != 0) {
@@ -1021,6 +1041,8 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
   const auto &req = downstream->request();
   auto &resp = downstream->response();
   auto &balloc = downstream->get_block_allocator();
+  auto dconn = downstream->get_downstream_connection();
+  assert(dconn);
 
   if (downstream->get_non_final_response() &&
       !downstream->supports_non_final_response()) {
@@ -1030,6 +1052,20 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
 
 #ifdef HAVE_MRUBY
   if (!downstream->get_non_final_response()) {
+    const auto &group = dconn->get_downstream_addr_group();
+    if (group) {
+      const auto &dmruby_ctx = group->mruby_ctx;
+
+      if (dmruby_ctx->run_on_response_proc(downstream) != 0) {
+        error_reply(500);
+        return -1;
+      }
+
+      if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
+        return -1;
+      }
+    }
+
     auto worker = handler_->get_worker();
     auto mruby_ctx = worker->get_mruby_context();
 
@@ -1052,9 +1088,15 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
   buf->append('.');
   buf->append('0' + req.http_minor);
   buf->append(' ');
-  buf->append(http2::stringify_status(balloc, resp.http_status));
-  buf->append(' ');
-  buf->append(http2::get_reason_phrase(resp.http_status));
+  if (req.connect_proto && downstream->get_upgraded()) {
+    buf->append(http2::stringify_status(balloc, 101));
+    buf->append(' ');
+    buf->append(http2::get_reason_phrase(101));
+  } else {
+    buf->append(http2::stringify_status(balloc, resp.http_status));
+    buf->append(' ');
+    buf->append(http2::get_reason_phrase(resp.http_status));
+  }
   buf->append("\r\n");
 
   auto config = get_config();
@@ -1104,18 +1146,35 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
   }
 
   if (!connect_method && downstream->get_upgraded()) {
-    auto connection = resp.fs.header(http2::HD_CONNECTION);
-    if (connection) {
-      buf->append("Connection: ");
-      buf->append((*connection).value);
+    if (req.connect_proto == CONNECT_PROTO_WEBSOCKET &&
+        resp.http_status / 100 == 2) {
+      buf->append("Upgrade: websocket\r\nConnection: Upgrade\r\n");
+      auto key = req.fs.header(http2::HD_SEC_WEBSOCKET_KEY);
+      if (!key || key->value.size() != base64::encode_length(16)) {
+        return -1;
+      }
+      std::array<uint8_t, base64::encode_length(20)> out;
+      auto accept = http2::make_websocket_accept_token(out.data(), key->value);
+      if (accept.empty()) {
+        return -1;
+      }
+      buf->append("Sec-WebSocket-Accept: ");
+      buf->append(accept);
       buf->append("\r\n");
-    }
+    } else {
+      auto connection = resp.fs.header(http2::HD_CONNECTION);
+      if (connection) {
+        buf->append("Connection: ");
+        buf->append((*connection).value);
+        buf->append("\r\n");
+      }
 
-    auto upgrade = resp.fs.header(http2::HD_UPGRADE);
-    if (upgrade) {
-      buf->append("Upgrade: ");
-      buf->append((*upgrade).value);
-      buf->append("\r\n");
+      auto upgrade = resp.fs.header(http2::HD_UPGRADE);
+      if (upgrade) {
+        buf->append("Upgrade: ");
+        buf->append((*upgrade).value);
+        buf->append("\r\n");
+      }
     }
   }
 
@@ -1150,8 +1209,6 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
   if (req.method != HTTP_CONNECT || !downstream->get_upgraded()) {
     auto affinity_cookie = downstream->get_affinity_cookie_to_send();
     if (affinity_cookie) {
-      auto dconn = downstream->get_downstream_connection();
-      assert(dconn);
       auto &group = dconn->get_downstream_addr_group();
       auto &shared_addr = group->shared_addr;
       auto &cookieconf = shared_addr->affinity.cookie;
index 863ffa5..9d30ca7 100644 (file)
@@ -406,7 +406,9 @@ int LiveCheck::tls_handshake() {
   const unsigned char *next_proto = nullptr;
   unsigned int next_proto_len = 0;
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
   SSL_get0_next_proto_negotiated(conn_.tls.ssl, &next_proto, &next_proto_len);
+#endif // !OPENSSL_NO_NEXTPROTONEG
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
   if (next_proto == nullptr) {
     SSL_get0_alpn_selected(conn_.tls.ssl, &next_proto, &next_proto_len);
index d43b6fa..8459d15 100644 (file)
 #include "shrpx_log.h"
 
 #ifdef HAVE_SYSLOG_H
-#include <syslog.h>
+#  include <syslog.h>
 #endif // HAVE_SYSLOG_H
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 #ifdef HAVE_INTTYPES_H
-#include <inttypes.h>
+#  include <inttypes.h>
 #endif // HAVE_INTTYPES_H
 #include <sys/types.h>
 #include <sys/stat.h>
 #ifdef HAVE_FCNTL_H
-#include <fcntl.h>
+#  include <fcntl.h>
 #endif // HAVE_FCNTL_H
 #include <sys/wait.h>
 
@@ -74,6 +74,44 @@ constexpr const char *SEVERITY_COLOR[] = {
 };
 } // namespace
 
+#ifndef NOTHREADS
+#  ifdef HAVE_THREAD_LOCAL
+namespace {
+thread_local LogBuffer logbuf_;
+} // namespace
+
+namespace {
+LogBuffer *get_logbuf() { return &logbuf_; }
+} // namespace
+#  else  // !HAVE_THREAD_LOCAL
+namespace {
+pthread_key_t lckey;
+pthread_once_t lckey_once = PTHREAD_ONCE_INIT;
+} // namespace
+
+namespace {
+void make_key() { pthread_key_create(&lckey, NULL); }
+} // namespace
+
+LogBuffer *get_logbuf() {
+  pthread_once(&lckey_once, make_key);
+  auto buf = static_cast<LogBuffer *>(pthread_getspecific(lckey));
+  if (!buf) {
+    buf = new LogBuffer();
+    pthread_setspecific(lckey, buf);
+  }
+  return buf;
+}
+#  endif // !HAVE_THREAD_LOCAL
+#else    // NOTHREADS
+namespace {
+LogBuffer *get_logbuf() {
+  static LogBuffer logbuf;
+  return &logbuf;
+}
+} // namespace
+#endif   // NOTHREADS
+
 int Log::severity_thres_ = NOTICE;
 
 void Log::set_severity_level(int severity) { severity_thres_ = severity; }
@@ -106,7 +144,15 @@ int severity_to_syslog_level(int severity) {
 }
 
 Log::Log(int severity, const char *filename, int linenum)
-    : filename_(filename), severity_(severity), linenum_(linenum) {}
+    : buf_(*get_logbuf()),
+      begin_(buf_.data()),
+      end_(begin_ + buf_.size()),
+      last_(begin_),
+      filename_(filename),
+      flags_(0),
+      severity_(severity),
+      linenum_(linenum),
+      full_(false) {}
 
 Log::~Log() {
   int rv;
@@ -127,12 +173,13 @@ Log::~Log() {
 
   if (errorconf.syslog) {
     if (severity_ == NOTICE) {
-      syslog(severity_to_syslog_level(severity_), "[%s] %s",
-             SEVERITY_STR[severity_].c_str(), stream_.str().c_str());
+      syslog(severity_to_syslog_level(severity_), "[%s] %.*s",
+             SEVERITY_STR[severity_].c_str(), static_cast<int>(rleft()),
+             begin_);
     } else {
-      syslog(severity_to_syslog_level(severity_), "[%s] %s (%s:%d)",
-             SEVERITY_STR[severity_].c_str(), stream_.str().c_str(), filename_,
-             linenum_);
+      syslog(severity_to_syslog_level(severity_), "[%s] %.*s (%s:%d)",
+             SEVERITY_STR[severity_].c_str(), static_cast<int>(rleft()), begin_,
+             filename_, linenum_);
     }
 
     return;
@@ -145,11 +192,11 @@ Log::~Log() {
 
   // Error log format: <datetime> <master-pid> <current-pid>
   // <thread-id> <level> (<filename>:<line>) <msg>
-  rv = snprintf(buf, sizeof(buf), "%s %d %d %s %s%s%s (%s:%d) %s\n",
+  rv = snprintf(buf, sizeof(buf), "%s %d %d %s %s%s%s (%s:%d) %.*s\n",
                 lgconf->tstamp->time_iso8601.c_str(), config->pid, lgconf->pid,
                 lgconf->thread_id.c_str(), tty ? SEVERITY_COLOR[severity_] : "",
                 SEVERITY_STR[severity_].c_str(), tty ? "\033[0m" : "",
-                filename_, linenum_, stream_.str().c_str());
+                filename_, linenum_, static_cast<int>(rleft()), begin_);
 
   if (rv < 0) {
     return;
@@ -161,6 +208,156 @@ Log::~Log() {
     ;
 }
 
+Log &Log::operator<<(const std::string &s) {
+  write_seq(std::begin(s), std::end(s));
+  return *this;
+}
+
+Log &Log::operator<<(const StringRef &s) {
+  write_seq(std::begin(s), std::end(s));
+  return *this;
+}
+
+Log &Log::operator<<(const char *s) {
+  write_seq(s, s + strlen(s));
+  return *this;
+}
+
+Log &Log::operator<<(const ImmutableString &s) {
+  write_seq(std::begin(s), std::end(s));
+  return *this;
+}
+
+Log &Log::operator<<(int64_t n) {
+  if (n >= 0) {
+    return *this << static_cast<uint64_t>(n);
+  }
+
+  if (flags_ & fmt_hex) {
+    write_hex(n);
+    return *this;
+  }
+
+  if (full_) {
+    return *this;
+  }
+
+  n *= -1;
+
+  size_t nlen = 0;
+  for (auto t = n; t; t /= 10, ++nlen)
+    ;
+  if (wleft() < 1 /* sign */ + nlen) {
+    full_ = true;
+    return *this;
+  }
+  *last_++ = '-';
+  *last_ += nlen;
+  update_full();
+
+  auto p = last_ - 1;
+  for (; n; n /= 10) {
+    *p-- = (n % 10) + '0';
+  }
+  return *this;
+}
+
+Log &Log::operator<<(uint64_t n) {
+  if (flags_ & fmt_hex) {
+    write_hex(n);
+    return *this;
+  }
+
+  if (full_) {
+    return *this;
+  }
+
+  if (n == 0) {
+    *last_++ = '0';
+    update_full();
+    return *this;
+  }
+  size_t nlen = 0;
+  for (auto t = n; t; t /= 10, ++nlen)
+    ;
+  if (wleft() < nlen) {
+    full_ = true;
+    return *this;
+  }
+
+  last_ += nlen;
+  update_full();
+
+  auto p = last_ - 1;
+  for (; n; n /= 10) {
+    *p-- = (n % 10) + '0';
+  }
+  return *this;
+}
+
+Log &Log::operator<<(double n) {
+  if (full_) {
+    return *this;
+  }
+
+  auto left = wleft();
+  auto rv = snprintf(reinterpret_cast<char *>(last_), left, "%.9f", n);
+  if (rv > static_cast<int>(left)) {
+    full_ = true;
+    return *this;
+  }
+
+  last_ += rv;
+  update_full();
+
+  return *this;
+}
+
+Log &Log::operator<<(long double n) {
+  if (full_) {
+    return *this;
+  }
+
+  auto left = wleft();
+  auto rv = snprintf(reinterpret_cast<char *>(last_), left, "%.9Lf", n);
+  if (rv > static_cast<int>(left)) {
+    full_ = true;
+    return *this;
+  }
+
+  last_ += rv;
+  update_full();
+
+  return *this;
+}
+
+Log &Log::operator<<(bool n) {
+  if (full_) {
+    return *this;
+  }
+
+  *last_++ = n ? '1' : '0';
+  update_full();
+
+  return *this;
+}
+
+Log &Log::operator<<(const void *p) {
+  if (full_) {
+    return *this;
+  }
+
+  write_hex(reinterpret_cast<uintptr_t>(p));
+
+  return *this;
+}
+
+namespace log {
+void hex(Log &log) { log.set_flags(Log::fmt_hex); };
+
+void dec(Log &log) { log.set_flags(Log::fmt_dec); };
+} // namespace log
+
 namespace {
 template <typename OutputIterator>
 std::pair<OutputIterator, OutputIterator> copy(const char *src, size_t srclen,
@@ -696,8 +893,9 @@ void log_chld(pid_t pid, int rstatus, const char *msg) {
 
   LOG(NOTICE) << msg << ": [" << pid << "] exited "
               << (WIFEXITED(rstatus) ? "normally" : "abnormally")
-              << " with status " << std::hex << rstatus << std::oct
-              << "; exit status " << WEXITSTATUS(rstatus)
+              << " with status " << log::hex << rstatus << log::dec
+              << "; exit status "
+              << (WIFEXITED(rstatus) ? WEXITSTATUS(rstatus) : 0)
               << (signalstr.empty() ? "" : signalstr.c_str());
 }
 
index ea77fbc..1130b8d 100644 (file)
@@ -29,7 +29,6 @@
 
 #include <sys/types.h>
 
-#include <sstream>
 #include <memory>
 #include <vector>
 #include <chrono>
@@ -38,6 +37,7 @@
 #include "shrpx_log_config.h"
 #include "tls.h"
 #include "template.h"
+#include "util.h"
 
 using namespace nghttp2;
 
@@ -90,26 +90,120 @@ struct DownstreamAddr;
 
 enum SeverityLevel { INFO, NOTICE, WARN, ERROR, FATAL };
 
+using LogBuffer = std::array<uint8_t, 4_k>;
+
 class Log {
 public:
   Log(int severity, const char *filename, int linenum);
   ~Log();
-  template <typename Type> Log &operator<<(Type s) {
-    stream_ << s;
+  Log &operator<<(const std::string &s);
+  Log &operator<<(const char *s);
+  Log &operator<<(const StringRef &s);
+  Log &operator<<(const ImmutableString &s);
+  Log &operator<<(int16_t n) { return *this << static_cast<int64_t>(n); }
+  Log &operator<<(int32_t n) { return *this << static_cast<int64_t>(n); }
+  Log &operator<<(int64_t n);
+  Log &operator<<(uint16_t n) { return *this << static_cast<uint64_t>(n); }
+  Log &operator<<(uint32_t n) { return *this << static_cast<uint64_t>(n); }
+  Log &operator<<(uint64_t n);
+  Log &operator<<(float n) { return *this << static_cast<double>(n); }
+  Log &operator<<(double n);
+  Log &operator<<(long double n);
+  Log &operator<<(bool n);
+  Log &operator<<(const void *p);
+  template <typename T> Log &operator<<(const std::shared_ptr<T> &ptr) {
+    return *this << ptr.get();
+  }
+  Log &operator<<(void (*func)(Log &log)) {
+    func(*this);
     return *this;
   }
+  template <typename InputIt> void write_seq(InputIt first, InputIt last) {
+    if (full_) {
+      return;
+    }
+
+    auto d = std::distance(first, last);
+    auto n = std::min(wleft(), static_cast<size_t>(d));
+    last_ = std::copy(first, first + n, last_);
+    update_full();
+  }
+
+  template <typename T> void write_hex(T n) {
+    if (full_) {
+      return;
+    }
+
+    if (n == 0) {
+      if (wleft() < 4 /* for "0x00" */) {
+        full_ = true;
+        return;
+      }
+      *last_++ = '0';
+      *last_++ = 'x';
+      *last_++ = '0';
+      *last_++ = '0';
+      update_full();
+      return;
+    }
+
+    size_t nlen = 0;
+    for (auto t = n; t; t >>= 8, ++nlen)
+      ;
+
+    nlen *= 2;
+
+    if (wleft() < 2 /* for "0x" */ + nlen) {
+      full_ = true;
+      return;
+    }
+
+    *last_++ = '0';
+    *last_++ = 'x';
+
+    last_ += nlen;
+    update_full();
+
+    auto p = last_ - 1;
+    for (; n; n >>= 8) {
+      uint8_t b = n & 0xff;
+      *p-- = util::LOWER_XDIGITS[b & 0xf];
+      *p-- = util::LOWER_XDIGITS[b >> 4];
+    }
+  }
   static void set_severity_level(int severity);
   static int set_severity_level_by_name(const StringRef &name);
   static bool log_enabled(int severity) { return severity >= severity_thres_; }
 
+  enum {
+    fmt_dec = 0x00,
+    fmt_hex = 0x01,
+  };
+
+  void set_flags(int flags) { flags_ = flags; }
+
 private:
-  std::stringstream stream_;
+  size_t rleft() { return last_ - begin_; }
+  size_t wleft() { return end_ - last_; }
+  void update_full() { full_ = last_ == end_; }
+
+  LogBuffer &buf_;
+  uint8_t *begin_;
+  uint8_t *end_;
+  uint8_t *last_;
   const char *filename_;
+  uint32_t flags_;
   int severity_;
   int linenum_;
+  bool full_;
   static int severity_thres_;
 };
 
+namespace log {
+void hex(Log &log);
+void dec(Log &log);
+} // namespace log
+
 #define TTY_HTTP_HD (log_config()->errorlog_tty ? "\033[1;34m" : "")
 #define TTY_RST (log_config()->errorlog_tty ? "\033[0m" : "")
 
index a8fbe41..668f91a 100644 (file)
@@ -57,14 +57,14 @@ LogConfig::LogConfig()
 }
 
 #ifndef NOTHREADS
-#ifdef HAVE_THREAD_LOCAL
+#  ifdef HAVE_THREAD_LOCAL
 namespace {
 thread_local std::unique_ptr<LogConfig> config = make_unique<LogConfig>();
 } // namespace
 
 LogConfig *log_config() { return config.get(); }
 void delete_log_config() {}
-#else  // !HAVE_THREAD_LOCAL
+#  else  // !HAVE_THREAD_LOCAL
 namespace {
 pthread_key_t lckey;
 pthread_once_t lckey_once = PTHREAD_ONCE_INIT;
@@ -85,8 +85,8 @@ LogConfig *log_config() {
 }
 
 void delete_log_config() { delete log_config(); }
-#endif // !HAVE_THREAD_LOCAL
-#else  // NOTHREADS
+#  endif // !HAVE_THREAD_LOCAL
+#else    // NOTHREADS
 namespace {
 std::unique_ptr<LogConfig> config = make_unique<LogConfig>();
 } // namespace
@@ -94,7 +94,7 @@ std::unique_ptr<LogConfig> config = make_unique<LogConfig>();
 LogConfig *log_config() { return config.get(); }
 
 void delete_log_config() {}
-#endif // NOTHREADS
+#endif   // NOTHREADS
 
 void LogConfig::update_tstamp_millis(
     const std::chrono::system_clock::time_point &now) {
index d087ac8..99ec1eb 100644 (file)
@@ -578,9 +578,9 @@ int MemcachedConnection::parse_packet() {
 #define DEFAULT_WR_IOVCNT 128
 
 #if defined(IOV_MAX) && IOV_MAX < DEFAULT_WR_IOVCNT
-#define MAX_WR_IOVCNT IOV_MAX
+#  define MAX_WR_IOVCNT IOV_MAX
 #else // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT
-#define MAX_WR_IOVCNT DEFAULT_WR_IOVCNT
+#  define MAX_WR_IOVCNT DEFAULT_WR_IOVCNT
 #endif // !defined(IOV_MAX) || IOV_MAX >= DEFAULT_WR_IOVCNT
 
 size_t MemcachedConnection::fill_request_buffer(struct iovec *iov,
index ec08aab..ca29ef0 100644 (file)
@@ -136,7 +136,7 @@ mrb_value instantiate_app(mrb_state *mrb, RProc *proc) {
 // Based on
 // https://github.com/h2o/h2o/blob/master/lib/handler/mruby.c.  It is
 // very hard to write these kind of code because mruby has almost no
-// documentation aobut compiling or generating code, at least at the
+// documentation about compiling or generating code, at least at the
 // time of this writing.
 RProc *compile(mrb_state *mrb, const StringRef &filename) {
   if (filename.empty()) {
@@ -145,6 +145,7 @@ RProc *compile(mrb_state *mrb, const StringRef &filename) {
 
   auto infile = fopen(filename.c_str(), "rb");
   if (infile == nullptr) {
+    LOG(ERROR) << "Could not open mruby file " << filename;
     return nullptr;
   }
   auto infile_d = defer(fclose, infile);
index 9a7c364..b3ed365 100644 (file)
@@ -397,6 +397,18 @@ mrb_value env_get_alpn(mrb_state *mrb, mrb_value self) {
 }
 } // namespace
 
+namespace {
+mrb_value env_get_tls_handshake_finished(mrb_state *mrb, mrb_value self) {
+  auto data = static_cast<MRubyAssocData *>(mrb->ud);
+  auto downstream = data->downstream;
+  auto upstream = downstream->get_upstream();
+  auto handler = upstream->get_client_handler();
+  auto conn = handler->get_connection();
+  return SSL_is_init_finished(conn->tls.ssl) ? mrb_true_value()
+                                             : mrb_false_value();
+}
+} // namespace
+
 void init_env_class(mrb_state *mrb, RClass *module) {
   auto env_class =
       mrb_define_class_under(mrb, module, "Env", mrb->object_class);
@@ -439,6 +451,8 @@ void init_env_class(mrb_state *mrb, RClass *module) {
   mrb_define_method(mrb, env_class, "tls_session_reused",
                     env_get_tls_session_reused, MRB_ARGS_NONE());
   mrb_define_method(mrb, env_class, "alpn", env_get_alpn, MRB_ARGS_NONE());
+  mrb_define_method(mrb, env_class, "tls_handshake_finished",
+                    env_get_tls_handshake_finished, MRB_ARGS_NONE());
 }
 
 } // namespace mruby
index 0cd0d03..1ed3aa9 100644 (file)
@@ -254,7 +254,7 @@ mrb_value request_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
   }
 
   if (mrb_array_p(values)) {
-    auto n = mrb_ary_len(mrb, values);
+    auto n = RARRAY_LEN(values);
     for (int i = 0; i < n; ++i) {
       auto value = mrb_ary_ref(mrb, values, i);
       if (!mrb_string_p(value)) {
index 62f46d5..3b24ea0 100644 (file)
@@ -146,7 +146,7 @@ mrb_value response_mod_header(mrb_state *mrb, mrb_value self, bool repl) {
   }
 
   if (mrb_array_p(values)) {
-    auto n = mrb_ary_len(mrb, values);
+    auto n = RARRAY_LEN(values);
     for (int i = 0; i < n; ++i) {
       auto value = mrb_ary_ref(mrb, values, i);
       if (!mrb_string_p(value)) {
@@ -299,7 +299,7 @@ mrb_value response_send_info(mrb_state *mrb, mrb_value self) {
   auto &balloc = downstream->get_block_allocator();
 
   auto keys = mrb_hash_keys(mrb, hash);
-  auto keyslen = mrb_ary_len(mrb, keys);
+  auto keyslen = RARRAY_LEN(keys);
 
   for (int i = 0; i < keyslen; ++i) {
     auto key = mrb_ary_ref(mrb, keys, i);
@@ -322,7 +322,7 @@ mrb_value response_send_info(mrb_state *mrb, mrb_value self) {
     auto token = http2::lookup_token(keyref.byte(), keyref.size());
 
     if (mrb_array_p(values)) {
-      auto n = mrb_ary_len(mrb, values);
+      auto n = RARRAY_LEN(values);
       for (int i = 0; i < n; ++i) {
         auto value = mrb_ary_ref(mrb, values, i);
         if (!mrb_string_p(value)) {
index 77a1fe2..f05ae64 100644 (file)
@@ -108,8 +108,9 @@ void RateLimit::stopw() {
 }
 
 void RateLimit::handle_tls_pending_read() {
-  if (!conn_ || !conn_->tls.ssl ||
-      (SSL_pending(conn_->tls.ssl) == 0 && conn_->tls.rbuf.rleft() == 0)) {
+  if (!conn_ || !conn_->tls.ssl || !conn_->tls.initial_handshake_done ||
+      (SSL_pending(conn_->tls.ssl) == 0 && conn_->tls.rbuf.rleft() == 0 &&
+       conn_->tls.earlybuf.rleft() == 0)) {
     return;
   }
 
index 03b49d2..d39cb87 100644 (file)
@@ -26,7 +26,7 @@
 #define SHRPX_ROUTER_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif // HAVE_CONFIG_H
 
 namespace shrpx {
index 87ca928..76a6e76 100644 (file)
 #include "shrpx_tls.h"
 
 #ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
+#  include <sys/socket.h>
 #endif // HAVE_SYS_SOCKET_H
 #ifdef HAVE_NETDB_H
-#include <netdb.h>
+#  include <netdb.h>
 #endif // HAVE_NETDB_H
 #include <netinet/tcp.h>
 #include <pthread.h>
@@ -46,7 +46,7 @@
 #include <openssl/rand.h>
 #include <openssl/dh.h>
 #ifndef OPENSSL_NO_OCSP
-#include <openssl/ocsp.h>
+#  include <openssl/ocsp.h>
 #endif // OPENSSL_NO_OCSP
 
 #include <nghttp2/nghttp2.h>
@@ -80,6 +80,7 @@ const unsigned char *ASN1_STRING_get0_data(ASN1_STRING *x) {
 } // namespace
 #endif // !OPENSSL_1_1_API
 
+#ifndef OPENSSL_NO_NEXTPROTONEG
 namespace {
 int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
                   void *arg) {
@@ -89,6 +90,7 @@ int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
   return SSL_TLSEXT_ERR_OK;
 }
 } // namespace
+#endif // !OPENSSL_NO_NEXTPROTONEG
 
 namespace {
 int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
@@ -192,7 +194,7 @@ int servername_callback(SSL *ssl, int *al, void *arg) {
   const auto &ssl_ctx_list = conn_handler->get_indexed_ssl_ctx(idx);
   assert(!ssl_ctx_list.empty());
 
-#if !defined(OPENSSL_IS_BORINGSSL) && !defined(LIBRESSL_VERSION_NUMBER) &&     \
+#if !defined(OPENSSL_IS_BORINGSSL) && !LIBRESSL_IN_USE &&                      \
     OPENSSL_VERSION_NUMBER >= 0x10002000L
   auto num_shared_curves = SSL_get_shared_curve(ssl, -1);
 
@@ -202,21 +204,21 @@ int servername_callback(SSL *ssl, int *al, void *arg) {
     for (auto ssl_ctx : ssl_ctx_list) {
       auto cert = SSL_CTX_get0_certificate(ssl_ctx);
 
-#if OPENSSL_1_1_API
+#  if OPENSSL_1_1_API
       auto pubkey = X509_get0_pubkey(cert);
-#else  // !OPENSSL_1_1_API
+#  else  // !OPENSSL_1_1_API
       auto pubkey = X509_get_pubkey(cert);
-#endif // !OPENSSL_1_1_API
+#  endif // !OPENSSL_1_1_API
 
       if (EVP_PKEY_base_id(pubkey) != EVP_PKEY_EC) {
         continue;
       }
 
-#if OPENSSL_1_1_API
+#  if OPENSSL_1_1_API
       auto eckey = EVP_PKEY_get0_EC_KEY(pubkey);
-#else  // !OPENSSL_1_1_API
+#  else  // !OPENSSL_1_1_API
       auto eckey = EVP_PKEY_get1_EC_KEY(pubkey);
-#endif // !OPENSSL_1_1_API
+#  endif // !OPENSSL_1_1_API
 
       if (eckey == nullptr) {
         continue;
@@ -225,10 +227,10 @@ int servername_callback(SSL *ssl, int *al, void *arg) {
       auto ecgroup = EC_KEY_get0_group(eckey);
       auto cert_curve = EC_GROUP_get_curve_name(ecgroup);
 
-#if !OPENSSL_1_1_API
+#  if !OPENSSL_1_1_API
       EC_KEY_free(eckey);
       EVP_PKEY_free(pubkey);
-#endif // !OPENSSL_1_1_API
+#  endif // !OPENSSL_1_1_API
 
       if (shared_curve == cert_curve) {
         SSL_set_SSL_CTX(ssl, ssl_ctx);
@@ -236,7 +238,7 @@ int servername_callback(SSL *ssl, int *al, void *arg) {
       }
     }
   }
-#endif // !defined(OPENSSL_IS_BORINGSSL) && !defined(LIBRESSL_VERSION_NUMBER) &&
+#endif // !defined(OPENSSL_IS_BORINGSSL) && !LIBRESSL_IN_USE &&
        // OPENSSL_VERSION_NUMBER >= 0x10002000L
 
   SSL_set_SSL_CTX(ssl, ssl_ctx_list[0]);
@@ -249,13 +251,13 @@ int servername_callback(SSL *ssl, int *al, void *arg) {
 namespace {
 std::shared_ptr<std::vector<uint8_t>>
 get_ocsp_data(TLSContextData *tls_ctx_data) {
-#ifdef HAVE_ATOMIC_STD_SHARED_PTR
+#  ifdef HAVE_ATOMIC_STD_SHARED_PTR
   return std::atomic_load_explicit(&tls_ctx_data->ocsp_data,
                                    std::memory_order_acquire);
-#else  // !HAVE_ATOMIC_STD_SHARED_PTR
+#  else  // !HAVE_ATOMIC_STD_SHARED_PTR
   std::lock_guard<std::mutex> g(tls_ctx_data->mu);
   return tls_ctx_data->ocsp_data;
-#endif // !HAVE_ATOMIC_STD_SHARED_PTR
+#  endif // !HAVE_ATOMIC_STD_SHARED_PTR
 }
 } // namespace
 
@@ -515,6 +517,13 @@ int ticket_key_cb(SSL *ssl, unsigned char *key_name, unsigned char *iv,
 
 namespace {
 void info_callback(const SSL *ssl, int where, int ret) {
+#ifdef TLS1_3_VERSION
+  // TLSv1.3 has no renegotiation.
+  if (SSL_version(ssl) == TLS1_3_VERSION) {
+    return;
+  }
+#endif // TLS1_3_VERSION
+
   // To mitigate possible DOS attack using lots of renegotiations, we
   // disable renegotiation. Since OpenSSL does not provide an easy way
   // to disable it, we check that renegotiation is started in this
@@ -565,9 +574,9 @@ int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
 
 #if !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
 
-#ifndef TLSEXT_TYPE_signed_certificate_timestamp
-#define TLSEXT_TYPE_signed_certificate_timestamp 18
-#endif // !TLSEXT_TYPE_signed_certificate_timestamp
+#  ifndef TLSEXT_TYPE_signed_certificate_timestamp
+#    define TLSEXT_TYPE_signed_certificate_timestamp 18
+#  endif // !TLSEXT_TYPE_signed_certificate_timestamp
 
 namespace {
 int sct_add_cb(SSL *ssl, unsigned int ext_type, unsigned int context,
@@ -582,7 +591,7 @@ int sct_add_cb(SSL *ssl, unsigned int ext_type, unsigned int context,
 
   if (LOG_ENABLED(INFO)) {
     LOG(INFO) << "sct_add_cb is called, chainidx=" << chainidx << ", x=" << x
-              << ", context=" << std::hex << context;
+              << ", context=" << log::hex << context;
   }
 
   // We only have SCTs for leaf certificate.
@@ -627,7 +636,7 @@ int sct_parse_cb(SSL *ssl, unsigned int ext_type, unsigned int context,
 }
 } // namespace
 
-#if !OPENSSL_1_1_1_API
+#  if !OPENSSL_1_1_1_API
 
 namespace {
 int legacy_sct_add_cb(SSL *ssl, unsigned int ext_type,
@@ -652,10 +661,10 @@ int legacy_sct_parse_cb(SSL *ssl, unsigned int ext_type,
 }
 } // namespace
 
-#endif // !OPENSSL_1_1_1_API
-#endif // !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
+#  endif // !OPENSSL_1_1_1_API
+#endif   // !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
 
-#if !LIBRESSL_IN_USE
+#ifndef OPENSSL_NO_PSK
 namespace {
 unsigned int psk_server_cb(SSL *ssl, const char *identity, unsigned char *psk,
                            unsigned int max_psk_len) {
@@ -679,9 +688,9 @@ unsigned int psk_server_cb(SSL *ssl, const char *identity, unsigned char *psk,
   return static_cast<unsigned int>(secret.size());
 }
 } // namespace
-#endif // !LIBRESSL_IN_USE
+#endif // !OPENSSL_NO_PSK
 
-#if !LIBRESSL_IN_USE
+#ifndef OPENSSL_NO_PSK
 namespace {
 unsigned int psk_client_cb(SSL *ssl, const char *hint, char *identity_out,
                            unsigned int max_identity_len, unsigned char *psk,
@@ -714,7 +723,7 @@ unsigned int psk_client_cb(SSL *ssl, const char *hint, char *identity_out,
   return static_cast<unsigned int>(secret.size());
 }
 } // namespace
-#endif // !LIBRESSL_IN_USE
+#endif // !OPENSSL_NO_PSK
 
 struct TLSProtocol {
   StringRef name;
@@ -761,7 +770,17 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
       (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) | SSL_OP_NO_SSLv2 |
       SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION |
       SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_SINGLE_ECDH_USE |
-      SSL_OP_SINGLE_DH_USE | SSL_OP_CIPHER_SERVER_PREFERENCE;
+      SSL_OP_SINGLE_DH_USE |
+      SSL_OP_CIPHER_SERVER_PREFERENCE
+#if OPENSSL_1_1_1_API
+      // The reason for disabling built-in anti-replay in OpenSSL is
+      // that it only works if client gets back to the same server.
+      // The freshness check described in
+      // https://tools.ietf.org/html/rfc8446#section-8.3 is still
+      // performed.
+      | SSL_OP_NO_ANTI_REPLAY
+#endif // OPENSSL_1_1_1_API
+      ;
 
   auto config = mod_config();
   auto &tlsconf = config->tls;
@@ -791,19 +810,27 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
     DIE();
   }
 
+#if OPENSSL_1_1_1_API
+  if (SSL_CTX_set_ciphersuites(ssl_ctx, tlsconf.tls13_ciphers.c_str()) == 0) {
+    LOG(FATAL) << "SSL_CTX_set_ciphersuites " << tlsconf.tls13_ciphers
+               << " failed: " << ERR_error_string(ERR_get_error(), nullptr);
+    DIE();
+  }
+#endif // OPENSSL_1_1_1_API
+
 #ifndef OPENSSL_NO_EC
-#if !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
+#  if !LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L
   if (SSL_CTX_set1_curves_list(ssl_ctx, tlsconf.ecdh_curves.c_str()) != 1) {
     LOG(FATAL) << "SSL_CTX_set1_curves_list " << tlsconf.ecdh_curves
                << " failed";
     DIE();
   }
-#if !defined(OPENSSL_IS_BORINGSSL) && !OPENSSL_1_1_API
+#    if !defined(OPENSSL_IS_BORINGSSL) && !OPENSSL_1_1_API
   // It looks like we need this function call for OpenSSL 1.0.2.  This
   // function was deprecated in OpenSSL 1.1.0 and BoringSSL.
   SSL_CTX_set_ecdh_auto(ssl_ctx, 1);
-#endif // !defined(OPENSSL_IS_BORINGSSL) && !OPENSSL_1_1_API
-#else  // LIBRESSL_IN_USE || OPENSSL_VERSION_NUBMER < 0x10002000L
+#    endif // !defined(OPENSSL_IS_BORINGSSL) && !OPENSSL_1_1_API
+#  else    // LIBRESSL_LEGACY_API || OPENSSL_VERSION_NUBMER < 0x10002000L
   // Use P-256, which is sufficiently secure at the time of this
   // writing.
   auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
@@ -814,8 +841,8 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
   }
   SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
   EC_KEY_free(ecdh);
-#endif // LIBRESSL_IN_USE || OPENSSL_VERSION_NUBMER < 0x10002000L
-#endif // OPENSSL_NO_EC
+#  endif   // LIBRESSL_LEGACY_API || OPENSSL_VERSION_NUBMER < 0x10002000L
+#endif     // OPENSSL_NO_EC
 
   if (!tlsconf.dh_param_file.empty()) {
     // Read DH parameters from file
@@ -923,7 +950,9 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
 #endif // OPENSSL_IS_BORINGSSL
 
   // NPN advertisement
+#ifndef OPENSSL_NO_NEXTPROTONEG
   SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, nullptr);
+#endif // !OPENSSL_NO_NEXTPROTONEG
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
   // ALPN selection callback
   SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, nullptr);
@@ -935,7 +964,7 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
   // OpenSSL handles signed_certificate_timestamp extension specially,
   // and it lets custom handler to process the extension.
   if (!sct_data.empty()) {
-#if OPENSSL_1_1_1_API
+#  if OPENSSL_1_1_1_API
     // It is not entirely clear to me that SSL_EXT_CLIENT_HELLO is
     // required here.  sct_parse_cb is called without
     // SSL_EXT_CLIENT_HELLO being set.  But the passed context value
@@ -949,7 +978,7 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
                  << ERR_error_string(ERR_get_error(), nullptr);
       DIE();
     }
-#else  // !OPENSSL_1_1_1_API
+#  else  // !OPENSSL_1_1_1_API
     if (SSL_CTX_add_server_custom_ext(
             ssl_ctx, TLSEXT_TYPE_signed_certificate_timestamp,
             legacy_sct_add_cb, legacy_sct_free_cb, nullptr, legacy_sct_parse_cb,
@@ -958,13 +987,21 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
                  << ERR_error_string(ERR_get_error(), nullptr);
       DIE();
     }
-#endif // !OPENSSL_1_1_1_API
+#  endif // !OPENSSL_1_1_1_API
   }
 #endif // !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
 
-#if !LIBRESSL_IN_USE
+#if OPENSSL_1_1_1_API
+  if (SSL_CTX_set_max_early_data(ssl_ctx, tlsconf.max_early_data) != 1) {
+    LOG(FATAL) << "SSL_CTX_set_max_early_data failed: "
+               << ERR_error_string(ERR_get_error(), nullptr);
+    DIE();
+  }
+#endif // OPENSSL_1_1_1_API
+
+#ifndef OPENSSL_NO_PSK
   SSL_CTX_set_psk_server_callback(ssl_ctx, psk_server_cb);
-#endif // !LIBRESSL_IN_USE
+#endif // !LIBRESSL_NO_PSK
 
   auto tls_ctx_data = new TLSContextData();
   tls_ctx_data->cert_file = cert_file;
@@ -1062,6 +1099,15 @@ SSL_CTX *create_ssl_client_context(
     DIE();
   }
 
+#if OPENSSL_1_1_1_API
+  if (SSL_CTX_set_ciphersuites(ssl_ctx, tlsconf.client.tls13_ciphers.c_str()) ==
+      0) {
+    LOG(FATAL) << "SSL_CTX_set_ciphersuites " << tlsconf.client.tls13_ciphers
+               << " failed: " << ERR_error_string(ERR_get_error(), nullptr);
+    DIE();
+  }
+#endif // OPENSSL_1_1_1_API
+
   SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
 
   if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) {
@@ -1112,13 +1158,15 @@ SSL_CTX *create_ssl_client_context(
 #endif // HAVE_NEVERBLEED
   }
 
-#if !LIBRESSL_IN_USE
+#ifndef OPENSSL_NO_PSK
   SSL_CTX_set_psk_client_callback(ssl_ctx, psk_client_cb);
-#endif // !LIBRESSL_IN_USE
+#endif // !OPENSSL_NO_PSK
 
   // NPN selection callback.  This is required to set SSL_CTX because
   // OpenSSL does not offer SSL_set_next_proto_select_cb.
+#ifndef OPENSSL_NO_NEXTPROTONEG
   SSL_CTX_set_next_proto_select_cb(ssl_ctx, next_proto_select_cb, nullptr);
+#endif // !OPENSSL_NO_NEXTPROTONEG
 
   return ssl_ctx;
 }
@@ -1549,16 +1597,15 @@ int cert_lookup_tree_add_ssl_ctx(
     SSL_CTX *ssl_ctx) {
   std::array<uint8_t, NI_MAXHOST> buf;
 
-#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10002000L
+#if LIBRESSL_2_7_API ||                                                        \
+    (!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L)
   auto cert = SSL_CTX_get0_certificate(ssl_ctx);
-#else  // defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER <
-  // 0x10002000L
+#else  // !LIBRESSL_2_7_API && OPENSSL_VERSION_NUMBER < 0x10002000L
   auto tls_ctx_data =
       static_cast<TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
   auto cert = load_certificate(tls_ctx_data->cert_file);
   auto cert_deleter = defer(X509_free, cert);
-#endif // defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER <
-       // 0x10002000L
+#endif // !LIBRESSL_2_7_API && OPENSSL_VERSION_NUMBER < 0x10002000L
 
   auto altnames = static_cast<GENERAL_NAMES *>(
       X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr));
@@ -1809,7 +1856,7 @@ void try_cache_tls_session(TLSSessionCache *cache, SSL_SESSION *session,
 
   if (LOG_ENABLED(INFO)) {
     LOG(INFO) << "Update client cache entry "
-              << "timestamp = " << std::fixed << std::setprecision(6) << t;
+              << "timestamp = " << t;
   }
 
   cache->session_data = serialize_ssl_session(session);
@@ -1846,7 +1893,7 @@ int proto_version_from_string(const StringRef &v) {
 int verify_ocsp_response(SSL_CTX *ssl_ctx, const uint8_t *ocsp_resp,
                          size_t ocsp_resplen) {
 
-#if !defined(OPENSSL_NO_OCSP) && !defined(LIBRESSL_VERSION_NUMBER) &&          \
+#if !defined(OPENSSL_NO_OCSP) && !LIBRESSL_IN_USE &&                           \
     OPENSSL_VERSION_NUMBER >= 0x10002000L
   int rv;
 
@@ -1860,6 +1907,11 @@ int verify_ocsp_response(SSL_CTX *ssl_ctx, const uint8_t *ocsp_resp,
   }
   auto resp_deleter = defer(OCSP_RESPONSE_free, resp);
 
+  if (OCSP_response_status(resp) != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+    LOG(ERROR) << "OCSP response status is not successful";
+    return -1;
+  }
+
   ERR_clear_error();
 
   auto bs = OCSP_response_get1_basic(resp);
@@ -1888,11 +1940,11 @@ int verify_ocsp_response(SSL_CTX *ssl_ctx, const uint8_t *ocsp_resp,
     return -1;
   }
 
-#if OPENSSL_1_1_API
+#  if OPENSSL_1_1_API
   auto certid = OCSP_SINGLERESP_get0_id(sresp);
-#else  // !OPENSSL_1_1_API
+#  else  // !OPENSSL_1_1_API
   auto certid = sresp->certId;
-#endif // !OPENSSL_1_1_API
+#  endif // !OPENSSL_1_1_API
   assert(certid != nullptr);
 
   ASN1_INTEGER *serial;
@@ -1919,7 +1971,7 @@ int verify_ocsp_response(SSL_CTX *ssl_ctx, const uint8_t *ocsp_resp,
   if (LOG_ENABLED(INFO)) {
     LOG(INFO) << "OCSP verification succeeded";
   }
-#endif // !defined(OPENSSL_NO_OCSP) && !defined(LIBRESSL_VERSION_NUMBER)
+#endif // !defined(OPENSSL_NO_OCSP) && !LIBRESSL_IN_USE
        // && OPENSSL_VERSION_NUMBER >= 0x10002000L
 
   return 0;
@@ -1966,10 +2018,10 @@ StringRef get_x509_issuer_name(BlockAllocator &balloc, X509 *x) {
 }
 
 #ifdef WORDS_BIGENDIAN
-#define bswap64(N) (N)
+#  define bswap64(N) (N)
 #else /* !WORDS_BIGENDIAN */
-#define bswap64(N)                                                             \
-  ((uint64_t)(ntohl((uint32_t)(N))) << 32 | ntohl((uint32_t)((N) >> 32)))
+#  define bswap64(N)                                                           \
+    ((uint64_t)(ntohl((uint32_t)(N))) << 32 | ntohl((uint32_t)((N) >> 32)))
 #endif /* !WORDS_BIGENDIAN */
 
 StringRef get_x509_serial(BlockAllocator &balloc, X509 *x) {
index f3ee63c..1ca2033 100644 (file)
@@ -36,7 +36,7 @@
 #include <ev.h>
 
 #ifdef HAVE_NEVERBLEED
-#include <neverbleed.h>
+#  include <neverbleed.h>
 #endif // HAVE_NEVERBLEED
 
 #include "network.h"
index 40360db..e9c69d0 100644 (file)
@@ -26,7 +26,7 @@
 #define SHRPX_TLS_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif // HAVE_CONFIG_H
 
 namespace shrpx {
index 5801943..05caa19 100644 (file)
@@ -25,7 +25,7 @@
 #include "shrpx_worker.h"
 
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 
 #include <memory>
@@ -37,7 +37,7 @@
 #include "shrpx_log_config.h"
 #include "shrpx_memcached_dispatcher.h"
 #ifdef HAVE_MRUBY
-#include "shrpx_mruby.h"
+#  include "shrpx_mruby.h"
 #endif // HAVE_MRUBY
 #include "util.h"
 #include "template.h"
@@ -68,12 +68,16 @@ void proc_wev_cb(struct ev_loop *loop, ev_timer *w, int revents) {
 }
 } // namespace
 
+DownstreamAddrGroup::DownstreamAddrGroup() : retired{false} {}
+
+DownstreamAddrGroup::~DownstreamAddrGroup() {}
+
 // DownstreamKey is used to index SharedDownstreamAddr in order to
 // find the same configuration.
 using DownstreamKey = std::tuple<
     std::vector<std::tuple<StringRef, StringRef, size_t, size_t, shrpx_proto,
                            uint16_t, bool, bool, bool, bool>>,
-    bool, int, StringRef, StringRef, int>;
+    bool, int, StringRef, StringRef, int, int64_t, int64_t>;
 
 namespace {
 DownstreamKey create_downstream_key(
@@ -105,6 +109,9 @@ DownstreamKey create_downstream_key(
   std::get<3>(dkey) = affinity.cookie.name;
   std::get<4>(dkey) = affinity.cookie.path;
   std::get<5>(dkey) = affinity.cookie.secure;
+  auto &timeout = shared_addr->timeout;
+  std::get<6>(dkey) = timeout.read;
+  std::get<7>(dkey) = timeout.write;
 
   return dkey;
 }
@@ -177,6 +184,12 @@ void Worker::replace_downstream_config(
       std::vector<std::shared_ptr<DownstreamAddrGroup>>(groups.size());
 
   std::map<DownstreamKey, size_t> addr_groups_indexer;
+#ifdef HAVE_MRUBY
+  // TODO It is a bit less efficient because
+  // mruby::create_mruby_context returns std::unique_ptr and we cannot
+  // use std::make_shared.
+  std::map<StringRef, std::shared_ptr<mruby::MRubyContext>> shared_mruby_ctxs;
+#endif // HAVE_MRUBY
 
   for (size_t i = 0; i < groups.size(); ++i) {
     auto &src = groups[i];
@@ -185,6 +198,16 @@ void Worker::replace_downstream_config(
     dst = std::make_shared<DownstreamAddrGroup>();
     dst->pattern =
         ImmutableString{std::begin(src.pattern), std::end(src.pattern)};
+#ifdef HAVE_MRUBY
+    auto mruby_ctx_it = shared_mruby_ctxs.find(src.mruby_file);
+    if (mruby_ctx_it == std::end(shared_mruby_ctxs)) {
+      dst->mruby_ctx = mruby::create_mruby_context(src.mruby_file);
+      assert(dst->mruby_ctx);
+      shared_mruby_ctxs.emplace(src.mruby_file, dst->mruby_ctx);
+    } else {
+      dst->mruby_ctx = (*mruby_ctx_it).second;
+    }
+#endif // HAVE_MRUBY
 
     auto shared_addr = std::make_shared<SharedDownstreamAddr>();
 
@@ -201,6 +224,8 @@ void Worker::replace_downstream_config(
     }
     shared_addr->affinity_hash = src.affinity_hash;
     shared_addr->redirect_if_not_tls = src.redirect_if_not_tls;
+    shared_addr->timeout.read = src.timeout.read;
+    shared_addr->timeout.write = src.timeout.write;
 
     size_t num_http1 = 0;
     size_t num_http2 = 0;
index a7a7773..cc8c2a0 100644 (file)
@@ -34,7 +34,7 @@
 #include <deque>
 #include <thread>
 #ifndef NOTHREADS
-#include <future>
+#  include <future>
 #endif // NOTHREADS
 
 #include <openssl/ssl.h>
@@ -180,10 +180,16 @@ struct SharedDownstreamAddr {
   // true if this group requires that client connection must be TLS,
   // and the request must be redirected to https URI.
   bool redirect_if_not_tls;
+  // Timeouts for backend connection.
+  struct {
+    ev_tstamp read;
+    ev_tstamp write;
+  } timeout;
 };
 
 struct DownstreamAddrGroup {
-  DownstreamAddrGroup() : retired{false} {};
+  DownstreamAddrGroup();
+  ~DownstreamAddrGroup();
 
   DownstreamAddrGroup(const DownstreamAddrGroup &) = delete;
   DownstreamAddrGroup(DownstreamAddrGroup &&) = delete;
@@ -192,6 +198,9 @@ struct DownstreamAddrGroup {
 
   ImmutableString pattern;
   std::shared_ptr<SharedDownstreamAddr> shared_addr;
+#ifdef HAVE_MRUBY
+  std::shared_ptr<mruby::MRubyContext> mruby_ctx;
+#endif // HAVE_MRUBY
   // true if this group is no longer used for new request.  If this is
   // true, the connection made using one of address in shared_addr
   // must not be pooled.
index 647c167..8c92ce0 100644 (file)
@@ -26,7 +26,7 @@
 
 #include <sys/types.h>
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 #include <sys/resource.h>
 #include <sys/wait.h>
@@ -411,35 +411,30 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
 
   auto gen = util::make_mt19937();
 
-  ConnectionHandler conn_handler(loop, gen);
+  auto conn_handler = make_unique<ConnectionHandler>(loop, gen);
 
   for (auto &addr : config->conn.listener.addrs) {
-    conn_handler.add_acceptor(make_unique<AcceptHandler>(&addr, &conn_handler));
+    conn_handler->add_acceptor(
+        make_unique<AcceptHandler>(&addr, conn_handler.get()));
   }
 
 #ifdef HAVE_NEVERBLEED
-  {
-    std::array<char, NEVERBLEED_ERRBUF_SIZE> nb_errbuf;
-    auto nb = make_unique<neverbleed_t>();
-    if (neverbleed_init(nb.get(), nb_errbuf.data()) != 0) {
-      LOG(FATAL) << "neverbleed_init failed: " << nb_errbuf.data();
-      return -1;
-    }
-
-    LOG(NOTICE) << "neverbleed process [" << nb->daemon_pid << "] spawned";
-
-    conn_handler.set_neverbleed(std::move(nb));
+  std::array<char, NEVERBLEED_ERRBUF_SIZE> nb_errbuf;
+  auto nb = make_unique<neverbleed_t>();
+  if (neverbleed_init(nb.get(), nb_errbuf.data()) != 0) {
+    LOG(FATAL) << "neverbleed_init failed: " << nb_errbuf.data();
+    return -1;
   }
 
-  auto nb = conn_handler.get_neverbleed();
+  LOG(NOTICE) << "neverbleed process [" << nb->daemon_pid << "] spawned";
+
+  conn_handler->set_neverbleed(nb.get());
 
   ev_child nb_childev;
-  if (nb) {
-    ev_child_init(&nb_childev, nb_child_cb, nb->daemon_pid, 0);
-    nb_childev.data = nullptr;
-    ev_child_start(loop, &nb_childev);
-  }
 
+  ev_child_init(&nb_childev, nb_child_cb, nb->daemon_pid, 0);
+  nb_childev.data = nullptr;
+  ev_child_start(loop, &nb_childev);
 #endif // HAVE_NEVERBLEED
 
   MemchunkPool mcpool;
@@ -453,17 +448,17 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
       SSL_CTX *ssl_ctx = nullptr;
 
       if (memcachedconf.tls) {
-        ssl_ctx = conn_handler.create_tls_ticket_key_memcached_ssl_ctx();
+        ssl_ctx = conn_handler->create_tls_ticket_key_memcached_ssl_ctx();
       }
 
-      conn_handler.set_tls_ticket_key_memcached_dispatcher(
+      conn_handler->set_tls_ticket_key_memcached_dispatcher(
           make_unique<MemcachedDispatcher>(
               &ticketconf.memcached.addr, loop, ssl_ctx,
               StringRef{memcachedconf.host}, &mcpool, gen));
 
       ev_timer_init(&renew_ticket_key_timer, memcached_get_ticket_key_cb, 0.,
                     0.);
-      renew_ticket_key_timer.data = &conn_handler;
+      renew_ticket_key_timer.data = conn_handler.get();
       // Get first ticket keys.
       memcached_get_ticket_key_cb(loop, &renew_ticket_key_timer, 0);
     } else {
@@ -483,14 +478,14 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
         if (!ticket_keys) {
           LOG(WARN) << "Use internal session ticket key generator";
         } else {
-          conn_handler.set_ticket_keys(std::move(ticket_keys));
+          conn_handler->set_ticket_keys(std::move(ticket_keys));
           auto_tls_ticket_key = false;
         }
       }
       if (auto_tls_ticket_key) {
         // Generate new ticket key every 1hr.
         ev_timer_init(&renew_ticket_key_timer, renew_ticket_key_cb, 0., 1_h);
-        renew_ticket_key_timer.data = &conn_handler;
+        renew_ticket_key_timer.data = conn_handler.get();
         ev_timer_again(loop, &renew_ticket_key_timer);
 
         // Generate first session ticket key before running workers.
@@ -500,7 +495,7 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
   }
 
   if (config->single_thread) {
-    rv = conn_handler.create_single_worker();
+    rv = conn_handler->create_single_worker();
     if (rv != 0) {
       return -1;
     }
@@ -518,7 +513,7 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
     }
 #endif // !NOTHREADS
 
-    rv = conn_handler.create_worker_thread(config->num_worker);
+    rv = conn_handler->create_worker_thread(config->num_worker);
     if (rv != 0) {
       return -1;
     }
@@ -535,22 +530,22 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
 
   drop_privileges(
 #ifdef HAVE_NEVERBLEED
-      nb
+      nb.get()
 #endif // HAVE_NEVERBLEED
   );
 
   ev_io ipcev;
   ev_io_init(&ipcev, ipc_readcb, wpconf->ipc_fd, EV_READ);
-  ipcev.data = &conn_handler;
+  ipcev.data = conn_handler.get();
   ev_io_start(loop, &ipcev);
 
   if (tls::upstream_tls_enabled(config->conn) && !config->tls.ocsp.disabled) {
     if (config->tls.ocsp.startup) {
-      conn_handler.set_enable_acceptor_on_ocsp_completion(true);
-      conn_handler.disable_acceptor();
+      conn_handler->set_enable_acceptor_on_ocsp_completion(true);
+      conn_handler->disable_acceptor();
     }
 
-    conn_handler.proceed_next_cert_ocsp();
+    conn_handler->proceed_next_cert_ocsp();
   }
 
   if (LOG_ENABLED(INFO)) {
@@ -559,27 +554,29 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
 
   ev_run(loop, 0);
 
-  conn_handler.cancel_ocsp_update();
+  conn_handler->cancel_ocsp_update();
+
+  // Destroy SSL_CTX held in conn_handler before killing neverbleed
+  // daemon.  Otherwise priv_rsa_finish yields "write error" and
+  // worker process aborts.
+  conn_handler.reset();
 
 #ifdef HAVE_NEVERBLEED
-  if (nb) {
-    assert(nb->daemon_pid > 0);
+  assert(nb->daemon_pid > 0);
 
-    rv = kill(nb->daemon_pid, SIGTERM);
-    if (rv != 0) {
-      auto error = errno;
-      LOG(ERROR) << "Could not send signal to neverbleed daemon: errno="
-                 << error;
-    }
+  rv = kill(nb->daemon_pid, SIGTERM);
+  if (rv != 0) {
+    auto error = errno;
+    LOG(ERROR) << "Could not send signal to neverbleed daemon: errno=" << error;
+  }
 
-    while ((rv = waitpid(nb->daemon_pid, nullptr, 0)) == -1 && errno == EINTR)
-      ;
-    if (rv == -1) {
-      auto error = errno;
-      LOG(ERROR) << "Error occurred while we were waiting for the completion "
-                    "of neverbleed process: errno="
-                 << error;
-    }
+  while ((rv = waitpid(nb->daemon_pid, nullptr, 0)) == -1 && errno == EINTR)
+    ;
+  if (rv == -1) {
+    auto error = errno;
+    LOG(ERROR) << "Error occurred while we were waiting for the completion "
+                  "of neverbleed process: errno="
+               << error;
   }
 #endif // HAVE_NEVERBLEED
 
index d907b32..7c5c329 100644 (file)
@@ -25,7 +25,7 @@
 #include "shrpx_worker_test.h"
 
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 
 #include <cstdlib>
index c9bdfe0..8ffa2f1 100644 (file)
@@ -26,7 +26,7 @@
 #define SHRPX_WORKER_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif // HAVE_CONFIG_H
 
 namespace shrpx {
index 22077b1..9574b7c 100644 (file)
  */
 #ifndef OPENSSL_COMPAT_H
 
-#include <openssl/opensslv.h>
+#  include <openssl/opensslv.h>
 
-#if defined(LIBRESSL_VERSION_NUMBER)
-#define LIBRESSL_IN_USE 1
-#else // !defined(LIBRESSL_VERSION_NUMBER)
-#define LIBRESSL_IN_USE 0
-#endif // !defined(LIBRESSL_VERSION_NUMBER)
-
-#define OPENSSL_1_1_API                                                        \
-  (!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x1010000fL)
-
-#define OPENSSL_1_1_1_API                                                      \
-  (!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10101000L)
+#  if defined(LIBRESSL_VERSION_NUMBER)
+#    define OPENSSL_1_1_API 0
+#    define OPENSSL_1_1_1_API 0
+#    define LIBRESSL_IN_USE 1
+#    define LIBRESSL_LEGACY_API (LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#    define LIBRESSL_2_7_API (LIBRESSL_VERSION_NUMBER >= 0x20700000L)
+#  else // !defined(LIBRESSL_VERSION_NUMBER)
+#    define OPENSSL_1_1_API (OPENSSL_VERSION_NUMBER >= 0x1010000fL)
+#    define OPENSSL_1_1_1_API (OPENSSL_VERSION_NUMBER >= 0x10101000L)
+#    define LIBRESSL_IN_USE 0
+#    define LIBRESSL_LEGACY_API 0
+#    define LIBRESSL_2_7_API 0
+#  endif // !defined(LIBRESSL_VERSION_NUMBER)
 
 #endif // OPENSSL_COMPAT_H
index 1a5a85b..2c1448f 100644 (file)
@@ -26,7 +26,7 @@
 #define TEMPLATE_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif // HAVE_CONFIG_H
 
 namespace nghttp2 {
index 4383fda..56f9cc6 100644 (file)
@@ -26,7 +26,7 @@
 #define TIMEGM_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #ifdef __cplusplus
@@ -34,7 +34,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 #ifdef HAVE_TIME_H
-#include <time.h>
+#  include <time.h>
 #endif // HAVE_TIME_H
 
 time_t nghttp2_timegm(struct tm *tm);
index aca3802..d6c10db 100644 (file)
--- a/src/tls.h
+++ b/src/tls.h
@@ -31,6 +31,8 @@
 
 #include <openssl/ssl.h>
 
+#include "ssl_compat.h"
+
 namespace nghttp2 {
 
 namespace tls {
@@ -48,30 +50,20 @@ public:
 // mozilla.
 //
 // https://wiki.mozilla.org/Security/Server_Side_TLS
-//
-// Plus TLSv1.3 cipher suites if defined.
 constexpr char DEFAULT_CIPHER_LIST[] =
-#ifdef TLS1_3_TXT_AES_256_GCM_SHA384
-    TLS1_3_TXT_AES_256_GCM_SHA384
-    ":"
-#endif // TLS1_3_TXT_AES_256_GCM_SHA384
-#ifdef TLS1_3_TXT_CHACHA20_POLY1305_SHA256
-    TLS1_3_TXT_CHACHA20_POLY1305_SHA256 ":"
-#endif // TLS1_3_TXT_CHACHA20_POLY1305_SHA256
-#ifdef TLS1_3_TXT_AES_128_GCM_SHA256
-    TLS1_3_TXT_AES_128_GCM_SHA256 ":"
-#endif // TLS1_3_TXT_AES_128_GCM_SHA256
-#ifdef TLS1_3_TXT_AES_128_CCM_SHA256
-    TLS1_3_TXT_AES_128_CCM_SHA256 ":"
-#endif // TLS1_3_TXT_AES_128_CCM_SHA256
-#ifdef TLS1_3_TXT_AES_128_CCM_8_SHA256
-    TLS1_3_TXT_AES_128_CCM_8_SHA256 ":"
-#endif // TLS1_3_TXT_AES_128_CCM_8_SHA256
     "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-"
     "CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-"
     "SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-"
     "AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256";
 
+constexpr char DEFAULT_TLS13_CIPHER_LIST[] =
+#if OPENSSL_1_1_1_API
+    TLS_DEFAULT_CIPHERSUITES
+#else  // !OPENSSL_1_1_1_API
+    ""
+#endif // !OPENSSL_1_1_1_API
+    ;
+
 constexpr auto NGHTTP2_TLS_MIN_VERSION = TLS1_VERSION;
 #ifdef TLS1_3_VERSION
 constexpr auto NGHTTP2_TLS_MAX_VERSION = TLS1_3_VERSION;
index 485163f..b3c8de5 100644 (file)
 #include "util.h"
 
 #ifdef HAVE_TIME_H
-#include <time.h>
+#  include <time.h>
 #endif // HAVE_TIME_H
 #include <sys/types.h>
 #ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
+#  include <sys/socket.h>
 #endif // HAVE_SYS_SOCKET_H
 #ifdef HAVE_NETDB_H
-#include <netdb.h>
+#  include <netdb.h>
 #endif // HAVE_NETDB_H
 #include <sys/stat.h>
 #ifdef HAVE_FCNTL_H
-#include <fcntl.h>
+#  include <fcntl.h>
 #endif // HAVE_FCNTL_H
 #ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
+#  include <netinet/in.h>
 #endif // HAVE_NETINET_IN_H
 #ifdef _WIN32
-#include <ws2tcpip.h>
-#include <boost/date_time/posix_time/posix_time.hpp>
+#  include <ws2tcpip.h>
+#  include <boost/date_time/posix_time/posix_time.hpp>
 #else // !_WIN32
-#include <netinet/tcp.h>
+#  include <netinet/tcp.h>
 #endif // !_WIN32
 #ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
+#  include <arpa/inet.h>
 #endif // HAVE_ARPA_INET_H
 
 #include <cmath>
@@ -80,9 +80,9 @@ int nghttp2_inet_pton(int af, const char *src, void *dst) {
 namespace {
 // inet_pton-wrapper for Windows
 int nghttp2_inet_pton(int af, const char *src, void *dst) {
-#if _WIN32_WINNT >= 0x0600
+#  if _WIN32_WINNT >= 0x0600
   return InetPtonA(af, src, dst);
-#else
+#  else
   // the function takes a 'char*', so we need to make a copy
   char addr[INET6_ADDRSTRLEN + 1];
   strncpy(addr, src, sizeof(addr));
@@ -93,7 +93,7 @@ int nghttp2_inet_pton(int af, const char *src, void *dst) {
   if (WSAStringToAddress(addr, af, NULL, (LPSOCKADDR)dst, &size) == 0)
     return 1;
   return 0;
-#endif
+#  endif
 }
 } // namespace
 #endif // _WIN32
@@ -1453,7 +1453,8 @@ void EVP_MD_CTX_free(EVP_MD_CTX *ctx) { EVP_MD_CTX_destroy(ctx); }
 } // namespace
 #endif // !OPENSSL_1_1_API
 
-int sha256(uint8_t *res, const StringRef &s) {
+namespace {
+int message_digest(uint8_t *res, const EVP_MD *meth, const StringRef &s) {
   int rv;
 
   auto ctx = EVP_MD_CTX_new();
@@ -1463,7 +1464,7 @@ int sha256(uint8_t *res, const StringRef &s) {
 
   auto ctx_deleter = defer(EVP_MD_CTX_free, ctx);
 
-  rv = EVP_DigestInit_ex(ctx, EVP_sha256(), nullptr);
+  rv = EVP_DigestInit_ex(ctx, meth, nullptr);
   if (rv != 1) {
     return -1;
   }
@@ -1473,7 +1474,7 @@ int sha256(uint8_t *res, const StringRef &s) {
     return -1;
   }
 
-  unsigned int mdlen = 32;
+  unsigned int mdlen = EVP_MD_size(meth);
 
   rv = EVP_DigestFinal_ex(ctx, res, &mdlen);
   if (rv != 1) {
@@ -1482,6 +1483,15 @@ int sha256(uint8_t *res, const StringRef &s) {
 
   return 0;
 }
+} // namespace
+
+int sha256(uint8_t *res, const StringRef &s) {
+  return message_digest(res, EVP_sha256(), s);
+}
+
+int sha1(uint8_t *res, const StringRef &s) {
+  return message_digest(res, EVP_sha1(), s);
+}
 
 bool is_hex_string(const StringRef &s) {
   if (s.size() % 2) {
index cc85b2c..897f4ef 100644 (file)
 #include "nghttp2_config.h"
 
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif // HAVE_UNISTD_H
 #include <getopt.h>
 #ifdef HAVE_NETDB_H
-#include <netdb.h>
+#  include <netdb.h>
 #endif // HAVE_NETDB_H
 
 #include <cmath>
@@ -370,14 +370,12 @@ template <typename T> std::string utos(T n) {
     res = "0";
     return res;
   }
-  int i = 0;
-  T t = n;
-  for (; t; t /= 10, ++i)
+  size_t nlen = 0;
+  for (auto t = n; t; t /= 10, ++nlen)
     ;
-  res.resize(i);
-  --i;
-  for (; n; --i, n /= 10) {
-    res[i] = (n % 10) + '0';
+  res.resize(nlen);
+  for (; n; n /= 10) {
+    res[--nlen] = (n % 10) + '0';
   }
   return res;
 }
@@ -387,15 +385,13 @@ template <typename T, typename OutputIt> OutputIt utos(OutputIt dst, T n) {
     *dst++ = '0';
     return dst;
   }
-  int i = 0;
-  T t = n;
-  for (; t; t /= 10, ++i)
+  size_t nlen = 0;
+  for (auto t = n; t; t /= 10, ++nlen)
     ;
-  --i;
-  auto p = dst + i;
-  auto res = p + 1;
-  for (; n; --i, n /= 10) {
-    *p-- = (n % 10) + '0';
+  auto p = dst + nlen;
+  auto res = p;
+  for (; n; n /= 10) {
+    *--p = (n % 10) + '0';
   }
   return res;
 }
@@ -743,6 +739,13 @@ OutputIt random_alpha_digit(OutputIt first, OutputIt last, Generator &gen) {
   return first;
 }
 
+// Fills random bytes to the range [|first|, |last|).
+template <typename OutputIt, typename Generator>
+void random_bytes(OutputIt first, OutputIt last, Generator &gen) {
+  std::uniform_int_distribution<> dis(0, 255);
+  std::generate(first, last, [&dis, &gen]() { return dis(gen); });
+}
+
 template <typename OutputIterator, typename CharT, size_t N>
 OutputIterator copy_lit(OutputIterator it, CharT (&s)[N]) {
   return std::copy_n(s, N - 1, it);
@@ -757,6 +760,10 @@ uint32_t hash32(const StringRef &s);
 // returns 0 if it succeeds, or -1.
 int sha256(uint8_t *buf, const StringRef &s);
 
+// Computes SHA-1 of |s|, and stores it in |buf|.  This function
+// returns 0 if it succeeds, or -1.
+int sha1(uint8_t *buf, const StringRef &s);
+
 // Returns host from |hostport|.  If host cannot be found in
 // |hostport|, returns empty string.  The returned string might not be
 // NULL-terminated.
index bfc934c..41ba9fe 100644 (file)
@@ -26,7 +26,7 @@
 #define UTIL_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif // HAVE_CONFIG_H
 
 namespace shrpx {
index 1796b7d..d008d4e 100644 (file)
 
 /* Make sure that we get XSI-compliant version of strerror_r */
 #ifdef _POSIX_C_SOURCE
-#undef _POSIX_C_SOURCE
+#  undef _POSIX_C_SOURCE
 #endif /* _POSIX_C_SOURCE */
 
 #ifdef _GNU_SOURCE
-#undef _GNU_SOURCE
+#  undef _GNU_SOURCE
 #endif /* _GNU_SOURCE */
 
 #include <string.h>
index 9bb6165..32cadc3 100644 (file)
@@ -26,7 +26,7 @@
 #define XSI_STRERROR_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <stddef.h>
index 8e575b0..b8521a4 100755 (executable)
@@ -1,9 +1,9 @@
 #! /bin/sh
 # test-driver - basic testsuite driver script.
 
-scriptversion=2013-07-13.22; # UTC
+scriptversion=2018-03-07.03; # UTC
 
-# Copyright (C) 2011-2014 Free Software Foundation, Inc.
+# Copyright (C) 2011-2018 Free Software Foundation, Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@ scriptversion=2013-07-13.22; # UTC
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 # As a special exception to the GNU General Public License, if you
 # distribute this file as part of a program that contains a
@@ -140,9 +140,9 @@ echo ":copy-in-global-log: $gcopy" >> $trs_file
 # Local Variables:
 # mode: shell-script
 # sh-indentation: 2
-# eval: (add-hook 'write-file-hooks 'time-stamp)
+# eval: (add-hook 'before-save-hook 'time-stamp)
 # time-stamp-start: "scriptversion="
 # time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-time-zone: "UTC"
+# time-stamp-time-zone: "UTC0"
 # time-stamp-end: "; # UTC"
 # End:
index 9c6e655..dcf0848 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -196,7 +196,19 @@ am__v_at_0 = @
 am__v_at_1 = 
 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/failmalloc.Po \
+       ./$(DEPDIR)/failmalloc_test.Po ./$(DEPDIR)/main.Po \
+       ./$(DEPDIR)/malloc_wrapper.Po ./$(DEPDIR)/nghttp2_buf_test.Po \
+       ./$(DEPDIR)/nghttp2_frame_test.Po \
+       ./$(DEPDIR)/nghttp2_hd_test.Po \
+       ./$(DEPDIR)/nghttp2_helper_test.Po \
+       ./$(DEPDIR)/nghttp2_map_test.Po \
+       ./$(DEPDIR)/nghttp2_npn_test.Po ./$(DEPDIR)/nghttp2_pq_test.Po \
+       ./$(DEPDIR)/nghttp2_queue_test.Po \
+       ./$(DEPDIR)/nghttp2_session_test.Po \
+       ./$(DEPDIR)/nghttp2_stream_test.Po \
+       ./$(DEPDIR)/nghttp2_test_helper.Po
 am__mv = mv -f
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
        $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -238,7 +250,7 @@ am__recursive_targets = \
   $(RECURSIVE_CLEAN_TARGETS) \
   $(am__extra_recursive_targets)
 AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
-       check recheck distdir
+       check recheck distdir distdir-am
 am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
 # Read a list of newline-separated strings from the standard input,
 # and print each of them once, without duplicates.  Input order is
@@ -738,8 +750,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
          *config.status*) \
            cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
          *) \
-           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
-           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
        esac;
 
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -774,21 +786,27 @@ mostlyclean-compile:
 distclean-compile:
        -rm -f *.tab.c
 
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/failmalloc.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/failmalloc_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/malloc_wrapper.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_buf_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_frame_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_helper_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_map_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_npn_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_pq_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_queue_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_session_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_stream_test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_test_helper.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/failmalloc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/failmalloc_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/malloc_wrapper.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_buf_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_frame_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_hd_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_helper_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_map_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_npn_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_pq_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_queue_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_session_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_stream_test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nghttp2_test_helper.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+       @$(MKDIR_P) $(@D)
+       @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
 
 .c.o:
 @am__fastdepCC_TRUE@   $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
@@ -1039,7 +1057,7 @@ $(TEST_SUITE_LOG): $(TEST_LOGS)
        fi;                                                             \
        $$success || exit 1
 
-check-TESTS:
+check-TESTS: $(check_PROGRAMS)
        @list='$(RECHECK_LOGS)';           test -z "$$list" || rm -f $$list
        @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
        @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
@@ -1089,7 +1107,10 @@ failmalloc.log: failmalloc$(EXEEXT)
 @am__EXEEXT_TRUE@      $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
 @am__EXEEXT_TRUE@      "$$tst" $(AM_TESTS_FD_REDIRECT)
 
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
        @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        list='$(DISTFILES)'; \
@@ -1190,7 +1211,21 @@ clean-am: clean-checkPROGRAMS clean-generic clean-libtool \
        mostlyclean-am
 
 distclean: distclean-recursive
-       -rm -rf ./$(DEPDIR)
+               -rm -f ./$(DEPDIR)/failmalloc.Po
+       -rm -f ./$(DEPDIR)/failmalloc_test.Po
+       -rm -f ./$(DEPDIR)/main.Po
+       -rm -f ./$(DEPDIR)/malloc_wrapper.Po
+       -rm -f ./$(DEPDIR)/nghttp2_buf_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_frame_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_hd_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_helper_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_map_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_npn_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_pq_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_queue_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_session_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_stream_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_test_helper.Po
        -rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
        distclean-tags
@@ -1236,7 +1271,21 @@ install-ps-am:
 installcheck-am:
 
 maintainer-clean: maintainer-clean-recursive
-       -rm -rf ./$(DEPDIR)
+               -rm -f ./$(DEPDIR)/failmalloc.Po
+       -rm -f ./$(DEPDIR)/failmalloc_test.Po
+       -rm -f ./$(DEPDIR)/main.Po
+       -rm -f ./$(DEPDIR)/malloc_wrapper.Po
+       -rm -f ./$(DEPDIR)/nghttp2_buf_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_frame_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_hd_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_helper_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_map_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_npn_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_pq_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_queue_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_session_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_stream_test.Po
+       -rm -f ./$(DEPDIR)/nghttp2_test_helper.Po
        -rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
@@ -1257,20 +1306,20 @@ uninstall-am:
 
 .MAKE: $(am__recursive_targets) check-am install-am install-strip
 
-.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
-       check-TESTS check-am clean clean-checkPROGRAMS clean-generic \
-       clean-libtool cscopelist-am ctags ctags-am distclean \
-       distclean-compile distclean-generic distclean-libtool \
-       distclean-tags distdir dvi dvi-am html html-am info info-am \
-       install install-am install-data install-data-am install-dvi \
-       install-dvi-am install-exec install-exec-am install-html \
-       install-html-am install-info install-info-am install-man \
-       install-pdf install-pdf-am install-ps install-ps-am \
-       install-strip installcheck installcheck-am installdirs \
-       installdirs-am maintainer-clean maintainer-clean-generic \
-       mostlyclean mostlyclean-compile mostlyclean-generic \
-       mostlyclean-libtool pdf pdf-am ps ps-am recheck tags tags-am \
-       uninstall uninstall-am
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+       am--depfiles check check-TESTS check-am clean \
+       clean-checkPROGRAMS clean-generic clean-libtool cscopelist-am \
+       ctags ctags-am distclean distclean-compile distclean-generic \
+       distclean-libtool distclean-tags distdir dvi dvi-am html \
+       html-am info info-am install install-am install-data \
+       install-data-am install-dvi install-dvi-am install-exec \
+       install-exec-am install-html install-html-am install-info \
+       install-info-am install-man install-pdf install-pdf-am \
+       install-ps install-ps-am install-strip installcheck \
+       installcheck-am installdirs installdirs-am maintainer-clean \
+       maintainer-clean-generic mostlyclean mostlyclean-compile \
+       mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+       recheck tags tags-am uninstall uninstall-am
 
 .PRECIOUS: Makefile
 
index 4d7376e..4bf83ca 100644 (file)
@@ -23,7 +23,7 @@
  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <stdio.h>
@@ -43,13 +43,13 @@ int main() {
 
   /* initialize the CUnit test registry */
   if (CUE_SUCCESS != CU_initialize_registry())
-    return CU_get_error();
+    return (int)CU_get_error();
 
   /* add a suite to the registry */
   pSuite = CU_add_suite("libnghttp2_TestSuite", init_suite1, clean_suite1);
   if (NULL == pSuite) {
     CU_cleanup_registry();
-    return CU_get_error();
+    return (int)CU_get_error();
   }
 
   /* add the tests to the suite */
@@ -62,7 +62,7 @@ int main() {
       !CU_add_test(pSuite, "failmalloc_frame", test_nghttp2_frame) ||
       !CU_add_test(pSuite, "failmalloc_hd", test_nghttp2_hd)) {
     CU_cleanup_registry();
-    return CU_get_error();
+    return (int)CU_get_error();
   }
 
   /* Run all tests using the CUnit Basic interface */
@@ -74,6 +74,6 @@ int main() {
     return (int)num_tests_failed;
   } else {
     printf("CUnit Error: %s\n", CU_get_error_msg());
-    return CU_get_error();
+    return (int)CU_get_error();
   }
 }
index a7ac2f0..d8a229d 100644 (file)
@@ -224,6 +224,11 @@ static void run_nghttp2_session_send_server(void) {
   ssize_t txdatalen;
   const uint8_t origin[] = "nghttp2.org";
   const uint8_t altsvc_field_value[] = "h2=\":443\"";
+  static const uint8_t nghttp2[] = "https://nghttp2.org";
+  static const nghttp2_origin_entry ov = {
+      (uint8_t *)nghttp2,
+      sizeof(nghttp2) - 1,
+  };
 
   rv = nghttp2_session_callbacks_new(&callbacks);
   if (rv != 0) {
@@ -246,6 +251,11 @@ static void run_nghttp2_session_send_server(void) {
     goto fail;
   }
 
+  rv = nghttp2_submit_origin(session, NGHTTP2_FLAG_NONE, &ov, 1);
+  if (rv != 0) {
+    goto fail;
+  }
+
   txdatalen = nghttp2_session_mem_send(session, &txdata);
 
   if (txdatalen < 0) {
index c430653..576932a 100644 (file)
@@ -26,7 +26,7 @@
 #define FAILMALLOC_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 void test_nghttp2_session_send(void);
index a922da6..13865de 100644 (file)
@@ -23,7 +23,7 @@
  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <stdio.h>
@@ -55,13 +55,13 @@ int main() {
 
   /* initialize the CUnit test registry */
   if (CUE_SUCCESS != CU_initialize_registry())
-    return CU_get_error();
+    return (int)CU_get_error();
 
   /* add a suite to the registry */
   pSuite = CU_add_suite("libnghttp2_TestSuite", init_suite1, clean_suite1);
   if (NULL == pSuite) {
     CU_cleanup_registry();
-    return CU_get_error();
+    return (int)CU_get_error();
   }
 
   /* add the tests to the suite */
@@ -87,6 +87,8 @@ int main() {
                    test_nghttp2_session_recv_continuation) ||
       !CU_add_test(pSuite, "session_recv_headers_with_priority",
                    test_nghttp2_session_recv_headers_with_priority) ||
+      !CU_add_test(pSuite, "session_recv_headers_with_padding",
+                   test_nghttp2_session_recv_headers_with_padding) ||
       !CU_add_test(pSuite, "session_recv_headers_early_response",
                    test_nghttp2_session_recv_headers_early_response) ||
       !CU_add_test(pSuite, "session_server_recv_push_response",
@@ -105,6 +107,8 @@ int main() {
                    test_nghttp2_session_recv_extension) ||
       !CU_add_test(pSuite, "session_recv_altsvc",
                    test_nghttp2_session_recv_altsvc) ||
+      !CU_add_test(pSuite, "session_recv_origin",
+                   test_nghttp2_session_recv_origin) ||
       !CU_add_test(pSuite, "session_continue", test_nghttp2_session_continue) ||
       !CU_add_test(pSuite, "session_add_frame",
                    test_nghttp2_session_add_frame) ||
@@ -204,6 +208,7 @@ int main() {
                    test_nghttp2_submit_invalid_nv) ||
       !CU_add_test(pSuite, "submit_extension", test_nghttp2_submit_extension) ||
       !CU_add_test(pSuite, "submit_altsvc", test_nghttp2_submit_altsvc) ||
+      !CU_add_test(pSuite, "submit_origin", test_nghttp2_submit_origin) ||
       !CU_add_test(pSuite, "session_open_stream",
                    test_nghttp2_session_open_stream) ||
       !CU_add_test(pSuite, "session_open_stream_with_idle_stream_dep",
@@ -314,6 +319,8 @@ int main() {
                    test_nghttp2_session_pause_data) ||
       !CU_add_test(pSuite, "session_no_closed_streams",
                    test_nghttp2_session_no_closed_streams) ||
+      !CU_add_test(pSuite, "session_set_stream_user_data",
+                   test_nghttp2_session_set_stream_user_data) ||
       !CU_add_test(pSuite, "http_mandatory_headers",
                    test_nghttp2_http_mandatory_headers) ||
       !CU_add_test(pSuite, "http_content_length",
@@ -353,6 +360,8 @@ int main() {
                    test_nghttp2_frame_pack_window_update) ||
       !CU_add_test(pSuite, "frame_pack_altsvc",
                    test_nghttp2_frame_pack_altsvc) ||
+      !CU_add_test(pSuite, "frame_pack_origin",
+                   test_nghttp2_frame_pack_origin) ||
       !CU_add_test(pSuite, "nv_array_copy", test_nghttp2_nv_array_copy) ||
       !CU_add_test(pSuite, "iv_check", test_nghttp2_iv_check) ||
       !CU_add_test(pSuite, "hd_deflate", test_nghttp2_hd_deflate) ||
@@ -409,7 +418,7 @@ int main() {
                    test_nghttp2_bufs_next_present) ||
       !CU_add_test(pSuite, "bufs_realloc", test_nghttp2_bufs_realloc)) {
     CU_cleanup_registry();
-    return CU_get_error();
+    return (int)CU_get_error();
   }
 
   /* Run all tests using the CUnit Basic interface */
@@ -421,6 +430,6 @@ int main() {
     return (int)num_tests_failed;
   } else {
     printf("CUnit Error: %s\n", CU_get_error_msg());
-    return CU_get_error();
+    return (int)CU_get_error();
   }
 }
index 62369d6..a3a3dd7 100644 (file)
@@ -26,7 +26,7 @@
 #define MALLOC_WRAPPER_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include <stdlib.h>
index c37e515..714b89f 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_BUF_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 void test_nghttp2_bufs_add(void);
index e1064e0..de40931 100644 (file)
@@ -508,6 +508,98 @@ void test_nghttp2_frame_pack_altsvc(void) {
   nghttp2_bufs_free(&bufs);
 }
 
+void test_nghttp2_frame_pack_origin(void) {
+  nghttp2_extension frame, oframe;
+  nghttp2_ext_origin origin, oorigin;
+  nghttp2_bufs bufs;
+  nghttp2_buf *buf;
+  int rv;
+  size_t payloadlen;
+  static const uint8_t example[] = "https://example.com";
+  static const uint8_t nghttp2[] = "https://nghttp2.org";
+  nghttp2_origin_entry ov[] = {
+      {
+          (uint8_t *)example,
+          sizeof(example) - 1,
+      },
+      {
+          NULL,
+          0,
+      },
+      {
+          (uint8_t *)nghttp2,
+          sizeof(nghttp2) - 1,
+      },
+  };
+  nghttp2_mem *mem;
+
+  mem = nghttp2_mem_default();
+
+  frame_pack_bufs_init(&bufs);
+
+  frame.payload = &origin;
+  oframe.payload = &oorigin;
+
+  nghttp2_frame_origin_init(&frame, ov, 3);
+
+  payloadlen = 2 + sizeof(example) - 1 + 2 + 2 + sizeof(nghttp2) - 1;
+
+  rv = nghttp2_frame_pack_origin(&bufs, &frame);
+
+  CU_ASSERT(0 == rv);
+  CU_ASSERT(NGHTTP2_FRAME_HDLEN + payloadlen == nghttp2_bufs_len(&bufs));
+
+  rv = unpack_framebuf((nghttp2_frame *)&oframe, &bufs);
+
+  CU_ASSERT(0 == rv);
+
+  check_frame_header(payloadlen, NGHTTP2_ORIGIN, NGHTTP2_FLAG_NONE, 0,
+                     &oframe.hd);
+
+  CU_ASSERT(2 == oorigin.nov);
+  CU_ASSERT(sizeof(example) - 1 == oorigin.ov[0].origin_len);
+  CU_ASSERT(0 == memcmp(example, oorigin.ov[0].origin, sizeof(example) - 1));
+  CU_ASSERT(sizeof(nghttp2) - 1 == oorigin.ov[1].origin_len);
+  CU_ASSERT(0 == memcmp(nghttp2, oorigin.ov[1].origin, sizeof(nghttp2) - 1));
+
+  nghttp2_frame_origin_free(&oframe, mem);
+
+  /* Check the case where origin length is too large */
+  buf = &bufs.head->buf;
+  nghttp2_put_uint16be(buf->pos + NGHTTP2_FRAME_HDLEN,
+                       (uint16_t)(payloadlen - 1));
+
+  rv = unpack_framebuf((nghttp2_frame *)&oframe, &bufs);
+
+  CU_ASSERT(NGHTTP2_ERR_FRAME_SIZE_ERROR == rv);
+
+  nghttp2_bufs_reset(&bufs);
+  memset(&oframe, 0, sizeof(oframe));
+  memset(&oorigin, 0, sizeof(oorigin));
+  oframe.payload = &oorigin;
+
+  /* Empty ORIGIN frame */
+  nghttp2_frame_origin_init(&frame, NULL, 0);
+
+  rv = nghttp2_frame_pack_origin(&bufs, &frame);
+
+  CU_ASSERT(0 == rv);
+  CU_ASSERT(NGHTTP2_FRAME_HDLEN == nghttp2_bufs_len(&bufs));
+
+  rv = unpack_framebuf((nghttp2_frame *)&oframe, &bufs);
+
+  CU_ASSERT(0 == rv);
+
+  check_frame_header(0, NGHTTP2_ORIGIN, NGHTTP2_FLAG_NONE, 0, &oframe.hd);
+
+  CU_ASSERT(0 == oorigin.nov);
+  CU_ASSERT(NULL == oorigin.ov);
+
+  nghttp2_frame_origin_free(&oframe, mem);
+
+  nghttp2_bufs_free(&bufs);
+}
+
 void test_nghttp2_nv_array_copy(void) {
   nghttp2_nv *nva;
   ssize_t rv;
index 059b20b..488d710 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_FRAME_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 void test_nghttp2_frame_pack_headers(void);
@@ -39,6 +39,7 @@ void test_nghttp2_frame_pack_ping(void);
 void test_nghttp2_frame_pack_goaway(void);
 void test_nghttp2_frame_pack_window_update(void);
 void test_nghttp2_frame_pack_altsvc(void);
+void test_nghttp2_frame_pack_origin(void);
 void test_nghttp2_nv_array_copy(void);
 void test_nghttp2_iv_check(void);
 
index ab5fcae..858afe6 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_HD_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 void test_nghttp2_hd_deflate(void);
index 68bdb6f..cca8122 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_HELPER_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 void test_nghttp2_adjust_local_window_size(void);
index 9f2770a..62ae54b 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_MAP_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 void test_nghttp2_map(void);
index 9d85e64..f1c9763 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_NPN_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 void test_nghttp2_npn(void);
index 2ce1661..969662a 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_PQ_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 void test_nghttp2_pq(void);
index eb4d529..64f8ce8 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_QUEUE_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 void test_nghttp2_queue(void);
index 783b0ed..296fb9d 100644 (file)
@@ -1575,6 +1575,94 @@ void test_nghttp2_session_recv_headers_with_priority(void) {
   nghttp2_session_del(session);
 }
 
+void test_nghttp2_session_recv_headers_with_padding(void) {
+  nghttp2_session *session;
+  nghttp2_session_callbacks callbacks;
+  nghttp2_bufs bufs;
+  nghttp2_buf *buf;
+  nghttp2_frame_hd hd;
+  nghttp2_outbound_item *item;
+  my_user_data ud;
+  ssize_t rv;
+
+  frame_pack_bufs_init(&bufs);
+
+  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
+  callbacks.on_frame_recv_callback = on_frame_recv_callback;
+  callbacks.send_callback = null_send_callback;
+
+  /* HEADERS: Wrong padding length */
+  nghttp2_session_server_new(&session, &callbacks, &ud);
+  nghttp2_session_send(session);
+
+  nghttp2_frame_hd_init(&hd, 10, NGHTTP2_HEADERS,
+                        NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY |
+                            NGHTTP2_FLAG_PADDED,
+                        1);
+  buf = &bufs.head->buf;
+  nghttp2_frame_pack_frame_hd(buf->last, &hd);
+  buf->last += NGHTTP2_FRAME_HDLEN;
+  /* padding is 6 bytes */
+  *buf->last++ = 5;
+  /* priority field */
+  nghttp2_put_uint32be(buf->last, 3);
+  buf->last += sizeof(uint32_t);
+  *buf->last++ = 1;
+  /* rest is garbage */
+  memset(buf->last, 0, 4);
+  buf->last += 4;
+
+  ud.frame_recv_cb_called = 0;
+
+  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
+
+  CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
+  CU_ASSERT(0 == ud.frame_recv_cb_called);
+
+  item = nghttp2_session_get_next_ob_item(session);
+
+  CU_ASSERT(NULL != item);
+  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
+
+  nghttp2_bufs_reset(&bufs);
+  nghttp2_session_del(session);
+
+  /* PUSH_PROMISE: Wrong padding length */
+  nghttp2_session_client_new(&session, &callbacks, &ud);
+  nghttp2_session_send(session);
+
+  open_sent_stream(session, 1);
+
+  nghttp2_frame_hd_init(&hd, 9, NGHTTP2_PUSH_PROMISE,
+                        NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PADDED, 1);
+  buf = &bufs.head->buf;
+  nghttp2_frame_pack_frame_hd(buf->last, &hd);
+  buf->last += NGHTTP2_FRAME_HDLEN;
+  /* padding is 6 bytes */
+  *buf->last++ = 5;
+  /* promised stream ID field */
+  nghttp2_put_uint32be(buf->last, 2);
+  buf->last += sizeof(uint32_t);
+  /* rest is garbage */
+  memset(buf->last, 0, 4);
+  buf->last += 4;
+
+  ud.frame_recv_cb_called = 0;
+
+  rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
+
+  CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
+  CU_ASSERT(0 == ud.frame_recv_cb_called);
+
+  item = nghttp2_session_get_next_ob_item(session);
+
+  CU_ASSERT(NULL != item);
+  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
+
+  nghttp2_bufs_free(&bufs);
+  nghttp2_session_del(session);
+}
+
 static int response_on_begin_frame_callback(nghttp2_session *session,
                                             const nghttp2_frame_hd *hd,
                                             void *user_data) {
@@ -2348,6 +2436,153 @@ void test_nghttp2_session_recv_altsvc(void) {
   nghttp2_option_del(option);
 }
 
+void test_nghttp2_session_recv_origin(void) {
+  nghttp2_session *session;
+  nghttp2_session_callbacks callbacks;
+  my_user_data ud;
+  nghttp2_bufs bufs;
+  ssize_t rv;
+  nghttp2_option *option;
+  nghttp2_extension frame;
+  nghttp2_ext_origin origin;
+  nghttp2_origin_entry ov;
+  static const uint8_t nghttp2[] = "https://nghttp2.org";
+
+  frame_pack_bufs_init(&bufs);
+
+  frame.payload = &origin;
+
+  ov.origin = (uint8_t *)nghttp2;
+  ov.origin_len = sizeof(nghttp2) - 1;
+
+  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
+
+  callbacks.on_frame_recv_callback = on_frame_recv_callback;
+
+  nghttp2_option_new(&option);
+  nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ORIGIN);
+
+  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
+
+  nghttp2_frame_origin_init(&frame, &ov, 1);
+
+  rv = nghttp2_frame_pack_origin(&bufs, &frame);
+
+  CU_ASSERT(0 == rv);
+
+  ud.frame_recv_cb_called = 0;
+  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
+                                nghttp2_bufs_len(&bufs));
+
+  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
+  CU_ASSERT(1 == ud.frame_recv_cb_called);
+  CU_ASSERT(NGHTTP2_ORIGIN == ud.recv_frame_hd.type);
+  CU_ASSERT(NGHTTP2_FLAG_NONE == ud.recv_frame_hd.flags);
+  CU_ASSERT(0 == ud.recv_frame_hd.stream_id);
+
+  nghttp2_session_del(session);
+  nghttp2_bufs_reset(&bufs);
+
+  /* The length of origin is larger than payload length. */
+  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
+
+  nghttp2_frame_origin_init(&frame, &ov, 1);
+  rv = nghttp2_frame_pack_origin(&bufs, &frame);
+
+  CU_ASSERT(0 == rv);
+
+  nghttp2_put_uint16be(bufs.head->buf.pos + NGHTTP2_FRAME_HDLEN,
+                       (uint16_t)sizeof(nghttp2));
+
+  ud.frame_recv_cb_called = 0;
+  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
+                                nghttp2_bufs_len(&bufs));
+
+  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
+  CU_ASSERT(0 == ud.frame_recv_cb_called);
+
+  nghttp2_session_del(session);
+  nghttp2_bufs_reset(&bufs);
+
+  /* A frame should be ignored if it is sent to a stream other than
+     stream 0. */
+  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
+
+  nghttp2_frame_origin_init(&frame, &ov, 1);
+  frame.hd.stream_id = 1;
+  rv = nghttp2_frame_pack_origin(&bufs, &frame);
+
+  CU_ASSERT(0 == rv);
+
+  ud.frame_recv_cb_called = 0;
+  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
+                                nghttp2_bufs_len(&bufs));
+
+  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
+  CU_ASSERT(0 == ud.frame_recv_cb_called);
+
+  nghttp2_session_del(session);
+  nghttp2_bufs_reset(&bufs);
+
+  /* A frame should be ignored if the reserved flag is set */
+  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
+
+  nghttp2_frame_origin_init(&frame, &ov, 1);
+  frame.hd.flags = 0xf0;
+  rv = nghttp2_frame_pack_origin(&bufs, &frame);
+
+  CU_ASSERT(0 == rv);
+
+  ud.frame_recv_cb_called = 0;
+  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
+                                nghttp2_bufs_len(&bufs));
+
+  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
+  CU_ASSERT(0 == ud.frame_recv_cb_called);
+
+  nghttp2_session_del(session);
+  nghttp2_bufs_reset(&bufs);
+
+  /* A frame should be ignored if it is received by a server. */
+  nghttp2_session_server_new2(&session, &callbacks, &ud, option);
+
+  nghttp2_frame_origin_init(&frame, &ov, 1);
+  rv = nghttp2_frame_pack_origin(&bufs, &frame);
+
+  CU_ASSERT(0 == rv);
+
+  ud.frame_recv_cb_called = 0;
+  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
+                                nghttp2_bufs_len(&bufs));
+
+  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
+  CU_ASSERT(0 == ud.frame_recv_cb_called);
+
+  nghttp2_session_del(session);
+  nghttp2_bufs_reset(&bufs);
+
+  /* Receiving empty ORIGIN frame */
+  nghttp2_session_client_new2(&session, &callbacks, &ud, option);
+
+  nghttp2_frame_origin_init(&frame, NULL, 0);
+  rv = nghttp2_frame_pack_origin(&bufs, &frame);
+
+  CU_ASSERT(0 == rv);
+
+  ud.frame_recv_cb_called = 0;
+  rv = nghttp2_session_mem_recv(session, bufs.head->buf.pos,
+                                nghttp2_bufs_len(&bufs));
+
+  CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) == rv);
+  CU_ASSERT(1 == ud.frame_recv_cb_called);
+  CU_ASSERT(NGHTTP2_ORIGIN == ud.recv_frame_hd.type);
+
+  nghttp2_session_del(session);
+
+  nghttp2_option_del(option);
+  nghttp2_bufs_free(&bufs);
+}
+
 void test_nghttp2_session_continue(void) {
   nghttp2_session *session;
   nghttp2_session_callbacks callbacks;
@@ -3059,6 +3294,7 @@ void test_nghttp2_session_on_settings_received(void) {
   nghttp2_outbound_item *item;
   nghttp2_nv nv = MAKE_NV(":authority", "example.org");
   nghttp2_mem *mem;
+  nghttp2_option *option;
 
   mem = nghttp2_mem_default();
 
@@ -3171,6 +3407,23 @@ void test_nghttp2_session_on_settings_received(void) {
   nghttp2_frame_settings_free(&frame.settings, mem);
   nghttp2_session_del(session);
 
+  /* Check that remote SETTINGS_MAX_CONCURRENT_STREAMS is set to a value set by
+     nghttp2_option_set_peer_max_concurrent_streams() and reset to the default
+     value (unlimited) after receiving initial SETTINGS frame from the peer. */
+  nghttp2_option_new(&option);
+  nghttp2_option_set_peer_max_concurrent_streams(option, 1000);
+  nghttp2_session_client_new2(&session, &callbacks, NULL, option);
+  CU_ASSERT(1000 == session->remote_settings.max_concurrent_streams);
+
+  nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, NULL, 0);
+  CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0));
+  CU_ASSERT(NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS ==
+            session->remote_settings.max_concurrent_streams);
+
+  nghttp2_frame_settings_free(&frame.settings, mem);
+  nghttp2_session_del(session);
+  nghttp2_option_del(option);
+
   /* Check too large SETTINGS_MAX_FRAME_SIZE */
   nghttp2_session_server_new(&session, &callbacks, NULL);
 
@@ -3227,6 +3480,29 @@ void test_nghttp2_session_on_settings_received(void) {
   CU_ASSERT(NGHTTP2_STREAM_CLOSING == stream1->state);
 
   nghttp2_session_del(session);
+
+  /* It is invalid that peer disables ENABLE_CONNECT_PROTOCOL once it
+     has been enabled. */
+  nghttp2_session_client_new(&session, &callbacks, NULL);
+
+  session->remote_settings.enable_connect_protocol = 1;
+
+  iv[0].settings_id = NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL;
+  iv[0].value = 0;
+
+  nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 1),
+                              1);
+
+  CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0));
+
+  nghttp2_frame_settings_free(&frame.settings, mem);
+
+  item = nghttp2_session_get_next_ob_item(session);
+
+  CU_ASSERT(NULL != item);
+  CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
+
+  nghttp2_session_del(session);
 }
 
 void test_nghttp2_session_on_push_promise_received(void) {
@@ -5948,6 +6224,95 @@ void test_nghttp2_submit_altsvc(void) {
   nghttp2_session_del(session);
 }
 
+void test_nghttp2_submit_origin(void) {
+  nghttp2_session *session;
+  nghttp2_session_callbacks callbacks;
+  my_user_data ud;
+  int rv;
+  ssize_t len;
+  const uint8_t *data;
+  static const uint8_t nghttp2[] = "https://nghttp2.org";
+  static const uint8_t examples[] = "https://examples.com";
+  static const nghttp2_origin_entry ov[] = {
+      {
+          (uint8_t *)nghttp2,
+          sizeof(nghttp2) - 1,
+      },
+      {
+          (uint8_t *)examples,
+          sizeof(examples) - 1,
+      },
+  };
+  nghttp2_frame frame;
+  nghttp2_ext_origin origin;
+  nghttp2_mem *mem;
+
+  mem = nghttp2_mem_default();
+
+  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
+  callbacks.on_frame_send_callback = on_frame_send_callback;
+
+  frame.ext.payload = &origin;
+
+  nghttp2_session_server_new(&session, &callbacks, &ud);
+
+  rv = nghttp2_submit_origin(session, NGHTTP2_FLAG_NONE, ov, 2);
+
+  CU_ASSERT(0 == rv);
+
+  ud.frame_send_cb_called = 0;
+  len = nghttp2_session_mem_send(session, &data);
+
+  CU_ASSERT(len > 0);
+  CU_ASSERT(1 == ud.frame_send_cb_called);
+
+  nghttp2_frame_unpack_frame_hd(&frame.hd, data);
+  rv = nghttp2_frame_unpack_origin_payload(
+      &frame.ext, data + NGHTTP2_FRAME_HDLEN, (size_t)len - NGHTTP2_FRAME_HDLEN,
+      mem);
+
+  CU_ASSERT(0 == rv);
+  CU_ASSERT(0 == frame.hd.stream_id);
+  CU_ASSERT(NGHTTP2_ORIGIN == frame.hd.type);
+  CU_ASSERT(2 == origin.nov);
+  CU_ASSERT(0 == memcmp(nghttp2, origin.ov[0].origin, sizeof(nghttp2) - 1));
+  CU_ASSERT(sizeof(nghttp2) - 1 == origin.ov[0].origin_len);
+  CU_ASSERT(0 == memcmp(examples, origin.ov[1].origin, sizeof(examples) - 1));
+  CU_ASSERT(sizeof(examples) - 1 == origin.ov[1].origin_len);
+
+  nghttp2_frame_origin_free(&frame.ext, mem);
+
+  nghttp2_session_del(session);
+
+  /* Submitting ORIGIN frame from client session is error */
+  nghttp2_session_client_new(&session, &callbacks, NULL);
+
+  rv = nghttp2_submit_origin(session, NGHTTP2_FLAG_NONE, ov, 1);
+
+  CU_ASSERT(NGHTTP2_ERR_INVALID_STATE == rv);
+
+  nghttp2_session_del(session);
+
+  /* Submitting empty ORIGIN frame */
+  nghttp2_session_server_new(&session, &callbacks, &ud);
+
+  rv = nghttp2_submit_origin(session, NGHTTP2_FLAG_NONE, NULL, 0);
+
+  CU_ASSERT(0 == rv);
+
+  ud.frame_send_cb_called = 0;
+  len = nghttp2_session_mem_send(session, &data);
+
+  CU_ASSERT(len == NGHTTP2_FRAME_HDLEN);
+  CU_ASSERT(1 == ud.frame_send_cb_called);
+
+  nghttp2_frame_unpack_frame_hd(&frame.hd, data);
+
+  CU_ASSERT(NGHTTP2_ORIGIN == frame.hd.type);
+
+  nghttp2_session_del(session);
+}
+
 void test_nghttp2_session_open_stream(void) {
   nghttp2_session *session;
   nghttp2_session_callbacks callbacks;
@@ -10354,6 +10719,39 @@ void test_nghttp2_session_no_closed_streams(void) {
   nghttp2_option_del(option);
 }
 
+void test_nghttp2_session_set_stream_user_data(void) {
+  nghttp2_session *session;
+  nghttp2_session_callbacks callbacks;
+  int32_t stream_id;
+  int user_data1, user_data2;
+  int rv;
+  const uint8_t *datap;
+  ssize_t datalen;
+
+  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
+
+  nghttp2_session_client_new(&session, &callbacks, NULL);
+
+  stream_id = nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), NULL,
+                                     &user_data1);
+
+  rv = nghttp2_session_set_stream_user_data(session, stream_id, &user_data2);
+
+  CU_ASSERT(0 == rv);
+
+  datalen = nghttp2_session_mem_send(session, &datap);
+
+  CU_ASSERT(datalen > 0);
+
+  CU_ASSERT(&user_data2 ==
+            nghttp2_session_get_stream_user_data(session, stream_id));
+
+  CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
+            nghttp2_session_set_stream_user_data(session, 2, NULL));
+
+  nghttp2_session_del(session);
+}
+
 static void check_nghttp2_http_recv_headers_fail(
     nghttp2_session *session, nghttp2_hd_deflater *deflater, int32_t stream_id,
     int stream_state, const nghttp2_nv *nva, size_t nvlen) {
@@ -10462,6 +10860,7 @@ void test_nghttp2_http_mandatory_headers(void) {
                                     MAKE_NV("content-length", "0")};
   const nghttp2_nv clnonzero204_resnv[] = {MAKE_NV(":status", "204"),
                                            MAKE_NV("content-length", "100")};
+  const nghttp2_nv status101_resnv[] = {MAKE_NV(":status", "101")};
 
   /* test case for request */
   const nghttp2_nv nopath_reqnv[] = {MAKE_NV(":scheme", "https"),
@@ -10508,6 +10907,23 @@ void test_nghttp2_http_mandatory_headers(void) {
   const nghttp2_nv asteriskoptions2_reqnv[] = {
       MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "localhost"),
       MAKE_NV(":method", "OPTIONS"), MAKE_NV(":path", "*")};
+  const nghttp2_nv connectproto_reqnv[] = {
+      MAKE_NV(":scheme", "https"), MAKE_NV(":path", "/"),
+      MAKE_NV(":method", "CONNECT"), MAKE_NV(":authority", "localhost"),
+      MAKE_NV(":protocol", "websocket")};
+  const nghttp2_nv connectprotoget_reqnv[] = {
+      MAKE_NV(":scheme", "https"), MAKE_NV(":path", "/"),
+      MAKE_NV(":method", "GET"), MAKE_NV(":authority", "localhost"),
+      MAKE_NV(":protocol", "websocket")};
+  const nghttp2_nv connectprotonopath_reqnv[] = {
+      MAKE_NV(":scheme", "https"), MAKE_NV(":method", "CONNECT"),
+      MAKE_NV(":authority", "localhost"), MAKE_NV(":protocol", "websocket")};
+  const nghttp2_nv connectprotonoauth_reqnv[] = {
+      MAKE_NV(":scheme", "http"), MAKE_NV(":path", "/"),
+      MAKE_NV(":method", "CONNECT"), MAKE_NV("host", "localhost"),
+      MAKE_NV(":protocol", "websocket")};
+  const nghttp2_nv regularconnect_reqnv[] = {
+      MAKE_NV(":method", "CONNECT"), MAKE_NV(":authority", "localhost")};
 
   mem = nghttp2_mem_default();
 
@@ -10576,6 +10992,12 @@ void test_nghttp2_http_mandatory_headers(void) {
       session, &deflater, 21, NGHTTP2_STREAM_OPENING, clnonzero204_resnv,
       ARRLEN(clnonzero204_resnv));
 
+  /* status code 101 should not be used in HTTP/2 because it is used
+     for HTTP Upgrade which HTTP/2 removes. */
+  check_nghttp2_http_recv_headers_fail(session, &deflater, 23,
+                                       NGHTTP2_STREAM_OPENING, status101_resnv,
+                                       ARRLEN(status101_resnv));
+
   nghttp2_hd_deflate_free(&deflater);
 
   nghttp2_session_del(session);
@@ -10649,6 +11071,50 @@ void test_nghttp2_http_mandatory_headers(void) {
                                      asteriskoptions2_reqnv,
                                      ARRLEN(asteriskoptions2_reqnv));
 
+  /* :protocol is not allowed unless it is enabled by the local
+     endpoint. */
+  check_nghttp2_http_recv_headers_fail(session, &deflater, 27, -1,
+                                       connectproto_reqnv,
+                                       ARRLEN(connectproto_reqnv));
+
+  nghttp2_hd_deflate_free(&deflater);
+
+  nghttp2_session_del(session);
+
+  /* enable SETTINGS_CONNECT_PROTOCOL */
+  nghttp2_session_server_new(&session, &callbacks, &ud);
+
+  session->pending_enable_connect_protocol = 1;
+
+  nghttp2_hd_deflate_init(&deflater, mem);
+
+  /* :protocol is allowed if SETTINGS_CONNECT_PROTOCOL is enabled by
+     the local endpoint. */
+  check_nghttp2_http_recv_headers_ok(session, &deflater, 1, -1,
+                                     connectproto_reqnv,
+                                     ARRLEN(connectproto_reqnv));
+
+  /* :protocol is only allowed with CONNECT method. */
+  check_nghttp2_http_recv_headers_fail(session, &deflater, 3, -1,
+                                       connectprotoget_reqnv,
+                                       ARRLEN(connectprotoget_reqnv));
+
+  /* CONNECT method with :protocol requires :path. */
+  check_nghttp2_http_recv_headers_fail(session, &deflater, 5, -1,
+                                       connectprotonopath_reqnv,
+                                       ARRLEN(connectprotonopath_reqnv));
+
+  /* CONNECT method with :protocol requires :authority. */
+  check_nghttp2_http_recv_headers_fail(session, &deflater, 7, -1,
+                                       connectprotonoauth_reqnv,
+                                       ARRLEN(connectprotonoauth_reqnv));
+
+  /* regular CONNECT method should succeed with
+     SETTINGS_CONNECT_PROTOCOL */
+  check_nghttp2_http_recv_headers_ok(session, &deflater, 9, -1,
+                                     regularconnect_reqnv,
+                                     ARRLEN(regularconnect_reqnv));
+
   nghttp2_hd_deflate_free(&deflater);
 
   nghttp2_session_del(session);
index 470b274..35a48b8 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_SESSION_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 void test_nghttp2_session_recv(void);
@@ -37,6 +37,7 @@ void test_nghttp2_session_recv_data(void);
 void test_nghttp2_session_recv_data_no_auto_flow_control(void);
 void test_nghttp2_session_recv_continuation(void);
 void test_nghttp2_session_recv_headers_with_priority(void);
+void test_nghttp2_session_recv_headers_with_padding(void);
 void test_nghttp2_session_recv_headers_early_response(void);
 void test_nghttp2_session_server_recv_push_response(void);
 void test_nghttp2_session_recv_premature_headers(void);
@@ -46,6 +47,7 @@ void test_nghttp2_session_recv_settings_header_table_size(void);
 void test_nghttp2_session_recv_too_large_frame_length(void);
 void test_nghttp2_session_recv_extension(void);
 void test_nghttp2_session_recv_altsvc(void);
+void test_nghttp2_session_recv_origin(void);
 void test_nghttp2_session_continue(void);
 void test_nghttp2_session_add_frame(void);
 void test_nghttp2_session_on_request_headers_received(void);
@@ -99,6 +101,7 @@ void test_nghttp2_submit_shutdown_notice(void);
 void test_nghttp2_submit_invalid_nv(void);
 void test_nghttp2_submit_extension(void);
 void test_nghttp2_submit_altsvc(void);
+void test_nghttp2_submit_origin(void);
 void test_nghttp2_session_open_stream(void);
 void test_nghttp2_session_open_stream_with_idle_stream_dep(void);
 void test_nghttp2_session_get_next_ob_item(void);
@@ -155,6 +158,7 @@ void test_nghttp2_session_cancel_from_before_frame_send(void);
 void test_nghttp2_session_removed_closed_stream(void);
 void test_nghttp2_session_pause_data(void);
 void test_nghttp2_session_no_closed_streams(void);
+void test_nghttp2_session_set_stream_user_data(void);
 void test_nghttp2_http_mandatory_headers(void);
 void test_nghttp2_http_content_length(void);
 void test_nghttp2_http_content_length_mismatch(void);
index 16a1f0a..ad7be64 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_STREAM_TEST_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #endif /* NGHTTP2_STREAM_TEST_H */
index b8869f5..d741d5e 100644 (file)
@@ -84,6 +84,10 @@ int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len) {
     assert(payloadlen > 2);
     nghttp2_frame_unpack_altsvc_payload2(&frame->ext, payload, payloadlen, mem);
     break;
+  case NGHTTP2_ORIGIN:
+    rv = nghttp2_frame_unpack_origin_payload(&frame->ext, payload, payloadlen,
+                                             mem);
+    break;
   default:
     /* Must not be reachable */
     assert(0);
index 135f6ed..c66298a 100644 (file)
@@ -26,7 +26,7 @@
 #define NGHTTP2_TEST_HELPER_H
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
+#  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
 #include "nghttp2_frame.h"
index 9bf74b1..a22b066 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -353,8 +353,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
          *config.status*) \
            cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
          *) \
-           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
-           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
        esac;
 
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -378,7 +378,10 @@ ctags CTAGS:
 cscope cscopelist:
 
 
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
        @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        list='$(DISTFILES)'; \
index 04cdb42..19ab4fd 100644 (file)
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -166,7 +166,9 @@ am__v_at_0 = @
 am__v_at_1 = 
 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = http-parser/$(DEPDIR)/http_parser.Plo \
+       neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Plo
 am__mv = mv -f
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
        $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -427,8 +429,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
          *config.status*) \
            cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
          *) \
-           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
-           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
        esac;
 
 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
@@ -483,8 +485,14 @@ mostlyclean-compile:
 distclean-compile:
        -rm -f *.tab.c
 
-@AMDEP_TRUE@@am__include@ @am__quote@http-parser/$(DEPDIR)/http_parser.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@http-parser/$(DEPDIR)/http_parser.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+       @$(MKDIR_P) $(@D)
+       @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
 
 .c.o:
 @am__fastdepCC_TRUE@   $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
@@ -577,7 +585,10 @@ cscopelist-am: $(am__tagged_files)
 distclean-tags:
        -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
 
-distdir: $(DISTFILES)
+distdir: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
        @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
        list='$(DISTFILES)'; \
@@ -655,7 +666,8 @@ clean-am: clean-generic clean-libtool clean-local \
        clean-noinstLTLIBRARIES mostlyclean-am
 
 distclean: distclean-am
-       -rm -rf http-parser/$(DEPDIR) neverbleed/$(DEPDIR)
+               -rm -f http-parser/$(DEPDIR)/http_parser.Plo
+       -rm -f neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Plo
        -rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
        distclean-tags
@@ -701,7 +713,8 @@ install-ps-am:
 installcheck-am:
 
 maintainer-clean: maintainer-clean-am
-       -rm -rf http-parser/$(DEPDIR) neverbleed/$(DEPDIR)
+               -rm -f http-parser/$(DEPDIR)/http_parser.Plo
+       -rm -f neverbleed/$(DEPDIR)/libneverbleed_la-neverbleed.Plo
        -rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
@@ -722,8 +735,8 @@ uninstall-am:
 
 .MAKE: install-am install-strip
 
-.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \
-       clean-generic clean-libtool clean-local \
+.PHONY: CTAGS GTAGS TAGS all all-am all-local am--depfiles check \
+       check-am clean clean-generic clean-libtool clean-local \
        clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \
        distclean-compile distclean-generic distclean-libtool \
        distclean-tags distdir dvi dvi-am html html-am info info-am \
index e3b95d7..4265f7f 100644 (file)
@@ -35,3 +35,6 @@ Original Authors "mruby developers" are:
    Terence Lee
    Zachary Scott
    Tomasz Dąbrowski
+   Christopher Aue
+   Masahiro Wakame
+   YAMAMOTO Masaya
index d02b8fe..8d8c78f 100644 (file)
@@ -1,4 +1,4 @@
-Copyright (c) 2017 mruby developers
+Copyright (c) 2018 mruby developers
 
 Permission is hereby granted, free of charge, to any person obtaining a
 copy of this software and associated documentation files (the "Software"),
index 9d0c611..529091b 100644 (file)
@@ -17,7 +17,7 @@ of the Ministry of Economy, Trade and Industry of Japan.
 
 ## How to get mruby
 
-The stable version 1.3.0 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/1.3.0.zip](https://github.com/mruby/mruby/archive/1.3.0.zip)
+The stable version 1.4.1 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/1.4.1.zip](https://github.com/mruby/mruby/archive/1.4.1.zip)
 
 The latest development version of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/zipball/master](https://github.com/mruby/mruby/zipball/master)
 
index dd14c8b..2f6fa05 100644 (file)
@@ -5,10 +5,12 @@ MRUBY_ROOT = File.dirname(File.expand_path(__FILE__))
 MRUBY_BUILD_HOST_IS_CYGWIN = RUBY_PLATFORM.include?('cygwin')
 MRUBY_BUILD_HOST_IS_OPENBSD = RUBY_PLATFORM.include?('openbsd')
 
+$LOAD_PATH << File.join(MRUBY_ROOT, "lib")
+
 # load build systems
-load "#{MRUBY_ROOT}/tasks/ruby_ext.rake"
-load "#{MRUBY_ROOT}/tasks/mruby_build.rake"
-load "#{MRUBY_ROOT}/tasks/mrbgem_spec.rake"
+require "mruby-core-ext"
+require "mruby/build"
+require "mruby/gem"
 
 # load configuration file
 MRUBY_CONFIG = (ENV['MRUBY_CONFIG'] && ENV['MRUBY_CONFIG'] != '') ? ENV['MRUBY_CONFIG'] : "#{MRUBY_ROOT}/build_config.rb"
index c62b135..b4514ec 100644 (file)
@@ -5,6 +5,10 @@ os: Visual Studio 2015
 clone_depth: 50
 
 
+cache:
+  - win_flex_bison-2.5.10.zip
+
+
 environment:
   matrix:
     # Visual Studio 2015 64bit
@@ -18,10 +22,17 @@ environment:
 
 init:
   - call "%visualcpp%" %machine%
-  # For using bison.exe
-  - set PATH=%PATH%;C:\cygwin\bin;
+  # For using Rubyinstaller's Ruby 2.4 64bit
+  - set PATH=C:\Ruby24-x64\bin;%PATH%
+  - ruby --version
+
+
+install:
+  - if not exist win_flex_bison-2.5.10.zip appveyor DownloadFile "https://github.com/lexxmark/winflexbison/releases/download/v.2.5.10/win_flex_bison-2.5.10.zip"
+  - 7z x -y -owin_flex_bison win_flex_bison-2.5.10.zip > nul
 
 
 build_script:
+  - set YACC=.\win_flex_bison\win_bison.exe
   - set MRUBY_CONFIG=appveyor_config.rb
-  - ruby .\minirake test
+  - ruby .\minirake test all
index 57e1033..2555b2f 100644 (file)
@@ -5,7 +5,7 @@ MRuby::Build.new('debug') do |conf|
   # include all core GEMs
   conf.gembox 'full-core'
   conf.compilers.each do |c|
-    c.defines += %w(MRB_GC_STRESS MRB_GC_FIXED_ARENA)
+    c.defines += %w(MRB_GC_STRESS MRB_GC_FIXED_ARENA MRB_METHOD_CACHE)
   end
 
   build_mrbc_exec
index 8212c3a..dc4388c 100644 (file)
@@ -68,7 +68,7 @@ class Vec
   def vnormalize
     len = vlength
     v = Vec.new(@x, @y, @z)
-    if len > 1.0e-17 then
+    if len > 1.0e-17
       v.x = v.x / len
       v.y = v.y / len
       v.z = v.z / len
@@ -92,10 +92,10 @@ class Sphere
     b = rs.vdot(ray.dir)
     c = rs.vdot(rs) - (@radius * @radius)
     d = b * b - c
-    if d > 0.0 then
+    if d > 0.0
       t = - b - Math.sqrt(d)
 
-      if t > 0.0 and t < isect.t then
+      if t > 0.0 and t < isect.t
         isect.t = t
         isect.hit = true
         isect.pl = Vec.new(ray.org.x + ray.dir.x * t,
@@ -118,16 +118,16 @@ class Plane
     d = -@p.vdot(@n)
     v = ray.dir.vdot(@n)
     v0 = v
-    if v < 0.0 then
+    if v < 0.0
       v0 = -v
     end
-    if v0 < 1.0e-17 then
+    if v0 < 1.0e-17
       return
     end
 
     t = -(ray.org.vdot(@n) + d) / v
 
-    if t > 0.0 and t < isect.t then
+    if t > 0.0 and t < isect.t
       isect.hit = true
       isect.t = t
       isect.n = @n
@@ -170,10 +170,10 @@ end
 
 def clamp(f)
   i = f * 255.5
-  if i > 255.0 then
+  if i > 255.0
     i = 255.0
   end
-  if i < 0.0 then
+  if i < 0.0
     i = 0.0
   end
   i.to_i
@@ -183,11 +183,11 @@ def otherBasis(basis, n)
   basis[2] = Vec.new(n.x, n.y, n.z)
   basis[1] = Vec.new(0.0, 0.0, 0.0)
 
-  if n.x < 0.6 and n.x > -0.6 then
+  if n.x < 0.6 and n.x > -0.6
     basis[1].x = 1.0
-  elsif n.y < 0.6 and n.y > -0.6 then
+  elsif n.y < 0.6 and n.y > -0.6
     basis[1].y = 1.0
-  elsif n.z < 0.6 and n.z > -0.6 then
+  elsif n.z < 0.6 and n.z > -0.6
     basis[1].z = 1.0
   else
     basis[1].x = 1.0
@@ -221,8 +221,8 @@ class Scene
     p0 = Vec.new(isect.pl.x + eps * isect.n.x,
                 isect.pl.y + eps * isect.n.y,
                 isect.pl.z + eps * isect.n.z)
-    nphi.times do |j|
-      ntheta.times do |i|
+    nphi.times do
+      ntheta.times do
         r = Rand::rand
         phi = 2.0 * 3.14159265 * Rand::rand
         x = Math.cos(phi) * Math.sqrt(1.0 - r)
@@ -241,7 +241,7 @@ class Scene
         @spheres[1].intersect(ray, occisect)
         @spheres[2].intersect(ray, occisect)
         @plane.intersect(ray, occisect)
-        if occisect.hit then
+        if occisect.hit
           occlusion = occlusion + 1.0
         else
           0.0
@@ -283,7 +283,7 @@ class Scene
             @spheres[1].intersect(ray, isect)
             @spheres[2].intersect(ray, isect)
             @plane.intersect(ray, isect)
-            if isect.hit then
+            if isect.hit
               col = ambient_occlusion(isect)
               rad.x = rad.x + col.x
               rad.y = rad.y + col.y
index e8f4a2a..512e312 100644 (file)
@@ -17,19 +17,19 @@ def test_lists()
   # li2 must now be empty
   # remove each individual item from right side of li3 and
   # append to right side of li2 (reversing list)
-  while (not li3.empty?)
+  until li3.empty?
     li2.push(li3.pop)
   end
   # li3 must now be empty
   # reverse li1 in place
   li1.reverse!
   # check that first item is now SIZE
-  if li1[0] != SIZE then
+  if li1[0] != SIZE
     p "not SIZE"
     0
   else
     # compare li1 and li2 for equality
-    if li1 != li2 then
+    if li1 != li2
       return(0)
     else
       # return the length of the list
index 8293092..1429837 100644 (file)
@@ -124,17 +124,17 @@ MRuby::Build.new('test') do |conf|
   conf.gembox 'default'
 end
 
-MRuby::Build.new('bench') do |conf|
-  # Gets set by the VS command prompts.
-  if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
-    toolchain :visualcpp
-  else
-    toolchain :gcc
-    conf.cc.flags << '-O3'
-  end
-
-  conf.gembox 'default'
-end
+#MRuby::Build.new('bench') do |conf|
+#  # Gets set by the VS command prompts.
+#  if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
+#    toolchain :visualcpp
+#  else
+#    toolchain :gcc
+#    conf.cc.flags << '-O3'
+#  end
+#
+#  conf.gembox 'default'
+#end
 
 # Define cross build settings
 # MRuby::CrossBuild.new('32bit') do |conf|
@@ -148,5 +148,4 @@ end
 #   conf.gem 'examples/mrbgems/c_and_ruby_extension_example'
 #
 #   conf.test_runner.command = 'env'
-#
 # end
index 72f2c2b..1cc7a9a 100644 (file)
@@ -38,7 +38,7 @@ To confirm mrdb was installed properly, run mrdb with the `--version` option:
 
 ```bash
 $ mrdb --version
-mruby 1.3.0 (2017-7-4)
+mruby 1.4.1 (2018-4-27)
 ```
 
 ## 2.2 Basic Operation
index d6659c8..aff7360 100644 (file)
@@ -114,14 +114,14 @@ inspect_ary(mrb_state *mrb, mrb_value ary, mrb_value list)
 
   mrb_ary_push(mrb, list, ary);
 
-  arystr = mrb_str_buf_new(mrb, 64);
-  mrb_str_buf_cat(mrb, arystr, head, sizeof(head));
+  arystr = mrb_str_new_capa(mrb, 64);
+  mrb_str_cat(mrb, arystr, head, sizeof(head));
 
   for(i=0; i<RARRAY_LEN(ary); i++) {
     int ai = mrb_gc_arena_save(mrb);
 
     if (i > 0) {
-      mrb_str_buf_cat(mrb, arystr, sep, sizeof(sep));
+      mrb_str_cat(mrb, arystr, sep, sizeof(sep));
     }
     if (mrb_array_p(RARRAY_PTR(ary)[i])) {
       s = inspect_ary(mrb, RARRAY_PTR(ary)[i], list);
@@ -129,11 +129,11 @@ inspect_ary(mrb_state *mrb, mrb_value ary, mrb_value list)
     else {
       s = mrb_inspect(mrb, RARRAY_PTR(ary)[i]);
     }
-    mrb_str_buf_cat(mrb, arystr, RSTRING_PTR(s), RSTRING_LEN(s));
+    mrb_str_cat(mrb, arystr, RSTRING_PTR(s), RSTRING_LEN(s));
     mrb_gc_arena_restore(mrb, ai);
   }
 
-  mrb_str_buf_cat(mrb, arystr, tail, sizeof(tail));
+  mrb_str_cat(mrb, arystr, tail, sizeof(tail));
   mrb_ary_pop(mrb, list);
 
   return arystr;
index 258f405..8dac0dc 100644 (file)
@@ -26,6 +26,11 @@ conf.gem :github => 'masuidrive/mrbgems-example', :branch => 'master'
 conf.gem :bitbucket => 'mruby/mrbgems-example', :branch => 'master'
 ```
 
+You can specify the sub directory of the repository with `:path` option:
+```ruby
+conf.gem github: 'mruby/mruby', path: 'mrbgems/mruby-socket'
+```
+
 To use mrbgem from [mgem-list](https://github.com/mruby/mgem-list) use `:mgem` option:
 ```ruby
 conf.gem :mgem => 'mruby-yaml'
index db8db9a..9d8924c 100644 (file)
@@ -38,7 +38,7 @@ puts [1,2,3]
 3
 ```
 
-#### mruby [1.3.0 (2017-7-4)]
+#### mruby [1.4.1 (2018-4-27)]
 
 ```
 [1, 2, 3]
@@ -61,26 +61,10 @@ end
 
 ```ZeroDivisionError``` is raised.
 
-#### mruby [1.3.0 (2017-7-4)]
+#### mruby [1.4.1 (2018-4-27)]
 
 No exception is raised.
 
-## Check of infinite recursion
-
-mruby does not check infinite recursion across C extensions.
-
-```ruby
-def test; eval 'test'; end; test
-```
-
-#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
-
-```SystemStackError``` is raised.
-
-#### mruby [1.3.0 (2017-7-4)]
-
-Segmentation fault.
-
 ## Fiber execution can't cross C function boundary
 
 mruby's ```Fiber``` is implemented in a similar way to Lua's co-routine. This
@@ -105,7 +89,7 @@ p Liste.new "foobar"
 
 ``` [] ```
 
-#### mruby [1.3.0 (2017-7-4)]
+#### mruby [1.4.1 (2018-4-27)]
 
 ```ArgumentError``` is raised.
 
@@ -135,7 +119,7 @@ false
 true
 ```
 
-#### mruby [1.3.0 (2017-7-4)]
+#### mruby [1.4.1 (2018-4-27)]
 
 ```
 true
@@ -158,7 +142,7 @@ defined?(Foo)
 nil
 ```
 
-#### mruby [1.3.0 (2017-7-4)]
+#### mruby [1.4.1 (2018-4-27)]
 
 ```NameError``` is raised.
 
@@ -175,7 +159,7 @@ alias $a $__a__
 
 ``` nil ```
 
-#### mruby [1.3.0 (2017-7-4)]
+#### mruby [1.4.1 (2018-4-27)]
 
 Syntax error
 
@@ -197,12 +181,7 @@ end
 ```ArgumentError``` is raised.
 The re-defined ```+``` operator does not accept any arguments.
 
-#### mruby [1.3.0 (2017-7-4)]
+#### mruby [1.4.1 (2018-4-27)]
 
 ``` 'ab' ```
 Behavior of the operator wasn't changed.
-
-## ```Kernel.binding``` missing
-
-```Kernel.binding``` is not implemented as it is not in the
-ISO standard.
index b246b97..cc28acf 100644 (file)
 /* add -DMRB_USE_FLOAT to use float instead of double for floating point numbers */
 //#define MRB_USE_FLOAT
 
+/* exclude floating point numbers */
+//#define MRB_WITHOUT_FLOAT
+
+/* add -DMRB_METHOD_CACHE to use method cache to improve performance */
+//#define MRB_METHOD_CACHE
+/* size of the method cache (need to be the power of 2) */
+//#define MRB_METHOD_CACHE_SIZE (1<<7)
+
+/* add -DMRB_METHOD_TABLE_INLINE to reduce the size of method table */
+/* MRB_METHOD_TABLE_INLINE requires LSB of function pointers to be zero */
+/* you might need to specify --falign-functions=n (where n>1) */
+//#define MRB_METHOD_TABLE_INLINE
+
 /* add -DMRB_INT16 to use 16bit integer for mrb_int; conflict with MRB_INT64 */
 //#define MRB_INT16
 
 /* add -DMRB_INT64 to use 64bit integer for mrb_int; conflict with MRB_INT16 */
 //#define MRB_INT64
 
-/* represent mrb_value in boxed double; conflict with MRB_USE_FLOAT */
+/* if no specific integer type is chosen */
+#if !defined(MRB_INT16) && !defined(MRB_INT32) && !defined(MRB_INT64)
+# if defined(MRB_64BIT) && !defined(MRB_NAN_BOXING)
+/* Use 64bit integers on 64bit architecture (without MRB_NAN_BOXING) */
+#  define MRB_INT64
+# else
+/* Otherwise use 32bit integers */
+#  define MRB_INT32
+# endif
+#endif
+
+/* represent mrb_value in boxed double; conflict with MRB_USE_FLOAT and MRB_WITHOUT_FLOAT */
 //#define MRB_NAN_BOXING
 
 /* define on big endian machines; used by MRB_NAN_BOXING */
 //#define MRB_FIXED_STATE_ATEXIT_STACK
 
 /* -DMRB_DISABLE_XXXX to drop following features */
-//#define MRB_DISABLE_STDIO    /* use of stdio */
+//#define MRB_DISABLE_STDIO /* use of stdio */
 
 /* -DMRB_ENABLE_XXXX to enable following features */
-//#define MRB_ENABLE_DEBUG_HOOK        /* hooks for debugger */
+//#define MRB_ENABLE_DEBUG_HOOK /* hooks for debugger */
 
 /* end of configuration */
 
index 18f54fd..3b953a3 100644 (file)
@@ -1,7 +1,7 @@
 /*
 ** mruby - An embeddable Ruby implementation
 **
-** Copyright (c) mruby developers 2010-2017
+** Copyright (c) mruby developers 2010-2018
 **
 ** Permission is hereby granted, free of charge, to any person obtaining
 ** a copy of this software and associated documentation files (the
 
 #include "mrbconf.h"
 
+#ifndef MRB_WITHOUT_FLOAT
+#ifndef FLT_EPSILON
+#define FLT_EPSILON (1.19209290e-07f)
+#endif
 #ifndef DBL_EPSILON
 #define DBL_EPSILON ((double)2.22044604925031308085e-16L)
 #endif
@@ -77,6 +81,7 @@
 #else
 #define MRB_FLOAT_EPSILON DBL_EPSILON
 #endif
+#endif
 
 #include "mruby/common.h"
 #include <mruby/value.h>
@@ -157,6 +162,36 @@ struct mrb_context {
   struct RFiber *fib;
 };
 
+#ifdef MRB_METHOD_CACHE_SIZE
+# define MRB_METHOD_CACHE
+#else
+/* default method cache size: 128 */
+/* cache size needs to be power of 2 */
+# define MRB_METHOD_CACHE_SIZE (1<<7)
+#endif
+
+typedef mrb_value (*mrb_func_t)(struct mrb_state *mrb, mrb_value);
+
+#ifdef MRB_METHOD_TABLE_INLINE
+typedef uintptr_t mrb_method_t;
+#else
+typedef struct {
+  mrb_bool func_p;
+  union {
+    struct RProc *proc;
+    mrb_func_t func;
+  };
+} mrb_method_t;
+#endif
+
+#ifdef MRB_METHOD_CACHE
+struct mrb_cache_entry {
+  struct RClass *c, *c0;
+  mrb_sym mid;
+  mrb_method_t m;
+};
+#endif
+
 struct mrb_jmpbuf;
 
 typedef void (*mrb_atexit_func)(struct mrb_state*);
@@ -185,8 +220,11 @@ typedef struct mrb_state {
   struct RClass *string_class;
   struct RClass *array_class;
   struct RClass *hash_class;
+  struct RClass *range_class;
 
+#ifndef MRB_WITHOUT_FLOAT
   struct RClass *float_class;
+#endif
   struct RClass *fixnum_class;
   struct RClass *true_class;
   struct RClass *false_class;
@@ -197,6 +235,10 @@ typedef struct mrb_state {
   struct alloca_header *mems;
   mrb_gc gc;
 
+#ifdef MRB_METHOD_CACHE
+  struct mrb_cache_entry cache[MRB_METHOD_CACHE_SIZE];
+#endif
+
   mrb_sym symidx;
   struct kh_n2s *name2sym;      /* symbol hash */
   struct symbol_name *symtbl;   /* symbol table */
@@ -229,9 +271,6 @@ typedef struct mrb_state {
   mrb_int atexit_stack_len;
 } mrb_state;
 
-
-typedef mrb_value (*mrb_func_t)(mrb_state *mrb, mrb_value);
-
 /**
  * Defines a new class.
  *
@@ -790,22 +829,22 @@ MRB_API struct RClass * mrb_define_module_under(mrb_state *mrb, struct RClass *o
  *
  * Must be a C string composed of the following format specifiers:
  *
- * | char | Ruby type      | C types           | Notes                                               |
+ * | char | Ruby type      | C types           | Notes                                              |
  * |:----:|----------------|-------------------|----------------------------------------------------|
  * | `o`  | {Object}       | {mrb_value}       | Could be used to retrieve any type of argument     |
  * | `C`  | {Class}/{Module} | {mrb_value}     |                                                    |
  * | `S`  | {String}       | {mrb_value}       | when `!` follows, the value may be `nil`           |
  * | `A`  | {Array}        | {mrb_value}       | when `!` follows, the value may be `nil`           |
  * | `H`  | {Hash}         | {mrb_value}       | when `!` follows, the value may be `nil`           |
- * | `s`  | {String}       | char *, {mrb_int} |  Receive two arguments; `s!` gives (`NULL`,`0`) for `nil`       |
+ * | `s`  | {String}       | char *, {mrb_int} | Receive two arguments; `s!` gives (`NULL`,`0`) for `nil`       |
  * | `z`  | {String}       | char *            | `NULL` terminated string; `z!` gives `NULL` for `nil`           |
  * | `a`  | {Array}        | {mrb_value} *, {mrb_int} | Receive two arguments; `a!` gives (`NULL`,`0`) for `nil` |
  * | `f`  | {Float}        | {mrb_float}       |                                                    |
  * | `i`  | {Integer}      | {mrb_int}         |                                                    |
  * | `b`  | boolean        | {mrb_bool}        |                                                    |
  * | `n`  | {Symbol}       | {mrb_sym}         |                                                    |
- * | `&`  | block          | {mrb_value}       |                                                    |
- * | `*`  | rest arguments | {mrb_value} *, {mrb_int} | Receive the rest of arguments as an array.  |
+ * | `&`  | block          | {mrb_value}       | &! raises exception if no block given.             |
+ * | `*`  | rest arguments | {mrb_value} *, {mrb_int} | Receive the rest of arguments as an array; *! avoid copy of the stack.  |
  * | &vert; | optional     |                   | After this spec following specs would be optional. |
  * | `?`  | optional given | {mrb_bool}        | `TRUE` if preceding argument is given. Used to check optional argument is given. |
  *
@@ -834,11 +873,14 @@ mrb_get_mid(mrb_state *mrb) /* get method symbol */
   return mrb->c->ci->mid;
 }
 
-static inline int
-mrb_get_argc(mrb_state *mrb) /* get argc */
-{
-  return mrb->c->ci->argc;
-}
+/**
+ * Retrieve number of arguments from mrb_state.
+ *
+ * Correctly handles *splat arguments.
+ */
+MRB_API mrb_int mrb_get_argc(mrb_state *mrb);
+
+MRB_API mrb_value* mrb_get_argv(mrb_state *mrb);
 
 /* `strlen` for character string literals (use with caution or `strlen` instead)
     Adjacent string literals are concatenated in C/C++ in translation phase 6.
@@ -952,13 +994,13 @@ MRB_API mrb_value mrb_str_new_static(mrb_state *mrb, const char *p, size_t len);
 #define mrb_str_new_lit(mrb, lit) mrb_str_new_static(mrb, (lit), mrb_strlen_lit(lit))
 
 #ifdef _WIN32
-char* mrb_utf8_from_locale(const char *p, size_t len);
-char* mrb_locale_from_utf8(const char *p, size_t len);
+char* mrb_utf8_from_locale(const char *p, int len);
+char* mrb_locale_from_utf8(const char *p, int len);
 #define mrb_locale_free(p) free(p)
 #define mrb_utf8_free(p) free(p)
 #else
-#define mrb_utf8_from_locale(p, l) (p)
-#define mrb_locale_from_utf8(p, l) (p)
+#define mrb_utf8_from_locale(p, l) ((char*)p)
+#define mrb_locale_from_utf8(p, l) ((char*)p)
 #define mrb_locale_free(p)
 #define mrb_utf8_free(p)
 #endif
@@ -1030,9 +1072,11 @@ MRB_API mrb_sym mrb_obj_to_sym(mrb_state *mrb, mrb_value name);
 MRB_API mrb_bool mrb_obj_eq(mrb_state*, mrb_value, mrb_value);
 MRB_API mrb_bool mrb_obj_equal(mrb_state*, mrb_value, mrb_value);
 MRB_API mrb_bool mrb_equal(mrb_state *mrb, mrb_value obj1, mrb_value obj2);
-MRB_API mrb_value mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base);
+MRB_API mrb_value mrb_convert_to_integer(mrb_state *mrb, mrb_value val, mrb_int base);
 MRB_API mrb_value mrb_Integer(mrb_state *mrb, mrb_value val);
+#ifndef MRB_WITHOUT_FLOAT
 MRB_API mrb_value mrb_Float(mrb_state *mrb, mrb_value val);
+#endif
 MRB_API mrb_value mrb_inspect(mrb_state *mrb, mrb_value obj);
 MRB_API mrb_bool mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2);
 
@@ -1051,9 +1095,6 @@ mrb_gc_arena_restore(mrb_state *mrb, int idx)
   mrb->gc.arena_idx = idx;
 }
 
-MRB_API int mrb_gc_arena_save(mrb_state*);
-MRB_API void mrb_gc_arena_restore(mrb_state*,int);
-
 MRB_API void mrb_garbage_collect(mrb_state*);
 MRB_API void mrb_full_gc(mrb_state*);
 MRB_API void mrb_incremental_gc(mrb_state *);
@@ -1120,9 +1161,12 @@ MRB_API void mrb_print_error(mrb_state *mrb);
 #define E_SYNTAX_ERROR              (mrb_exc_get(mrb, "SyntaxError"))
 #define E_LOCALJUMP_ERROR           (mrb_exc_get(mrb, "LocalJumpError"))
 #define E_REGEXP_ERROR              (mrb_exc_get(mrb, "RegexpError"))
+#define E_FROZEN_ERROR              (mrb_exc_get(mrb, "FrozenError"))
 
 #define E_NOTIMP_ERROR              (mrb_exc_get(mrb, "NotImplementedError"))
+#ifndef MRB_WITHOUT_FLOAT
 #define E_FLOATDOMAIN_ERROR         (mrb_exc_get(mrb, "FloatDomainError"))
+#endif
 
 #define E_KEY_ERROR                 (mrb_exc_get(mrb, "KeyError"))
 
@@ -1179,6 +1223,13 @@ MRB_API mrb_value mrb_fiber_resume(mrb_state *mrb, mrb_value fib, mrb_int argc,
 MRB_API mrb_value mrb_fiber_yield(mrb_state *mrb, mrb_int argc, const mrb_value *argv);
 
 /*
+ * Check if a Fiber is alive
+ *
+ * @mrbgem mruby-fiber
+ */
+MRB_API mrb_value mrb_fiber_alive_p(mrb_state *mrb, mrb_value fib);
+
+/*
  * FiberError reference
  *
  * @mrbgem mruby-fiber
@@ -1206,7 +1257,7 @@ MRB_API mrb_value mrb_format(mrb_state *mrb, const char *format, ...);
 /* use naive memcpy and memset instead */
 #undef memcpy
 #undef memset
-static inline void*
+static void*
 mrbmemcpy(void *dst, const void *src, size_t n)
 {
   char *d = (char*)dst;
@@ -1217,7 +1268,7 @@ mrbmemcpy(void *dst, const void *src, size_t n)
 }
 #define memcpy(a,b,c) mrbmemcpy(a,b,c)
 
-static inline void*
+static void*
 mrbmemset(void *s, int c, size_t n)
 {
   char *t = (char*)s;
index bd3124d..6fffe45 100644 (file)
@@ -41,13 +41,11 @@ struct RArray {
 #define mrb_ary_value(p)  mrb_obj_value((void*)(p))
 #define RARRAY(v)  ((struct RArray*)(mrb_ptr(v)))
 
-#define MRB_ARY_EMBED       4
-#define MRB_ARY_EMBED_MASK  3
-#define ARY_EMBED_P(a) ((a)->flags & MRB_ARY_EMBED)
-#define ARY_SET_EMBED_FLAG(a) ((a)->flags |= MRB_ARY_EMBED)
-#define ARY_UNSET_EMBED_FLAG(a) ((a)->flags &= ~(MRB_ARY_EMBED|MRB_ARY_EMBED_MASK))
-#define ARY_EMBED_LEN(a) ((a)->flags & MRB_ARY_EMBED_MASK)
-#define ARY_SET_EMBED_LEN(a,len) (a)->flags = ((a)->flags&~MRB_ARY_EMBED_MASK) | ((len)&MRB_ARY_EMBED_MASK);
+#define MRB_ARY_EMBED_MASK  7
+#define ARY_EMBED_P(a) ((a)->flags & MRB_ARY_EMBED_MASK)
+#define ARY_UNSET_EMBED_FLAG(a) ((a)->flags &= ~(MRB_ARY_EMBED_MASK))
+#define ARY_EMBED_LEN(a) ((mrb_int)(((a)->flags & MRB_ARY_EMBED_MASK) - 1))
+#define ARY_SET_EMBED_LEN(a,len) ((a)->flags = ((a)->flags&~MRB_ARY_EMBED_MASK) | ((uint32_t)(len) + 1))
 #define ARY_EMBED_PTR(a) (&((a)->as.embed[0]))
 
 #define ARY_LEN(a) (ARY_EMBED_P(a)?ARY_EMBED_LEN(a):(a)->as.heap.len)
@@ -204,7 +202,7 @@ MRB_API void mrb_ary_replace(mrb_state *mrb, mrb_value self, mrb_value other);
 MRB_API mrb_value mrb_check_array_type(mrb_state *mrb, mrb_value self);
 
 /*
- * Unshift an element into an array
+ * Unshift an element into the array
  *
  * Equivalent to:
  *
@@ -215,6 +213,17 @@ MRB_API mrb_value mrb_check_array_type(mrb_state *mrb, mrb_value self);
  * @param item The item to unshift.
  */
 MRB_API mrb_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item);
+
+/*
+ * Get nth element in the array
+ *
+ * Equivalent to:
+ *
+ *     ary[offset]
+ *
+ * @param ary The target array.
+ * @param offset The element position (negative counts from the tail).
+ */
 MRB_API mrb_value mrb_ary_entry(mrb_value ary, mrb_int offset);
 
 /*
@@ -231,7 +240,7 @@ MRB_API mrb_value mrb_ary_entry(mrb_value ary, mrb_int offset);
 MRB_API mrb_value mrb_ary_shift(mrb_state *mrb, mrb_value self);
 
 /*
- * Removes all elements from this array
+ * Removes all elements from the array
  *
  * Equivalent to:
  *
@@ -265,23 +274,6 @@ MRB_API mrb_value mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep);
  */
 MRB_API mrb_value mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len);
 
-static inline mrb_int
-mrb_ary_len(mrb_state *mrb, mrb_value ary)
-{
-  (void)mrb;
-  mrb_assert(mrb_array_p(ary));
-  return RARRAY_LEN(ary);
-}
-
-static inline mrb_value
-ary_elt(mrb_value ary, mrb_int offset)
-{
-  if (offset < 0 || RARRAY_LEN(ary) <= offset) {
-    return mrb_nil_value();
-  }
-  return RARRAY_PTR(ary)[offset];
-}
-
 MRB_END_DECL
 
 #endif  /* MRUBY_ARRAY_H */
index 700ea2e..3d9bbdc 100644 (file)
 # error ---->> MRB_NAN_BOXING and MRB_USE_FLOAT conflict <<----
 #endif
 
+#ifdef MRB_WITHOUT_FLOAT
+# error ---->> MRB_NAN_BOXING and MRB_WITHOUT_FLOAT conflict <<----
+#endif
+
 #ifdef MRB_INT64
 # error ---->> MRB_NAN_BOXING and MRB_INT64 conflict <<----
 #endif
index 7ee44a9..86ce645 100644 (file)
@@ -12,7 +12,9 @@
 
 typedef struct mrb_value {
   union {
+#ifndef MRB_WITHOUT_FLOAT
     mrb_float f;
+#endif
     void *p;
     mrb_int i;
     mrb_sym sym;
@@ -20,11 +22,15 @@ typedef struct mrb_value {
   enum mrb_vtype tt;
 } mrb_value;
 
+#ifndef MRB_WITHOUT_FLOAT
 #define mrb_float_pool(mrb,f) mrb_float_value(mrb,f)
+#endif
 
 #define mrb_ptr(o)      (o).value.p
 #define mrb_cptr(o)     mrb_ptr(o)
+#ifndef MRB_WITHOUT_FLOAT
 #define mrb_float(o)    (o).value.f
+#endif
 #define mrb_fixnum(o)   (o).value.i
 #define mrb_symbol(o)   (o).value.sym
 #define mrb_type(o)     (o).tt
@@ -39,7 +45,9 @@ typedef struct mrb_value {
 #define SET_TRUE_VALUE(r) BOXNIX_SET_VALUE(r, MRB_TT_TRUE, value.i, 1)
 #define SET_BOOL_VALUE(r,b) BOXNIX_SET_VALUE(r, b ? MRB_TT_TRUE : MRB_TT_FALSE, value.i, 1)
 #define SET_INT_VALUE(r,n) BOXNIX_SET_VALUE(r, MRB_TT_FIXNUM, value.i, (n))
+#ifndef MRB_WITHOUT_FLOAT
 #define SET_FLOAT_VALUE(mrb,r,v) BOXNIX_SET_VALUE(r, MRB_TT_FLOAT, value.f, (v))
+#endif
 #define SET_SYM_VALUE(r,v) BOXNIX_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v))
 #define SET_OBJ_VALUE(r,v) BOXNIX_SET_VALUE(r, (((struct RObject*)(v))->tt), value.p, (v))
 #define SET_CPTR_VALUE(mrb,r,v) BOXNIX_SET_VALUE(r, MRB_TT_CPTR, value.p, v)
index d1c457f..b17bfc9 100644 (file)
 #error MRB_INT64 cannot be used with MRB_WORD_BOXING in 32-bit mode.
 #endif
 
+#ifndef MRB_WITHOUT_FLOAT
 struct RFloat {
   MRB_OBJECT_HEADER;
   mrb_float f;
 };
+#endif
 
 struct RCptr {
   MRB_OBJECT_HEADER;
@@ -26,7 +28,11 @@ struct RCptr {
 };
 
 #define MRB_FIXNUM_SHIFT 1
+#ifdef MRB_WITHOUT_FLOAT
+#define MRB_TT_HAS_BASIC MRB_TT_CPTR
+#else
 #define MRB_TT_HAS_BASIC MRB_TT_FLOAT
+#endif
 
 enum mrb_special_consts {
   MRB_Qnil    = 0,
@@ -51,21 +57,29 @@ typedef union mrb_value {
       mrb_sym sym : (sizeof(mrb_sym) * CHAR_BIT);
     };
     struct RBasic *bp;
+#ifndef MRB_WITHOUT_FLOAT
     struct RFloat *fp;
+#endif
     struct RCptr *vp;
   } value;
   unsigned long w;
 } mrb_value;
 
 MRB_API mrb_value mrb_word_boxing_cptr_value(struct mrb_state*, void*);
+#ifndef MRB_WITHOUT_FLOAT
 MRB_API mrb_value mrb_word_boxing_float_value(struct mrb_state*, mrb_float);
 MRB_API mrb_value mrb_word_boxing_float_pool(struct mrb_state*, mrb_float);
+#endif
 
+#ifndef MRB_WITHOUT_FLOAT
 #define mrb_float_pool(mrb,f) mrb_word_boxing_float_pool(mrb,f)
+#endif
 
 #define mrb_ptr(o)     (o).value.p
 #define mrb_cptr(o)    (o).value.vp->p
+#ifndef MRB_WITHOUT_FLOAT
 #define mrb_float(o)   (o).value.fp->f
+#endif
 #define mrb_fixnum(o)  ((mrb_int)(o).value.i)
 #define mrb_symbol(o)  (o).value.sym
 
@@ -106,7 +120,9 @@ mrb_type(mrb_value o)
   }\
 } while (0)
 
+#ifndef MRB_WITHOUT_FLOAT
 #define SET_FLOAT_VALUE(mrb,r,v) r = mrb_word_boxing_float_value(mrb, v)
+#endif
 #define SET_CPTR_VALUE(mrb,r,v) r = mrb_word_boxing_cptr_value(mrb, v)
 #define SET_NIL_VALUE(r) BOXWORD_SET_VALUE(r, MRB_TT_FALSE, value.i, 0)
 #define SET_FALSE_VALUE(r) BOXWORD_SET_VALUE(r, MRB_TT_FALSE, value.i, 1)
index 0526386..ea35d8e 100644 (file)
@@ -40,8 +40,10 @@ mrb_class(mrb_state *mrb, mrb_value v)
     return mrb->symbol_class;
   case MRB_TT_FIXNUM:
     return mrb->fixnum_class;
+#ifndef MRB_WITHOUT_FLOAT
   case MRB_TT_FLOAT:
     return mrb->float_class;
+#endif
   case MRB_TT_CPTR:
     return mrb->object_class;
   case MRB_TT_ENV:
@@ -52,7 +54,7 @@ mrb_class(mrb_state *mrb, mrb_value v)
 }
 
 /* TODO: figure out where to put user flags */
-#define MRB_FLAG_IS_FROZEN (1 << 18)
+/* flags bits >= 18 is reserved */
 #define MRB_FLAG_IS_PREPENDED (1 << 19)
 #define MRB_FLAG_IS_ORIGIN (1 << 20)
 #define MRB_CLASS_ORIGIN(c) do {\
@@ -63,6 +65,7 @@ mrb_class(mrb_state *mrb, mrb_value v)
     }\
   }\
 } while (0)
+#define MRB_FLAG_IS_INHERITED (1 << 21)
 #define MRB_INSTANCE_TT_MASK (0xFF)
 #define MRB_SET_INSTANCE_TT(c, tt) c->flags = ((c->flags & ~MRB_INSTANCE_TT_MASK) | (char)tt)
 #define MRB_INSTANCE_TT(c) (enum mrb_vtype)(c->flags & MRB_INSTANCE_TT_MASK)
@@ -71,16 +74,17 @@ MRB_API struct RClass* mrb_define_class_id(mrb_state*, mrb_sym, struct RClass*);
 MRB_API struct RClass* mrb_define_module_id(mrb_state*, mrb_sym);
 MRB_API struct RClass *mrb_vm_define_class(mrb_state*, mrb_value, mrb_value, mrb_sym);
 MRB_API struct RClass *mrb_vm_define_module(mrb_state*, mrb_value, mrb_sym);
-MRB_API void mrb_define_method_raw(mrb_state*, struct RClass*, mrb_sym, struct RProc *);
+MRB_API void mrb_define_method_raw(mrb_state*, struct RClass*, mrb_sym, mrb_method_t);
 MRB_API void mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec);
 MRB_API void mrb_alias_method(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b);
 
-MRB_API struct RClass *mrb_class_outer_module(mrb_state*, struct RClass *);
-MRB_API struct RProc *mrb_method_search_vm(mrb_state*, struct RClass**, mrb_sym);
-MRB_API struct RProc *mrb_method_search(mrb_state*, struct RClass*, mrb_sym);
+MRB_API mrb_method_t mrb_method_search_vm(mrb_state*, struct RClass**, mrb_sym);
+MRB_API mrb_method_t mrb_method_search(mrb_state*, struct RClass*, mrb_sym);
 
 MRB_API struct RClass* mrb_class_real(struct RClass* cl);
 
+void mrb_class_name_class(mrb_state*, struct RClass*, struct RClass*, mrb_sym);
+mrb_value mrb_class_find_path(mrb_state*, struct RClass*);
 void mrb_gc_mark_mt(mrb_state*, struct RClass*);
 size_t mrb_gc_mark_mt_size(mrb_state*, struct RClass*);
 void mrb_gc_free_mt(mrb_state*, struct RClass*);
index 1606399..d6ec78b 100644 (file)
@@ -14,7 +14,7 @@
 #define MRB_END_DECL
 #else
 # define MRB_BEGIN_DECL extern "C" {
-# define MRB_END_DECL  }
+# define MRB_END_DECL }
 #endif
 #else
 /** Start declarations in C mode */
index 13e04d0..d7d0296 100644 (file)
@@ -138,7 +138,7 @@ struct mrb_parser_state {
   int tidx;
   int tsiz;
 
-  mrb_ast_node *all_heredocs;  /* list of mrb_parser_heredoc_info* */
+  mrb_ast_node *all_heredocs; /* list of mrb_parser_heredoc_info* */
   mrb_ast_node *heredocs_from_nextline;
   mrb_ast_node *parsing_heredoc;
   mrb_ast_node *lex_strterm_before_heredoc;
@@ -165,7 +165,6 @@ struct mrb_parser_state {
 MRB_API struct mrb_parser_state* mrb_parser_new(mrb_state*);
 MRB_API void mrb_parser_free(struct mrb_parser_state*);
 MRB_API void mrb_parser_parse(struct mrb_parser_state*,mrbc_context*);
-MRB_API double mrb_float_read(const char*, char**);
 
 MRB_API void mrb_parser_set_filename(struct mrb_parser_state*, char const*);
 MRB_API char const* mrb_parser_get_filename(struct mrb_parser_state*, uint16_t idx);
@@ -175,7 +174,7 @@ MRB_API char const* mrb_parser_get_filename(struct mrb_parser_state*, uint16_t i
 MRB_API struct mrb_parser_state* mrb_parse_file(mrb_state*,FILE*,mrbc_context*);
 #endif
 MRB_API struct mrb_parser_state* mrb_parse_string(mrb_state*,const char*,mrbc_context*);
-MRB_API struct mrb_parser_state* mrb_parse_nstring(mrb_state*,const char*,int,mrbc_context*);
+MRB_API struct mrb_parser_state* mrb_parse_nstring(mrb_state*,const char*,size_t,mrbc_context*);
 MRB_API struct RProc* mrb_generate_code(mrb_state*, struct mrb_parser_state*);
 MRB_API mrb_value mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c);
 
@@ -185,9 +184,9 @@ MRB_API mrb_value mrb_load_file(mrb_state*,FILE*);
 MRB_API mrb_value mrb_load_file_cxt(mrb_state*,FILE*, mrbc_context *cxt);
 #endif
 MRB_API mrb_value mrb_load_string(mrb_state *mrb, const char *s);
-MRB_API mrb_value mrb_load_nstring(mrb_state *mrb, const char *s, int len);
+MRB_API mrb_value mrb_load_nstring(mrb_state *mrb, const char *s, size_t len);
 MRB_API mrb_value mrb_load_string_cxt(mrb_state *mrb, const char *s, mrbc_context *cxt);
-MRB_API mrb_value mrb_load_nstring_cxt(mrb_state *mrb, const char *s, int len, mrbc_context *cxt);
+MRB_API mrb_value mrb_load_nstring_cxt(mrb_state *mrb, const char *s, size_t len, mrbc_context *cxt);
 
 /** @} */
 MRB_END_DECL
index 44f84e4..d1de348 100644 (file)
@@ -47,13 +47,13 @@ typedef struct mrb_irep_debug_info {
  * get line from irep's debug info and program counter
  * @return returns NULL if not found
  */
-MRB_API const char *mrb_debug_get_filename(mrb_irep *irep, uint32_t pc);
+MRB_API const char *mrb_debug_get_filename(mrb_irep *irep, ptrdiff_t pc);
 
 /*
  * get line from irep's debug info and program counter
  * @return returns -1 if not found
  */
-MRB_API int32_t mrb_debug_get_line(mrb_irep *irep, uint32_t pc);
+MRB_API int32_t mrb_debug_get_line(mrb_irep *irep, ptrdiff_t pc);
 
 MRB_API mrb_irep_debug_info_file *mrb_debug_info_append_file(
     mrb_state *mrb, mrb_irep *irep,
index 0a26255..1587795 100644 (file)
@@ -24,7 +24,7 @@ struct RException {
 MRB_API void mrb_sys_fail(mrb_state *mrb, const char *mesg);
 MRB_API mrb_value mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str);
 #define mrb_exc_new_str_lit(mrb, c, lit) mrb_exc_new_str(mrb, c, mrb_str_new_lit(mrb, lit))
-MRB_API mrb_value mrb_make_exception(mrb_state *mrb, int argc, const mrb_value *argv);
+MRB_API mrb_value mrb_make_exception(mrb_state *mrb, mrb_int argc, const mrb_value *argv);
 MRB_API mrb_value mrb_exc_backtrace(mrb_state *mrb, mrb_value exc);
 MRB_API mrb_value mrb_get_backtrace(mrb_state *mrb);
 MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, const char *fmt, ...);
@@ -34,7 +34,6 @@ MRB_API mrb_value mrb_f_raise(mrb_state*, mrb_value);
 
 struct RBreak {
   MRB_OBJECT_HEADER;
-  struct iv_tbl *iv;
   struct RProc *proc;
   mrb_value val;
 };
index ce214aa..2a3ff41 100644 (file)
@@ -33,6 +33,13 @@ typedef enum {
   MRB_GC_STATE_SWEEP
 } mrb_gc_state;
 
+/* Disable MSVC warning "C4200: nonstandard extension used: zero-sized array
+ * in struct/union" when in C++ mode */
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4200)
+#endif
+
 typedef struct mrb_heap_page {
   struct RBasic *freelist;
   struct mrb_heap_page *prev;
@@ -43,6 +50,10 @@ typedef struct mrb_heap_page {
   void *objects[];
 } mrb_heap_page;
 
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
 typedef struct mrb_gc {
   mrb_heap_page *heaps;                /* heaps for GC */
   mrb_heap_page *sweeps;
index 35ae2bb..efd2267 100644 (file)
@@ -44,7 +44,7 @@ typedef struct mrb_irep {
   uint16_t *lines;
   struct mrb_irep_debug_info* debug_info;
 
-  size_t ilen, plen, slen, rlen, refcnt;
+  int ilen, plen, slen, rlen, refcnt;
 } mrb_irep;
 
 #define MRB_ISEQ_NO_FREE 1
@@ -55,6 +55,7 @@ MRB_API mrb_value mrb_load_irep_cxt(mrb_state*, const uint8_t*, mrbc_context*);
 void mrb_irep_free(mrb_state*, struct mrb_irep*);
 void mrb_irep_incref(mrb_state*, struct mrb_irep*);
 void mrb_irep_decref(mrb_state*, struct mrb_irep*);
+void mrb_irep_cutref(mrb_state*, struct mrb_irep*);
 
 MRB_END_DECL
 
index 40c8c4a..da9225a 100644 (file)
@@ -22,13 +22,19 @@ MRB_BEGIN_DECL
 #define POSFIXABLE(f) TYPED_POSFIXABLE(f,mrb_int)
 #define NEGFIXABLE(f) TYPED_NEGFIXABLE(f,mrb_int)
 #define FIXABLE(f) TYPED_FIXABLE(f,mrb_int)
+#ifndef MRB_WITHOUT_FLOAT
 #define FIXABLE_FLOAT(f) TYPED_FIXABLE(f,double)
+#endif
 
+#ifndef MRB_WITHOUT_FLOAT
 MRB_API mrb_value mrb_flo_to_fixnum(mrb_state *mrb, mrb_value val);
-MRB_API mrb_value mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, int base);
+#endif
+MRB_API mrb_value mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, mrb_int base);
 /* ArgumentError if format string doesn't match /%(\.[0-9]+)?[aAeEfFgG]/ */
+#ifndef MRB_WITHOUT_FLOAT
 MRB_API mrb_value mrb_float_to_str(mrb_state *mrb, mrb_value x, const char *fmt);
 MRB_API mrb_float mrb_to_flo(mrb_state *mrb, mrb_value x);
+#endif
 
 mrb_value mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y);
 mrb_value mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y);
index 9347981..4f2134a 100644 (file)
@@ -22,6 +22,8 @@ struct RBasic {
 };
 #define mrb_basic_ptr(v) ((struct RBasic*)(mrb_ptr(v)))
 
+/* flags bits >= 18 is reserved */
+#define MRB_FLAG_IS_FROZEN (1 << 18)
 #define MRB_FROZEN_P(o) ((o)->flags & MRB_FLAG_IS_FROZEN)
 #define MRB_SET_FROZEN_FLAG(o) ((o)->flags |= MRB_FLAG_IS_FROZEN)
 #define MRB_UNSET_FROZEN_FLAG(o) ((o)->flags &= ~MRB_FLAG_IS_FROZEN)
index 9c26662..aa281b6 100644 (file)
@@ -18,19 +18,20 @@ MRB_BEGIN_DECL
 struct REnv {
   MRB_OBJECT_HEADER;
   mrb_value *stack;
-  ptrdiff_t cioff;
-  union {
-    mrb_sym mid;
-    struct mrb_context *c;
-  } cxt;
+  struct mrb_context *cxt;
+  mrb_sym mid;
 };
 
-#define MRB_SET_ENV_STACK_LEN(e,len) (e)->flags = (unsigned int)(len)
-#define MRB_ENV_STACK_LEN(e) ((mrb_int)(e)->flags)
-#define MRB_ENV_UNSHARE_STACK(e) ((e)->cioff = -1)
-#define MRB_ENV_STACK_SHARED_P(e) ((e)->cioff >= 0)
+/* flags (21bits): 1(shared flag):10(cioff/bidx):10(stack_len) */
+#define MRB_ENV_SET_STACK_LEN(e,len) (e)->flags = (((e)->flags & ~0x3ff)|((unsigned int)(len) & 0x3ff))
+#define MRB_ENV_STACK_LEN(e) ((mrb_int)((e)->flags & 0x3ff))
+#define MRB_ENV_STACK_UNSHARED (1<<20)
+#define MRB_ENV_UNSHARE_STACK(e) (e)->flags |= MRB_ENV_STACK_UNSHARED
+#define MRB_ENV_STACK_SHARED_P(e) (((e)->flags & MRB_ENV_STACK_UNSHARED) == 0)
+#define MRB_ENV_BIDX(e) (((e)->flags >> 10) & 0x3ff)
+#define MRB_ENV_SET_BIDX(e,idx) (e)->flags = (((e)->flags & ~(0x3ff<<10))|((unsigned int)(idx) & 0x3ff)<<10)
 
-MRB_API void mrb_env_unshare(mrb_state*, struct REnv*);
+void mrb_env_unshare(mrb_state*, struct REnv*);
 
 struct RProc {
   MRB_OBJECT_HEADER;
@@ -38,8 +39,11 @@ struct RProc {
     mrb_irep *irep;
     mrb_func_t func;
   } body;
-  struct RClass *target_class;
-  struct REnv *env;
+  struct RProc *upper;
+  union {
+    struct RClass *target_class;
+    struct REnv *env;
+  } e;
 };
 
 /* aspec access */
@@ -51,12 +55,29 @@ struct RProc {
 #define MRB_ASPEC_KDICT(a)        ((a) & (1<<1))
 #define MRB_ASPEC_BLOCK(a)        ((a) & 1)
 
-#define MRB_PROC_CFUNC 128
-#define MRB_PROC_CFUNC_P(p) (((p)->flags & MRB_PROC_CFUNC) != 0)
+#define MRB_PROC_CFUNC_FL 128
+#define MRB_PROC_CFUNC_P(p) (((p)->flags & MRB_PROC_CFUNC_FL) != 0)
+#define MRB_PROC_CFUNC(p) (p)->body.func
 #define MRB_PROC_STRICT 256
 #define MRB_PROC_STRICT_P(p) (((p)->flags & MRB_PROC_STRICT) != 0)
 #define MRB_PROC_ORPHAN 512
 #define MRB_PROC_ORPHAN_P(p) (((p)->flags & MRB_PROC_ORPHAN) != 0)
+#define MRB_PROC_ENVSET 1024
+#define MRB_PROC_ENV_P(p) (((p)->flags & MRB_PROC_ENVSET) != 0)
+#define MRB_PROC_ENV(p) (MRB_PROC_ENV_P(p) ? (p)->e.env : NULL)
+#define MRB_PROC_TARGET_CLASS(p) (MRB_PROC_ENV_P(p) ? (p)->e.env->c : (p)->e.target_class)
+#define MRB_PROC_SET_TARGET_CLASS(p,tc) do {\
+  if (MRB_PROC_ENV_P(p)) {\
+    (p)->e.env->c = (tc);\
+    mrb_field_write_barrier(mrb, (struct RBasic*)(p)->e.env, (struct RBasic*)tc);\
+  }\
+  else {\
+    (p)->e.target_class = (tc);\
+    mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)tc);\
+  }\
+} while (0)
+#define MRB_PROC_SCOPE 2048
+#define MRB_PROC_SCOPE_P(p) (((p)->flags & MRB_PROC_SCOPE) != 0)
 
 #define mrb_proc_ptr(v)    ((struct RProc*)(mrb_ptr(v)))
 
@@ -75,8 +96,35 @@ MRB_API mrb_value mrb_proc_cfunc_env_get(mrb_state*, mrb_int);
 /* old name */
 #define mrb_cfunc_env_get(mrb, idx) mrb_proc_cfunc_env_get(mrb, idx)
 
+#ifdef MRB_METHOD_TABLE_INLINE
+
+#define MRB_METHOD_FUNC_FL ((uintptr_t)1)
+#define MRB_METHOD_FUNC_P(m) (((uintptr_t)(m))&MRB_METHOD_FUNC_FL)
+#define MRB_METHOD_FUNC(m) ((mrb_func_t)((uintptr_t)(m)&(~MRB_METHOD_FUNC_FL)))
+#define MRB_METHOD_FROM_FUNC(m,fn) m=(mrb_method_t)((struct RProc*)((uintptr_t)(fn)|MRB_METHOD_FUNC_FL))
+#define MRB_METHOD_FROM_PROC(m,pr) m=(mrb_method_t)(struct RProc*)(pr)
+#define MRB_METHOD_PROC_P(m) (!MRB_METHOD_FUNC_P(m))
+#define MRB_METHOD_PROC(m) ((struct RProc*)(m))
+#define MRB_METHOD_UNDEF_P(m) ((m)==0)
+
+#else
+
+#define MRB_METHOD_FUNC_P(m) ((m).func_p)
+#define MRB_METHOD_FUNC(m) ((m).func)
+#define MRB_METHOD_FROM_FUNC(m,fn) do{(m).func_p=TRUE;(m).func=(fn);}while(0)
+#define MRB_METHOD_FROM_PROC(m,pr) do{(m).func_p=FALSE;(m).proc=(pr);}while(0)
+#define MRB_METHOD_PROC_P(m) (!MRB_METHOD_FUNC_P(m))
+#define MRB_METHOD_PROC(m) ((m).proc)
+#define MRB_METHOD_UNDEF_P(m) ((m).proc==NULL)
+
+#endif /* MRB_METHOD_TABLE_INLINE */
+
+#define MRB_METHOD_CFUNC_P(m) (MRB_METHOD_FUNC_P(m)?TRUE:(MRB_METHOD_PROC(m)?(MRB_PROC_CFUNC_P(MRB_METHOD_PROC(m))):FALSE))
+#define MRB_METHOD_CFUNC(m) (MRB_METHOD_FUNC_P(m)?MRB_METHOD_FUNC(m):((MRB_METHOD_PROC(m)&&MRB_PROC_CFUNC_P(MRB_METHOD_PROC(m)))?MRB_PROC_CFUNC(MRB_METHOD_PROC(m)):NULL))
+
+
 #include <mruby/khash.h>
-KHASH_DECLARE(mt, mrb_sym, struct RProc*, TRUE)
+KHASH_DECLARE(mt, mrb_sym, mrb_method_t, TRUE)
 
 MRB_END_DECL
 
index b180932..975b1fe 100644 (file)
@@ -26,6 +26,7 @@ struct RString {
       union {
         mrb_int capa;
         struct mrb_shared_string *shared;
+        struct RString *fshared;
       } aux;
       char *ptr;
     } heap;
@@ -59,10 +60,17 @@ struct RString {
 #define RSTR_SET_SHARED_FLAG(s) ((s)->flags |= MRB_STR_SHARED)
 #define RSTR_UNSET_SHARED_FLAG(s) ((s)->flags &= ~MRB_STR_SHARED)
 
+#define RSTR_FSHARED_P(s) ((s)->flags & MRB_STR_FSHARED)
+#define RSTR_SET_FSHARED_FLAG(s) ((s)->flags |= MRB_STR_FSHARED)
+#define RSTR_UNSET_FSHARED_FLAG(s) ((s)->flags &= ~MRB_STR_FSHARED)
+
 #define RSTR_NOFREE_P(s) ((s)->flags & MRB_STR_NOFREE)
 #define RSTR_SET_NOFREE_FLAG(s) ((s)->flags |= MRB_STR_NOFREE)
 #define RSTR_UNSET_NOFREE_FLAG(s) ((s)->flags &= ~MRB_STR_NOFREE)
 
+#define RSTR_POOL_P(s) ((s)->flags & MRB_STR_POOL)
+#define RSTR_SET_POOL_FLAG(s) ((s)->flags |= MRB_STR_POOL)
+
 /*
  * Returns a pointer from a Ruby string
  */
@@ -76,14 +84,23 @@ struct RString {
 MRB_API mrb_int mrb_str_strlen(mrb_state*, struct RString*);
 
 #define MRB_STR_SHARED    1
-#define MRB_STR_NOFREE    2
-#define MRB_STR_NO_UTF    8
-#define MRB_STR_EMBED    16
-#define MRB_STR_EMBED_LEN_MASK 0x3e0
-#define MRB_STR_EMBED_LEN_SHIFT 5
+#define MRB_STR_FSHARED   2
+#define MRB_STR_NOFREE    4
+#define MRB_STR_POOL      8
+#define MRB_STR_NO_UTF   16
+#define MRB_STR_EMBED    32
+#define MRB_STR_EMBED_LEN_MASK 0x7c0
+#define MRB_STR_EMBED_LEN_SHIFT 6
 
 void mrb_gc_free_str(mrb_state*, struct RString*);
 MRB_API void mrb_str_modify(mrb_state*, struct RString*);
+
+/*
+ * Finds the index of a substring in a string
+ */
+MRB_API mrb_int mrb_str_index(mrb_state*, mrb_value, const char*, mrb_int, mrb_int);
+#define mrb_str_index_lit(mrb, str, lit, off) mrb_str_index(mrb, str, lit, mrb_strlen_lit(lit), off);
+
 /*
  * Appends self to other. Returns self as a concatnated string.
  *
@@ -297,6 +314,7 @@ MRB_API mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb
 MRB_API mrb_value mrb_string_type(mrb_state *mrb, mrb_value str);
 
 MRB_API mrb_value mrb_check_string_type(mrb_state *mrb, mrb_value str);
+MRB_API mrb_value mrb_str_new_capa(mrb_state *mrb, size_t capa);
 MRB_API mrb_value mrb_str_buf_new(mrb_state *mrb, size_t capa);
 
 MRB_API const char *mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr);
@@ -401,7 +419,7 @@ MRB_API int mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2);
 MRB_API char *mrb_str_to_cstr(mrb_state *mrb, mrb_value str);
 
 mrb_value mrb_str_pool(mrb_state *mrb, mrb_value str);
-mrb_int mrb_str_hash(mrb_state *mrb, mrb_value str);
+uint32_t mrb_str_hash(mrb_state *mrb, mrb_value str);
 mrb_value mrb_str_dump(mrb_state *mrb, mrb_value str);
 
 /*
index 98c68d6..f988826 100644 (file)
@@ -63,12 +63,14 @@ struct mrb_state;
 #endif
 
 
+#ifndef MRB_WITHOUT_FLOAT
 MRB_API double mrb_float_read(const char*, char**);
 #ifdef MRB_USE_FLOAT
   typedef float mrb_float;
 #else
   typedef double mrb_float;
 #endif
+#endif
 
 #if defined _MSC_VER && _MSC_VER < 1900
 # ifndef __cplusplus
@@ -79,7 +81,7 @@ MRB_API int mrb_msvc_vsnprintf(char *s, size_t n, const char *format, va_list ar
 MRB_API int mrb_msvc_snprintf(char *s, size_t n, const char *format, ...);
 # define vsnprintf(s, n, format, arg) mrb_msvc_vsnprintf(s, n, format, arg)
 # define snprintf(s, n, format, ...) mrb_msvc_snprintf(s, n, format, __VA_ARGS__)
-# if _MSC_VER < 1800
+# if _MSC_VER < 1800 && !defined MRB_WITHOUT_FLOAT
 #  include <float.h>
 #  define isfinite(n) _finite(n)
 #  define isnan _isnan
@@ -158,7 +160,9 @@ typedef void mrb_value;
 #ifndef mrb_bool
 #define mrb_bool(o)   (mrb_type(o) != MRB_TT_FALSE)
 #endif
+#ifndef MRB_WITHOUT_FLOAT
 #define mrb_float_p(o) (mrb_type(o) == MRB_TT_FLOAT)
+#endif
 #define mrb_symbol_p(o) (mrb_type(o) == MRB_TT_SYMBOL)
 #define mrb_array_p(o) (mrb_type(o) == MRB_TT_ARRAY)
 #define mrb_string_p(o) (mrb_type(o) == MRB_TT_STRING)
@@ -171,6 +175,7 @@ MRB_API mrb_bool mrb_regexp_p(struct mrb_state*, mrb_value);
 /*
  * Returns a float in Ruby.
  */
+#ifndef MRB_WITHOUT_FLOAT
 MRB_INLINE mrb_value mrb_float_value(struct mrb_state *mrb, mrb_float f)
 {
   mrb_value v;
@@ -178,6 +183,7 @@ MRB_INLINE mrb_value mrb_float_value(struct mrb_state *mrb, mrb_float f)
   SET_FLOAT_VALUE(mrb, v, f);
   return v;
 }
+#endif
 
 static inline mrb_value
 mrb_cptr_value(struct mrb_state *mrb, void *p)
index 2f2bbbf..5fef83f 100644 (file)
@@ -47,7 +47,6 @@ MRB_API void mrb_iv_check(mrb_state *mrb, mrb_sym sym);
 MRB_API mrb_value mrb_obj_iv_get(mrb_state *mrb, struct RObject *obj, mrb_sym sym);
 MRB_API void mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v);
 MRB_API mrb_bool mrb_obj_iv_defined(mrb_state *mrb, struct RObject *obj, mrb_sym sym);
-MRB_API void mrb_obj_iv_ifnone(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v);
 MRB_API mrb_value mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym);
 MRB_API void mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v);
 MRB_API mrb_bool mrb_iv_defined(mrb_state*, mrb_value, mrb_sym);
@@ -126,7 +125,6 @@ mrb_value mrb_obj_instance_variables(mrb_state*, mrb_value);
 mrb_value mrb_mod_class_variables(mrb_state*, mrb_value);
 mrb_value mrb_mod_cv_get(mrb_state *mrb, struct RClass * c, mrb_sym sym);
 mrb_bool mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym);
-mrb_sym mrb_class_sym(mrb_state *mrb, struct RClass *c, struct RClass *outer);
 
 /* GC functions */
 void mrb_gc_mark_gv(mrb_state*);
index 8414bf2..43d6461 100644 (file)
@@ -42,12 +42,12 @@ MRB_BEGIN_DECL
 /*
  * Minor release version number.
  */
-#define MRUBY_RELEASE_MINOR 3
+#define MRUBY_RELEASE_MINOR 4
 
 /*
  * Tiny release version number.
  */
-#define MRUBY_RELEASE_TEENY 0
+#define MRUBY_RELEASE_TEENY 1
 
 /*
  * The mruby version.
@@ -62,17 +62,17 @@ MRB_BEGIN_DECL
 /*
  * Release year.
  */
-#define MRUBY_RELEASE_YEAR 2017
+#define MRUBY_RELEASE_YEAR 2018
 
 /*
  * Release month.
  */
-#define MRUBY_RELEASE_MONTH 7
+#define MRUBY_RELEASE_MONTH 4
 
 /*
  * Release day.
  */
-#define MRUBY_RELEASE_DAY 4
+#define MRUBY_RELEASE_DAY 27
 
 /*
  * Release date as a string.
similarity index 93%
rename from third-party/mruby/tasks/mruby_build.rake
rename to third-party/mruby/lib/mruby/build.rb
index f5d4374..57bd9c5 100644 (file)
@@ -1,5 +1,5 @@
-load "#{MRUBY_ROOT}/tasks/mruby_build_gem.rake"
-load "#{MRUBY_ROOT}/tasks/mruby_build_commands.rake"
+require "mruby/build/load_gems"
+require "mruby/build/command"
 
 module MRuby
   class << self
@@ -241,7 +241,7 @@ EOS
         else
           compiler.defines += %w(DISABLE_GEMS)
         end
-        compiler.define_rules build_dir, File.expand_path(File.join(File.dirname(__FILE__), '..'))
+        compiler.define_rules build_dir, File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
       end
     end
 
@@ -334,6 +334,7 @@ EOS
     attr_accessor :host_target, :build_target
 
     def initialize(name, build_dir=nil, &block)
+      @endian = nil
       @test_runner = Command::CrossTestRunner.new(self)
       super
     end
@@ -343,6 +344,7 @@ EOS
     end
 
     def run_test
+      @test_runner.runner_options << ' -v' if $verbose
       mrbtest = exefile("#{build_dir}/bin/mrbtest")
       if (@test_runner.command == nil)
         puts "You should run #{mrbtest} on target device."
@@ -351,5 +353,26 @@ EOS
         @test_runner.run(mrbtest)
       end
     end
+
+    def big_endian
+      if @endian
+        puts "Endian has already specified as #{@endian}."
+        return
+      end
+      @endian = :big
+      @mrbc.compile_options += ' -E'
+      compilers.each do |c|
+        c.defines += %w(MRB_ENDIAN_BIG)
+      end
+    end
+
+    def little_endian
+      if @endian
+        puts "Endian has already specified as #{@endian}."
+        return
+      end
+      @endian = :little
+      @mrbc.compile_options += ' -e'
+    end
   end # CrossBuild
 end # MRuby
@@ -76,9 +76,6 @@ module MRuby
 
       if params[:core]
         gemdir = "#{root}/mrbgems/#{params[:core]}"
-      elsif params[:path]
-        require 'pathname'
-        gemdir = Pathname.new(params[:path]).absolute? ? params[:path] : "#{root}/#{params[:path]}"
       elsif params[:git]
         url = params[:git]
         gemdir = "#{gem_clone_dir}/#{url.match(/([-\w]+)(\.[-\w]+|)$/).to_a[1]}"
@@ -108,6 +105,11 @@ module MRuby
           # Jump to the top of the branch
           git.run_checkout gemdir, branch if $pull_gems
         end
+
+        gemdir << "/#{params[:path]}" if params[:path]
+      elsif params[:path]
+        require 'pathname'
+        gemdir = Pathname.new(params[:path]).absolute? ? params[:path] : "#{root}/#{params[:path]}"
       else
         fail "unknown gem option #{params}"
       end
similarity index 95%
rename from third-party/mruby/tasks/mrbgem_spec.rake
rename to third-party/mruby/lib/mruby/gem.rb
index 27a1d35..a83ca63 100644 (file)
@@ -176,6 +176,7 @@ module MRuby
             f.puts %Q[  mrb_load_irep(mrb, gem_mrblib_irep_#{funcname});]
             f.puts %Q[  if (mrb->exc) {]
             f.puts %Q[    mrb_print_error(mrb);]
+            f.puts %Q[    mrb_close(mrb);]
             f.puts %Q[    exit(EXIT_FAILURE);]
             f.puts %Q[  }]
           end
@@ -323,20 +324,22 @@ module MRuby
         @ary.empty?
       end
 
+      def default_gem_params dep
+        if dep[:default]; dep
+        elsif File.exist? "#{MRUBY_ROOT}/mrbgems/#{dep[:gem]}" # check core
+          { :gem => dep[:gem], :default => { :core => dep[:gem] } }
+        else # fallback to mgem-list
+          { :gem => dep[:gem], :default => { :mgem => dep[:gem] } }
+        end
+      end
+
       def generate_gem_table build
         gem_table = @ary.reduce({}) { |res,v| res[v.name] = v; res }
 
         default_gems = []
         each do |g|
           g.dependencies.each do |dep|
-            unless gem_table.key? dep[:gem]
-              if dep[:default]; default_gems << dep
-              elsif File.exist? "#{MRUBY_ROOT}/mrbgems/#{dep[:gem]}" # check core
-                default_gems << { :gem => dep[:gem], :default => { :core => dep[:gem] } }
-              else # fallback to mgem-list
-                default_gems << { :gem => dep[:gem], :default => { :mgem => dep[:gem] } }
-              end
-            end
+            default_gems << default_gem_params(dep) unless gem_table.key? dep[:gem]
           end
         end
 
@@ -348,11 +351,7 @@ module MRuby
           spec.setup
 
           spec.dependencies.each do |dep|
-            unless gem_table.key? dep[:gem]
-              if dep[:default]; default_gems << dep
-              else default_gems << { :gem => dep[:gem], :default => { :mgem => dep[:gem] } }
-              end
-            end
+            default_gems << default_gem_params(dep) unless gem_table.key? dep[:gem]
           end
           gem_table[spec.name] = spec
         end
index 64f05de..7ddbb16 100644 (file)
@@ -1,4 +1,10 @@
 MRuby::GemBox.new do |conf|
+  # Use standard IO/File class
+  conf.gem :core => "mruby-io"
+
+  # Use standard Array#pack, String#unpack methods
+  conf.gem :core => "mruby-pack"
+
   # Use standard Kernel#sprintf method
   conf.gem :core => "mruby-sprintf"
 
@@ -14,6 +20,9 @@ MRuby::GemBox.new do |conf|
   # Use standard Struct class
   conf.gem :core => "mruby-struct"
 
+  # Use Comparable module extension
+  conf.gem :core => "mruby-compar-ext"
+
   # Use Enumerable module extension
   conf.gem :core => "mruby-enum-ext"
 
index 882caf1..58d4428 100644 (file)
@@ -2,4 +2,5 @@ MRuby::Gem::Specification.new('mruby-array-ext') do |spec|
   spec.license = 'MIT'
   spec.author  = 'mruby developers'
   spec.summary = 'Array class extension'
+  spec.add_test_dependency 'mruby-enumerator', core: 'mruby-enumerator'
 end
index 1e6d4d5..c0995bb 100644 (file)
@@ -81,11 +81,7 @@ class Array
   #
   def uniq(&block)
     ary = self.dup
-    if block
-      ary.uniq!(&block)
-    else
-      ary.uniq!
-    end
+    ary.uniq!(&block)
     ary
   end
 
@@ -105,8 +101,19 @@ class Array
 
     hash = {}
     array = []
-    elem.each { |x| hash[x] = true }
-    self.each { |x| array << x unless hash[x] }
+    idx = 0
+    len = elem.size
+    while idx < len
+      hash[elem[idx]] = true
+      idx += 1
+    end
+    idx = 0
+    len = size
+    while idx < len
+      v = self[idx]
+      array << v unless hash[v]
+      idx += 1
+    end
     array
   end
 
@@ -141,12 +148,21 @@ class Array
 
     hash = {}
     array = []
-    elem.each{|v| hash[v] = true }
-    self.each do |v|
+    idx = 0
+    len = elem.size
+    while idx < len
+      hash[elem[idx]] = true
+      idx += 1
+    end
+    idx = 0
+    len = size
+    while idx < len
+      v = self[idx]
       if hash[v]
         array << v
         hash.delete v
       end
+      idx += 1
     end
     array
   end
@@ -169,15 +185,9 @@ class Array
   #    a.flatten(1)              #=> [1, 2, 3, [4, 5]]
   #
   def flatten(depth=nil)
-    ar = []
-    self.each do |e|
-      if e.is_a?(Array) && (depth.nil? || depth > 0)
-        ar += e.flatten(depth.nil? ? nil : depth - 1)
-      else
-        ar << e
-      end
-    end
-    ar
+    res = dup
+    res.flatten! depth
+    res
   end
 
   ##
@@ -200,13 +210,17 @@ class Array
   def flatten!(depth=nil)
     modified = false
     ar = []
-    self.each do |e|
+    idx = 0
+    len = size
+    while idx < len
+      e = self[idx]
       if e.is_a?(Array) && (depth.nil? || depth > 0)
         ar += e.flatten(depth.nil? ? nil : depth - 1)
         modified = true
       else
         ar << e
       end
+      idx += 1
     end
     if modified
       self.replace(ar)
@@ -252,7 +266,7 @@ class Array
 
   # for efficiency
   def reverse_each(&block)
-    return to_enum :reverse_each unless block_given?
+    return to_enum :reverse_each unless block
 
     i = self.size - 1
     while i>=0
@@ -474,7 +488,7 @@ class Array
   #     scores.delete_if {|score| score < 80 }   #=> [97]
 
   def delete_if(&block)
-    return to_enum :delete_if unless block_given?
+    return to_enum :delete_if unless block
 
     idx = 0
     while idx < self.size do
@@ -503,7 +517,7 @@ class Array
   #  If no block is given, an Enumerator is returned instead.
 
   def reject!(&block)
-    return to_enum :reject! unless block_given?
+    return to_enum :reject! unless block
 
     len = self.size
     idx = 0
@@ -593,33 +607,60 @@ class Array
   #  undefined which value is actually picked up at each iteration.
 
   def bsearch(&block)
-    return to_enum :bsearch unless block_given?
+    return to_enum :bsearch unless block
+
+    if idx = bsearch_index(&block)
+      self[idx]
+    else
+      nil
+    end
+  end
+
+  ##
+  #  call-seq:
+  #     ary.bsearch_index {|x| block }  -> int or nil
+  #
+  #  By using binary search, finds an index of a value from this array which
+  #  meets the given condition in O(log n) where n is the size of the array.
+  #
+  #  It supports two modes, depending on the nature of the block and they are
+  #  exactly the same as in the case of #bsearch method with the only difference
+  #  being that this method returns the index of the element instead of the
+  #  element itself. For more details consult the documentation for #bsearch.
+
+  def bsearch_index(&block)
+    return to_enum :bsearch_index unless block
 
     low = 0
-    high = self.size
+    high = size
     satisfied = false
+
     while low < high
-      mid = low + ((high - low) / 2).truncate
-      val = self[mid]
-      v = block.call(val)
-      if v.is_a?(Integer)
-        return val if v == 0
-        smaller = v < 0
-      elsif v == true
+      mid = ((low+high)/2).truncate
+      res = block.call self[mid]
+
+      case res
+      when 0 # find-any mode: Found!
+        return mid
+      when Numeric # find-any mode: Continue...
+        in_lower_half = res < 0
+      when true # find-min mode
+        in_lower_half = true
         satisfied = true
-        smaller = true
-      elsif v == false || v.nil?
-        smaller = false
+      when false, nil # find-min mode
+        in_lower_half = false
+      else
+        raise TypeError, 'invalid block result (must be numeric, true, false or nil)'
       end
-      if smaller
+
+      if in_lower_half
         high = mid
       else
         low = mid + 1
       end
     end
-    return nil if low == self.size
-    return nil unless satisfied
-    self[low]
+
+    satisfied ? low : nil
   end
 
   ##
@@ -640,7 +681,7 @@ class Array
   #     scores.delete_if {|score| score < 80 }   #=> [97]
 
   def delete_if(&block)
-    return to_enum :delete_if unless block_given?
+    return to_enum :delete_if unless block
 
     idx = 0
     while idx < self.size do
@@ -669,7 +710,7 @@ class Array
   #     a.keep_if { |val| val > 3 } #=> [4, 5]
 
   def keep_if(&block)
-    return to_enum :keep_if unless block_given?
+    return to_enum :keep_if unless block
 
     idx = 0
     len = self.size
@@ -698,13 +739,17 @@ class Array
   #  If no block is given, an Enumerator is returned instead.
 
   def select!(&block)
-    return to_enum :select! unless block_given?
+    return to_enum :select! unless block
 
     result = []
-    self.each do |x|
-      result << x if block.call(x)
+    idx = 0
+    len = size
+    while idx < len
+      elem = self[idx]
+      result << elem if block.call(elem)
+      idx += 1
     end
-    return nil if self.size == result.size
+    return nil if len == result.size
     self.replace(result)
   end
 
@@ -726,8 +771,9 @@ class Array
 
     if block
       idx = 0
-      self.each do |*e|
-        return idx if block.call(*e)
+      len = size
+      while idx < len
+        return idx if block.call self[idx]
         idx += 1
       end
     else
@@ -762,4 +808,128 @@ class Array
       n
     end
   end
+
+  ##
+  # call-seq:
+  #    ary.permutation { |p| block }          -> ary
+  #    ary.permutation                        -> Enumerator
+  #    ary.permutation(n) { |p| block }       -> ary
+  #    ary.permutation(n)                     -> Enumerator
+  #
+  # When invoked with a block, yield all permutations of length +n+ of the
+  # elements of the array, then return the array itself.
+  #
+  # If +n+ is not specified, yield all permutations of all elements.
+  #
+  # The implementation makes no guarantees about the order in which the
+  # permutations are yielded.
+  #
+  # If no block is given, an Enumerator is returned instead.
+  #
+  # Examples:
+  #
+  #  a = [1, 2, 3]
+  #  a.permutation.to_a    #=> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
+  #  a.permutation(1).to_a #=> [[1],[2],[3]]
+  #  a.permutation(2).to_a #=> [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]]
+  #  a.permutation(3).to_a #=> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
+  #  a.permutation(0).to_a #=> [[]] # one permutation of length 0
+  #  a.permutation(4).to_a #=> []   # no permutations of length 4
+  def permutation(n=self.size, &block)
+    size = self.size
+    return to_enum(:permutation, n) unless block
+    return if n > size
+    if n == 0
+       yield []
+    else
+      i = 0
+      while i<size
+        result = [self[i]]
+        if n-1 > 0
+          ary = self[0...i] + self[i+1..-1]
+          ary.permutation(n-1) do |c|
+            yield result + c
+          end
+        else
+          yield result
+        end
+        i += 1
+      end
+    end
+  end
+
+  ##
+  # call-seq:
+  #    ary.combination(n) { |c| block }    -> ary
+  #    ary.combination(n)                  -> Enumerator
+  #
+  # When invoked with a block, yields all combinations of length +n+ of elements
+  # from the array and then returns the array itself.
+  #
+  # The implementation makes no guarantees about the order in which the
+  # combinations are yielded.
+  #
+  # If no block is given, an Enumerator is returned instead.
+  #
+  # Examples:
+  #
+  #    a = [1, 2, 3, 4]
+  #    a.combination(1).to_a  #=> [[1],[2],[3],[4]]
+  #    a.combination(2).to_a  #=> [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]
+  #    a.combination(3).to_a  #=> [[1,2,3],[1,2,4],[1,3,4],[2,3,4]]
+  #    a.combination(4).to_a  #=> [[1,2,3,4]]
+  #    a.combination(0).to_a  #=> [[]] # one combination of length 0
+  #    a.combination(5).to_a  #=> []   # no combinations of length 5
+
+  def combination(n, &block)
+    size = self.size
+    return to_enum(:combination, n) unless block
+    return if n > size
+    if n == 0
+       yield []
+    elsif n == 1
+      i = 0
+      while i<size
+        yield [self[i]]
+        i += 1
+      end
+    else
+      i = 0
+      while i<size
+        result = [self[i]]
+        self[i+1..-1].combination(n-1) do |c|
+          yield result + c
+        end
+        i += 1
+      end
+    end
+  end
+
+  ##
+  # call-seq:
+  #    ary.transpose -> new_ary
+  #
+  # Assumes that self is an array of arrays and transposes the rows and columns.
+  #
+  # If the length of the subarrays don’t match, an IndexError is raised.
+  #
+  # Examples:
+  #
+  #    a = [[1,2], [3,4], [5,6]]
+  #    a.transpose   #=> [[1, 3, 5], [2, 4, 6]]
+
+  def transpose
+    return [] if empty?
+
+    column_count = nil
+    self.each do |row|
+      raise TypeError unless row.is_a?(Array)
+      column_count ||= row.count
+      raise IndexError, 'element size differs' unless column_count == row.count
+    end
+
+    Array.new(column_count) do |column_index|
+      self.map { |row| row[column_index] }
+    end
+  end
 end
index 72e5080..169f968 100644 (file)
@@ -127,19 +127,20 @@ mrb_ary_to_h(mrb_state *mrb, mrb_value ary)
   hash = mrb_hash_new_capa(mrb, 0);
 
   for (i = 0; i < RARRAY_LEN(ary); ++i) {
-    v = mrb_check_array_type(mrb, RARRAY_PTR(ary)[i]);
+    mrb_value elt = RARRAY_PTR(ary)[i];
+    v = mrb_check_array_type(mrb, elt);
 
     if (mrb_nil_p(v)) {
       mrb_raisef(mrb, E_TYPE_ERROR, "wrong element type %S at %S (expected array)",
-        mrb_str_new_cstr(mrb,  mrb_obj_classname(mrb, ary_elt(ary, i))),
-        mrb_fixnum_value(i)
+                 mrb_str_new_cstr(mrb,  mrb_obj_classname(mrb, elt)),
+                 mrb_fixnum_value(i)
       );
     }
 
     if (RARRAY_LEN(v) != 2) {
       mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong array length at %S (expected 2, was %S)",
-        mrb_fixnum_value(i),
-        mrb_fixnum_value(RARRAY_LEN(v))
+                 mrb_fixnum_value(i),
+                 mrb_fixnum_value(RARRAY_LEN(v))
       );
     }
 
@@ -175,14 +176,16 @@ mrb_ary_slice_bang(mrb_state *mrb, mrb_value self)
 {
   struct RArray *a = mrb_ary_ptr(self);
   mrb_int i, j, k, len, alen = ARY_LEN(a);
-  mrb_value index;
   mrb_value val;
   mrb_value *ptr;
   mrb_value ary;
 
   mrb_ary_modify(mrb, a);
 
-  if (mrb_get_args(mrb, "o|i", &index, &len) == 1) {
+  if (mrb_get_argc(mrb) == 1) {
+    mrb_value index;
+
+    mrb_get_args(mrb, "o|i", &index, &len);
     switch (mrb_type(index)) {
     case MRB_TT_RANGE:
       if (mrb_range_beg_len(mrb, index, &i, &len, alen, TRUE) == 1) {
@@ -200,7 +203,7 @@ mrb_ary_slice_bang(mrb_state *mrb, mrb_value self)
     }
   }
 
-  i = mrb_fixnum(index);
+  mrb_get_args(mrb, "ii", &i, &len);
  delete_pos_len:
   if (i < 0) i += alen;
   if (i < 0 || alen < i) return mrb_nil_value();
index 95a796c..7d810ac 100644 (file)
@@ -228,18 +228,47 @@ end
 
 assert("Array#bsearch") do
   # Find minimum mode
-  a = [0, 4, 7, 10, 12]
-  assert_include [4, 7], a.bsearch {|x| x >= 4 }
-  assert_equal 7, a.bsearch {|x| x >= 6 }
-  assert_equal 0, a.bsearch {|x| x >= -1 }
-  assert_nil a.bsearch {|x| x >= 100 }
+  a = [0, 2, 4]
+  assert_equal 0, a.bsearch{ |x| x >= -1 }
+  assert_equal 0, a.bsearch{ |x| x >= 0 }
+  assert_equal 2, a.bsearch{ |x| x >= 1 }
+  assert_equal 2, a.bsearch{ |x| x >= 2 }
+  assert_equal 4, a.bsearch{ |x| x >= 3 }
+  assert_equal 4, a.bsearch{ |x| x >= 4 }
+  assert_nil      a.bsearch{ |x| x >= 5 }
 
   # Find any mode
-  a = [0, 4, 7, 10, 12]
-  assert_include [4, 7], a.bsearch {|x| 1 - (x / 4).truncate }
-  assert_nil a.bsearch {|x| 4 - (x / 2).truncate }
-  assert_equal(nil, a.bsearch {|x| 1 })
-  assert_equal(nil, a.bsearch {|x| -1 })
+  a = [0, 4, 8]
+  def between(lo, x, hi)
+    if x < lo
+      1
+    elsif x > hi
+      -1
+    else
+      0
+    end
+  end
+  assert_nil      a.bsearch{ |x| between(-3, x, -1) }
+  assert_equal 0, a.bsearch{ |x| between(-1, x,  1) }
+  assert_nil      a.bsearch{ |x| between( 1, x,  3) }
+  assert_equal 4, a.bsearch{ |x| between( 3, x,  5) }
+  assert_nil      a.bsearch{ |x| between( 5, x,  7) }
+  assert_equal 8, a.bsearch{ |x| between( 7, x,  9) }
+  assert_nil      a.bsearch{ |x| between( 9, x, 11) }
+
+  assert_equal 0, a.bsearch{ |x| between( 0, x,  3) }
+  assert_equal 4, a.bsearch{ |x| between( 0, x,  4) }
+  assert_equal 4, a.bsearch{ |x| between( 4, x,  8) }
+  assert_equal 8, a.bsearch{ |x| between( 5, x,  8) }
+
+  # Invalid block result
+  assert_raise TypeError, 'invalid block result (must be numeric, true, false or nil)' do
+    a.bsearch{ 'I like to watch the world burn' }
+  end
+end
+
+assert("Array#bsearch_index") do
+  # tested through Array#bsearch
 end
 
 assert("Array#delete_if") do
@@ -352,3 +381,42 @@ assert("Array#slice!") do
   assert_equal(i, [1, 2, 3])
   assert_equal(j, nil)
 end
+
+assert("Array#permutation") do
+  a = [1, 2, 3]
+  assert_equal([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]],
+               a.permutation.to_a)
+  assert_equal([[1],[2],[3]],
+               a.permutation(1).to_a)
+  assert_equal([[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]],
+               a.permutation(2).to_a)
+  assert_equal([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]],
+               a.permutation(3).to_a)
+  assert_equal([[]], a.permutation(0).to_a)
+  assert_equal([], a.permutation(4).to_a)
+end
+
+assert("Array#combination") do
+  a = [1, 2, 3, 4]
+  assert_equal([[1],[2],[3],[4]],
+               a.combination(1).to_a)
+  assert_equal([[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]],
+               a.combination(2).to_a)
+  assert_equal([[1,2,3],[1,2,4],[1,3,4],[2,3,4]],
+               a.combination(3).to_a)
+  assert_equal([[1,2,3,4]],
+               a.combination(4).to_a)
+  assert_equal([[]], a.combination(0).to_a)
+  assert_equal([], a.combination(5).to_a)
+end
+
+assert('Array#transpose') do
+  assert_equal([].transpose, [])
+  assert_equal([[]].transpose, [])
+  assert_equal([[1]].transpose, [[1]])
+  assert_equal([[1,2,3]].transpose, [[1], [2], [3]])
+  assert_equal([[1], [2], [3]].transpose, [[1,2,3]])
+  assert_equal([[1,2], [3,4], [5,6]].transpose, [[1,3,5], [2,4,6]])
+  assert_raise(TypeError) { [1].transpose }
+  assert_raise(IndexError) { [[1], [2,3,4]].transpose } 
+end
index 1bc96c4..0d4aad0 100644 (file)
@@ -91,7 +91,7 @@ assert('mruby-bin-debugger(print) error') do
   # test case
   tc = []
   tc << {:cmd=>"p (1+2",  :exp=>'$1 = SyntaxError'}
-  tc << {:cmd=>"p bar",   :exp=>'$2 = NoMethodError'}
+  tc << {:cmd=>"p bar",   :exp=>'$2 = (eval):2: undefined method'}
 
   BinTest_MrubyBinDebugger.test(src, tc)
 end
@@ -346,8 +346,8 @@ assert('mruby-bin-debugger(print) Literal:Numeric') do
 
   tc << {:cmd=>"p 3.14",    :exp=>'$8 = 3.14'}
   tc << {:cmd=>"p -12.3",   :exp=>'$9 = -12.3'}
-  tc << {:cmd=>"p +12.000", :exp=>'$10 = 12.0'}
-  tc << {:cmd=>"p 1e4",     :exp=>'$11 = 10000.0'}
+  tc << {:cmd=>"p +12.000", :exp=>'$10 = 12'}
+  tc << {:cmd=>"p 1e4",     :exp=>'$11 = 10000'}
   tc << {:cmd=>"p -0.1e-2", :exp=>'$12 = -0.001'}
 
   BinTest_MrubyBinDebugger.test(src, tc)
@@ -368,28 +368,28 @@ SRC
 
   tc << {:cmd=>'p "str"',        :exp=>'$1 = "str"'}
   tc << {:cmd=>'p "s\tt\rr\n"',  :exp=>'$2 = "s\\tt\\rr\\n"'}
-  tc << {:cmd=>'p "\C-a\C-z"',   :exp=>'$3 = "\\001\\032"'}
+  tc << {:cmd=>'p "\C-a\C-z"',   :exp=>'$3 = "\\x01\\x1a"'}
   tc << {:cmd=>'p "#{foo+bar}"', :exp=>'$4 = "foobar"'}
 
   tc << {:cmd=>'p \'str\'',          :exp=>'$5 = "str"'}
   tc << {:cmd=>'p \'s\\tt\\rr\\n\'', :exp=>'$6 = "s\\\\tt\\\\rr\\\\n"'}
   tc << {:cmd=>'p \'\\C-a\\C-z\'',   :exp=>'$7 = "\\\\C-a\\\\C-z"'}
-  tc << {:cmd=>'p \'#{foo+bar}\'',   :exp=>'$8 = "#{foo+bar}"'}
+  tc << {:cmd=>'p \'#{foo+bar}\'',   :exp=>'$8 = "\\#{foo+bar}"'}
 
   tc << {:cmd=>'p %!str!',        :exp=>'$9 = "str"'}
   tc << {:cmd=>'p %!s\tt\rr\n!',  :exp=>'$10 = "s\\tt\\rr\\n"'}
-  tc << {:cmd=>'p %!\C-a\C-z!',   :exp=>'$11 = "\\001\\032"'}
+  tc << {:cmd=>'p %!\C-a\C-z!',   :exp=>'$11 = "\\x01\\x1a"'}
   tc << {:cmd=>'p %!#{foo+bar}!', :exp=>'$12 = "foobar"'}
 
   tc << {:cmd=>'p %Q!str!',        :exp=>'$13 = "str"'}
   tc << {:cmd=>'p %Q!s\tt\rr\n!',  :exp=>'$14 = "s\\tt\\rr\\n"'}
-  tc << {:cmd=>'p %Q!\C-a\C-z!',   :exp=>'$15 = "\\001\\032"'}
+  tc << {:cmd=>'p %Q!\C-a\C-z!',   :exp=>'$15 = "\\x01\\x1a"'}
   tc << {:cmd=>'p %Q!#{foo+bar}!', :exp=>'$16 = "foobar"'}
 
   tc << {:cmd=>'p %q!str!',          :exp=>'$17 = "str"'}
   tc << {:cmd=>'p %q!s\\tt\\rr\\n!', :exp=>'$18 = "s\\\\tt\\\\rr\\\\n"'}
   tc << {:cmd=>'p %q!\\C-a\\C-z!',   :exp=>'$19 = "\\\\C-a\\\\C-z"'}
-  tc << {:cmd=>'p %q!#{foo+bar}!',   :exp=>'$20 = "#{foo+bar}"'}
+  tc << {:cmd=>'p %q!#{foo+bar}!',   :exp=>'$20 = "\\#{foo+bar}"'}
 
   BinTest_MrubyBinDebugger.test(src, tc)
 end
@@ -410,7 +410,7 @@ SRC
   tc << {:cmd=>'p []',                      :exp=>'$1 = []'}
   tc << {:cmd=>'p [ 5,  12,   8,    10, ]', :exp=>'$2 = [5, 12, 8, 10]'}
   tc << {:cmd=>'p [1,2.5,"#{foo+bar}"]',    :exp=>'$3 = [1, 2.5, "foobar"]'}
-  tc << {:cmd=>'p %w[3.14 A\ &\ B #{foo}]', :exp=>'$4 = ["3.14", "A & B", "#{foo}"]'}
+  tc << {:cmd=>'p %w[3.14 A\ &\ B #{foo}]', :exp=>'$4 = ["3.14", "A & B", "\#{foo}"]'}
   tc << {:cmd=>'p %W[3.14 A\ &\ B #{foo}]', :exp=>'$5 = ["3.14", "A & B", "foo"]'}
 
   BinTest_MrubyBinDebugger.test(src, tc)
@@ -588,7 +588,7 @@ SRC
   tc << {:cmd=>'p foo=[foo,bar,baz]', :exp=>'$2 = ["foo", "bar", "baz"]'}
 
   tc << {:cmd=>'p undefined=-1',      :exp=>'$3 = -1'}
-  tc << {:cmd=>'p "#{undefined}"',    :exp=>'$4 = NoMethodError'}
+  tc << {:cmd=>'p "#{undefined}"',    :exp=>'$4 = (eval):2: undefined method'}
 
   BinTest_MrubyBinDebugger.test(src, tc)
 end
@@ -626,7 +626,7 @@ SRC
   tc << {:cmd=>'p [a,b]',           :exp=>'$13 = [20, 10]'}
 
   tc << {:cmd=>'p undefined=-1',    :exp=>'$14 = -1'}
-  tc << {:cmd=>'p "#{undefined}"',  :exp=>'$15 = NoMethodError'}
+  tc << {:cmd=>'p "#{undefined}"',  :exp=>'$15 = (eval):2: undefined method'}
 
   BinTest_MrubyBinDebugger.test(src, tc)
 end
@@ -694,7 +694,7 @@ SRC
   tc << {:cmd=>'p [a,b]',           :exp=>'$13 = [20, 10]'}
 
   tc << {:cmd=>'p undefined=-1',    :exp=>'$14 = -1'}
-  tc << {:cmd=>'p "#{undefined}"',  :exp=>'$15 = NoMethodError'}
+  tc << {:cmd=>'p "#{undefined}"',  :exp=>'$15 = (eval):2: undefined method'}
 
   BinTest_MrubyBinDebugger.test(src, tc)
 end
index 4553a96..d3ccf08 100644 (file)
@@ -112,23 +112,12 @@ check_file_lineno(struct mrb_irep *irep, const char *file, uint16_t lineno)
   return result;
 }
 
-static const char*
-get_class_name(mrb_state *mrb, struct RClass *class_obj)
-{
-  struct RClass *outer;
-  mrb_sym class_sym;
-
-  outer = mrb_class_outer_module(mrb, class_obj);
-  class_sym = mrb_class_sym(mrb, class_obj, outer);
-  return mrb_sym2name(mrb, class_sym);
-}
-
 static int32_t
 compare_break_method(mrb_state *mrb, mrb_debug_breakpoint *bp, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc)
 {
   const char* class_name;
   const char* method_name;
-  struct RProc* m;
+  mrb_method_t m;
   struct RClass* sc;
   const char* sn;
   mrb_sym ssym;
@@ -139,7 +128,7 @@ compare_break_method(mrb_state *mrb, mrb_debug_breakpoint *bp, struct RClass *cl
 
   method_p = &bp->point.methodpoint;
   if (strcmp(method_p->method_name, method_name) == 0) {
-    class_name = get_class_name(mrb, class_obj);
+    class_name = mrb_class_name(mrb, class_obj);
     if (class_name == NULL) {
       if (method_p->class_name == NULL) {
         return bp->bpno;
@@ -147,10 +136,10 @@ compare_break_method(mrb_state *mrb, mrb_debug_breakpoint *bp, struct RClass *cl
     }
     else if (method_p->class_name != NULL) {
       m = mrb_method_search_vm(mrb, &class_obj, method_sym);
-      if (m == NULL) {
+      if (MRB_METHOD_UNDEF_P(m)) {
         return MRB_DEBUG_OK;
       }
-      if (MRB_PROC_CFUNC_P(m)) {
+      if (MRB_METHOD_CFUNC_P(m)) {
         *isCfunc = TRUE;
       }
 
@@ -162,12 +151,12 @@ compare_break_method(mrb_state *mrb, mrb_debug_breakpoint *bp, struct RClass *cl
       sc = mrb_class_get(mrb, method_p->class_name);
       ssym = mrb_symbol(mrb_check_intern_cstr(mrb, method_p->method_name));
       m = mrb_method_search_vm(mrb, &sc, ssym);
-      if (m == NULL) {
+      if (MRB_METHOD_UNDEF_P(m)) {
         return MRB_DEBUG_OK;
       }
 
-      class_name = get_class_name(mrb, class_obj);
-      sn = get_class_name(mrb, sc);
+      class_name = mrb_class_name(mrb, class_obj);
+      sn = mrb_class_name(mrb, sc);
       if (strcmp(sn, class_name) == 0) {
         return bp->bpno;
       }
@@ -204,7 +193,7 @@ mrb_debug_set_break_line(mrb_state *mrb, mrb_debug_context *dbg, const char *fil
     return MRB_DEBUG_BREAK_INVALID_LINENO;
   }
 
-  set_file = mrb_malloc(mrb, strlen(file) + 1);
+  set_file = (char*)mrb_malloc(mrb, strlen(file) + 1);
 
   index = dbg->bpnum;
   dbg->bp[index].bpno = dbg->next_bpno;
@@ -241,14 +230,14 @@ mrb_debug_set_break_method(mrb_state *mrb, mrb_debug_context *dbg, const char *c
   }
 
   if (class_name != NULL) {
-    set_class = mrb_malloc(mrb, strlen(class_name) + 1);
+    set_class = (char*)mrb_malloc(mrb, strlen(class_name) + 1);
     strncpy(set_class, class_name, strlen(class_name) + 1);
   }
   else {
     set_class = NULL;
   }
 
-  set_method = mrb_malloc(mrb, strlen(method_name) + 1);
+  set_method = (char*)mrb_malloc(mrb, strlen(method_name) + 1);
 
   strncpy(set_method, method_name, strlen(method_name) + 1);
 
@@ -440,7 +429,7 @@ static mrb_bool
 check_start_pc_for_line(mrb_irep *irep, mrb_code *pc, uint16_t line)
 {
   if (pc > irep->iseq) {
-    if (line == mrb_debug_get_line(irep, (uint32_t)(pc - irep->iseq - 1))) {
+    if (line == mrb_debug_get_line(irep, pc - irep->iseq - 1)) {
       return FALSE;
     }
   }
@@ -514,5 +503,3 @@ mrb_debug_check_breakpoint_method(mrb_state *mrb, mrb_debug_context *dbg, struct
 
   return 0;
 }
-
-
index 33e9575..21fe641 100644 (file)
@@ -48,7 +48,7 @@ build_path(mrb_state *mrb, const char *dir, const char *base)
     len += strlen(dir) + sizeof("/") - 1;
   }
 
-  path = mrb_malloc(mrb, len);
+  path = (char*)mrb_malloc(mrb, len);
   memset(path, 0, len);
 
   if (strcmp(dir, ".")) {
@@ -64,7 +64,8 @@ static char*
 dirname(mrb_state *mrb, const char *path)
 {
   size_t len;
-  char *p, *dir;
+  const char *p;
+  char *dir;
 
   if (path == NULL) {
     return NULL;
@@ -73,7 +74,7 @@ dirname(mrb_state *mrb, const char *path)
   p = strrchr(path, '/');
   len = p != NULL ? (size_t)(p - path) : strlen(path);
 
-  dir = mrb_malloc(mrb, len + 1);
+  dir = (char*)mrb_malloc(mrb, len + 1);
   strncpy(dir, path, len);
   dir[len] = '\0';
 
@@ -83,9 +84,9 @@ dirname(mrb_state *mrb, const char *path)
 static source_file*
 source_file_new(mrb_state *mrb, mrb_debug_context *dbg, char *filename)
 {
-  source_file *file = NULL;
+  source_file *file;
 
-  file = mrb_malloc(mrb, sizeof(source_file));
+  file = (source_file*)mrb_malloc(mrb, sizeof(source_file));
 
   memset(file, '\0', sizeof(source_file));
   file->fp = fopen(filename, "rb");
@@ -96,7 +97,7 @@ source_file_new(mrb_state *mrb, mrb_debug_context *dbg, char *filename)
   }
 
   file->lineno = 1;
-  file->path = mrb_malloc(mrb, strlen(filename) + 1);
+  file->path = (char*)mrb_malloc(mrb, strlen(filename) + 1);
   strcpy(file->path, filename);
   return file;
 }
@@ -174,9 +175,13 @@ mrb_debug_get_source(mrb_state *mrb, mrdb_state *mrdb, const char *srcpath, cons
   FILE *fp;
   const char *search_path[3];
   char *path = NULL;
+  const char *srcname = strrchr(filename, '/');
+
+  if (srcname) srcname++;
+  else srcname = filename;
 
   search_path[0] = srcpath;
-  search_path[1] = dirname(mrb, mrb_debug_get_filename(mrdb->dbg->root_irep, 0));
+  search_path[1] = dirname(mrb, mrb_debug_get_filename(mrdb->dbg->irep, 0));
   search_path[2] = ".";
 
   for (i = 0; i < 3; i++) {
@@ -184,7 +189,7 @@ mrb_debug_get_source(mrb_state *mrb, mrdb_state *mrdb, const char *srcpath, cons
       continue;
     }
 
-    if ((path = build_path(mrb, search_path[i], filename)) == NULL) {
+    if ((path = build_path(mrb, search_path[i], srcname)) == NULL) {
       continue;
     }
 
index a9c895b..c870053 100644 (file)
@@ -21,7 +21,7 @@ mrdb_check_syntax(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size
   c = mrbc_context_new(mrb);
   c->no_exec = TRUE;
   c->capture_errors = TRUE;
-  c->filename = (char*)dbg->prvfile;
+  mrbc_filename(mrb, c, (const char*)dbg->prvfile);
   c->lineno = dbg->prvline;
 
   /* Load program */
index 6bbe4cf..8e59017 100644 (file)
@@ -274,7 +274,7 @@ parse_breakcommand(mrdb_state *mrdb, const char **file, uint32_t *line, char **c
       STRTOUL(l, body);
       if (l <= 65535) {
         *line = l;
-        *file = (body == args)? mrb_debug_get_filename(dbg->irep, (uint32_t)(dbg->pc - dbg->irep->iseq)): args;
+        *file = (body == args)? mrb_debug_get_filename(dbg->irep, dbg->pc - dbg->irep->iseq): args;
       }
       else {
         puts(BREAK_ERR_MSG_RANGEOVER);
index 5984b62..0a86456 100644 (file)
@@ -133,7 +133,7 @@ typedef struct listcmd_parser_state {
 static listcmd_parser_state*
 listcmd_parser_state_new(mrb_state *mrb)
 {
-  listcmd_parser_state *st = mrb_malloc(mrb, sizeof(listcmd_parser_state));
+  listcmd_parser_state *st = (listcmd_parser_state*)mrb_malloc(mrb, sizeof(listcmd_parser_state));
   memset(st, 0, sizeof(listcmd_parser_state));
   return st;
 }
@@ -227,7 +227,7 @@ parse_filename(mrb_state *mrb, char **sp, listcmd_parser_state *st)
   }
 
   if (len > 0) {
-    st->filename = mrb_malloc(mrb, len + 1);
+    st->filename = (char*)mrb_malloc(mrb, len + 1);
     strncpy(st->filename, *sp, len);
     st->filename[len] = '\0';
     *sp += len;
@@ -242,7 +242,8 @@ char*
 replace_ext(mrb_state *mrb, const char *filename, const char *ext)
 {
   size_t len;
-  char *p, *s;
+  const char *p;
+  char *s;
 
   if (filename == NULL) {
     return NULL;
@@ -255,7 +256,7 @@ replace_ext(mrb_state *mrb, const char *filename, const char *ext)
     len = strlen(filename);
   }
 
-  s = mrb_malloc(mrb, len + strlen(ext) + 1);
+  s = (char*)mrb_malloc(mrb, len + strlen(ext) + 1);
   memset(s, '\0', len + strlen(ext) + 1);
   strncpy(s, filename, len);
   strcat(s, ext);
@@ -325,7 +326,7 @@ parse_listcmd_args(mrb_state *mrb, mrdb_state *mrdb, listcmd_parser_state *st)
 static mrb_bool
 check_cmd_pattern(const char *pattern, const char *cmd)
 {
-  char *lbracket, *rbracket, *p, *q;
+  const char *lbracket, *rbracket, *p, *q;
 
   if (pattern == NULL && cmd == NULL) {
     return TRUE;
index 3177d7a..0588dfc 100644 (file)
@@ -184,7 +184,7 @@ cleanup(mrb_state *mrb, struct _args *args)
 static mrb_debug_context*
 mrb_debug_context_new(mrb_state *mrb)
 {
-  mrb_debug_context *dbg = mrb_malloc(mrb, sizeof(mrb_debug_context));
+  mrb_debug_context *dbg = (mrb_debug_context*)mrb_malloc(mrb, sizeof(mrb_debug_context));
 
   memset(dbg, 0, sizeof(mrb_debug_context));
 
@@ -223,12 +223,12 @@ mrb_debug_context_free(mrb_state *mrb)
 static mrdb_state*
 mrdb_state_new(mrb_state *mrb)
 {
-  mrdb_state *mrdb = mrb_malloc(mrb, sizeof(mrdb_state));
+  mrdb_state *mrdb = (mrdb_state*)mrb_malloc(mrb, sizeof(mrdb_state));
 
   memset(mrdb, 0, sizeof(mrdb_state));
 
   mrdb->dbg = mrb_debug_context_get(mrb);
-  mrdb->command = mrb_malloc(mrb, MAX_COMMAND_LINE+1);
+  mrdb->command = (char*)mrb_malloc(mrb, MAX_COMMAND_LINE+1);
   mrdb->print_no = 1;
 
   return mrdb;
@@ -566,8 +566,8 @@ mrb_code_fetch_hook(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *reg
     dbg->xphase = DBG_PHASE_RUNNING;
   }
 
-  file = mrb_debug_get_filename(irep, (uint32_t)(pc - irep->iseq));
-  line = mrb_debug_get_line(irep, (uint32_t)(pc - irep->iseq));
+  file = mrb_debug_get_filename(irep, pc - irep->iseq);
+  line = mrb_debug_get_line(irep, pc - irep->iseq);
 
   switch (dbg->xm) {
   case DBG_STEP:
index fe311d8..a58a72e 100644 (file)
 #include <readline/history.h>
 #define MIRB_ADD_HISTORY(line) add_history(line)
 #define MIRB_READLINE(ch) readline(ch)
+#if !defined(RL_READLINE_VERSION) || RL_READLINE_VERSION < 0x600
+/* libedit & older readline do not have rl_free() */
+#define MIRB_LINE_FREE(line) free(line)
+#else
+#define MIRB_LINE_FREE(line) rl_free(line)
+#endif
 #define MIRB_WRITE_HISTORY(path) write_history(path)
 #define MIRB_READ_HISTORY(path) read_history(path)
 #define MIRB_USING_HISTORY() using_history()
@@ -27,6 +33,7 @@
 #include <linenoise.h>
 #define MIRB_ADD_HISTORY(line) linenoiseHistoryAdd(line)
 #define MIRB_READLINE(ch) linenoise(ch)
+#define MIRB_LINE_FREE(line) linenoiseFree(line)
 #define MIRB_WRITE_HISTORY(path) linenoiseHistorySave(path)
 #define MIRB_READ_HISTORY(path) linenoiseHistoryLoad(history_path)
 #define MIRB_USING_HISTORY()
@@ -88,6 +95,7 @@ static void
 p(mrb_state *mrb, mrb_value obj, int prompt)
 {
   mrb_value val;
+  char* msg;
 
   val = mrb_funcall(mrb, obj, "inspect", 0);
   if (prompt) {
@@ -101,7 +109,9 @@ p(mrb_state *mrb, mrb_value obj, int prompt)
   if (!mrb_string_p(val)) {
     val = mrb_obj_as_string(mrb, obj);
   }
-  fwrite(RSTRING_PTR(val), RSTRING_LEN(val), 1, stdout);
+  msg = mrb_locale_from_utf8(RSTRING_PTR(val), RSTRING_LEN(val));
+  fwrite(msg, strlen(msg), 1, stdout);
+  mrb_locale_free(msg);
   putc('\n', stdout);
 }
 
@@ -489,7 +499,7 @@ main(int argc, char **argv)
     strcpy(last_code_line, line);
     strcat(last_code_line, "\n");
     MIRB_ADD_HISTORY(line);
-    free(line);
+    MIRB_LINE_FREE(line);
 #endif
 
 done:
@@ -528,9 +538,17 @@ done:
       /* no evaluation of code */
     }
     else {
+      if (0 < parser->nwarn) {
+        /* warning */
+        char* msg = mrb_locale_from_utf8(parser->warn_buffer[0].message, -1);
+        printf("line %d: %s\n", parser->warn_buffer[0].lineno, msg);
+        mrb_utf8_free(msg);
+      }
       if (0 < parser->nerr) {
         /* syntax error */
-        printf("line %d: %s\n", parser->error_buffer[0].lineno, parser->error_buffer[0].message);
+        char* msg = mrb_locale_from_utf8(parser->error_buffer[0].message, -1);
+        printf("line %d: %s\n", parser->error_buffer[0].lineno, msg);
+        mrb_utf8_free(msg);
       }
       else {
         /* generate bytecode */
@@ -544,6 +562,13 @@ done:
         if (args.verbose) {
           mrb_codedump_all(mrb, proc);
         }
+        /* adjust stack length of toplevel environment */
+        if (mrb->c->cibase->env) {
+          struct REnv *e = mrb->c->cibase->env;
+          if (e && MRB_ENV_STACK_LEN(e) < proc->body.irep->nlocals) {
+            MRB_ENV_SET_STACK_LEN(e, proc->body.irep->nlocals);
+          }
+        }
         /* pass a proc for evaluation */
         /* evaluate the bytecode */
         result = mrb_vm_run(mrb,
@@ -577,6 +602,8 @@ done:
   mrb_free(mrb, history_path);
 #endif
 
+  if (args.rfp) fclose(args.rfp);
+  mrb_free(mrb, args.argv);
   mrbc_context_free(mrb, cxt);
   mrb_close(mrb);
 
index 1fd2bc5..deb66d5 100644 (file)
@@ -16,7 +16,7 @@ struct strip_args {
 static void
 irep_remove_lv(mrb_state *mrb, mrb_irep *irep)
 {
-  size_t i;
+  int i;
 
   if (irep->lv) {
     mrb_free(mrb, irep->lv);
index 5506c48..705db99 100644 (file)
@@ -15,6 +15,42 @@ mrb_mod_singleton_class_p(mrb_state *mrb, mrb_value self)
   return mrb_bool_value(mrb_type(self) == MRB_TT_SCLASS);
 }
 
+/*
+ *  call-seq:
+ *     module_exec(arg...) {|var...| block } -> obj
+ *     class_exec(arg...) {|var...| block } -> obj
+ *
+ * Evaluates the given block in the context of the
+ * class/module. The method defined in the block will belong
+ * to the receiver. Any arguments passed to the method will be
+ * passed to the block. This can be used if the block needs to
+ * access instance variables.
+ *
+ *     class Thing
+ *     end
+ *     Thing.class_exec{
+ *       def hello() "Hello there!" end
+ *     }
+ *     puts Thing.new.hello()
+ */
+
+static mrb_value
+mrb_mod_module_exec(mrb_state *mrb, mrb_value self)
+{
+  const mrb_value *argv;
+  mrb_int argc;
+  mrb_value blk;
+
+  mrb_get_args(mrb, "*&", &argv, &argc, &blk);
+
+  if (mrb_nil_p(blk)) {
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
+  }
+
+  mrb->c->ci->target_class = mrb_class_ptr(self);
+  return mrb_yield_cont(mrb, blk, self, argc, argv);
+}
+
 void
 mrb_mruby_class_ext_gem_init(mrb_state *mrb)
 {
@@ -22,6 +58,8 @@ mrb_mruby_class_ext_gem_init(mrb_state *mrb)
 
   mrb_define_method(mrb, mod, "name", mrb_mod_name, MRB_ARGS_NONE());
   mrb_define_method(mrb, mod, "singleton_class?", mrb_mod_singleton_class_p, MRB_ARGS_NONE());
+  mrb_define_method(mrb, mod, "module_exec", mrb_mod_module_exec, MRB_ARGS_ANY()|MRB_ARGS_BLOCK());
+  mrb_define_method(mrb, mod, "class_exec", mrb_mod_module_exec, MRB_ARGS_ANY()|MRB_ARGS_BLOCK());
 }
 
 void
index 65abde1..ed6713a 100644 (file)
@@ -32,3 +32,24 @@ assert 'Module#singleton_class?' do
   assert_false cls.singleton_class?
   assert_true scl.singleton_class?
 end
+
+assert 'Module#module_eval' do
+  mod = Module.new
+  mod.class_exec(1,2,3) do |a,b,c|
+    assert_equal([1,2,3], [a,b,c])
+    def hi
+      "hi"
+    end
+  end
+  cls = Class.new
+  cls.class_exec(42) do |x|
+    assert_equal(42, x)
+    include mod
+    def hello
+      "hello"
+    end
+  end
+  obj = cls.new
+  assert_equal("hi", obj.hi)
+  assert_equal("hello", obj.hello)
+end
diff --git a/third-party/mruby/mrbgems/mruby-compar-ext/mrbgem.rake b/third-party/mruby/mrbgems/mruby-compar-ext/mrbgem.rake
new file mode 100644 (file)
index 0000000..dcf5843
--- /dev/null
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-compar-ext') do |spec|
+  spec.license = 'MIT'
+  spec.author  = 'mruby developers'
+  spec.summary = 'Enumerable module extension'
+end
diff --git a/third-party/mruby/mrbgems/mruby-compar-ext/mrblib/compar.rb b/third-party/mruby/mrbgems/mruby-compar-ext/mrblib/compar.rb
new file mode 100644 (file)
index 0000000..d66f816
--- /dev/null
@@ -0,0 +1,31 @@
+module Comparable
+  ##
+  # Returns <i>min</i> if <i>obj</i> <code><=></code> <i>min</i> is less
+  # than zero, <i>max</i> if <i>obj</i> <code><=></code> <i>max</i> is
+  # greater than zero and <i>obj</i> otherwise.
+  #
+  #     12.clamp(0, 100)         #=> 12
+  #     523.clamp(0, 100)        #=> 100
+  #     -3.123.clamp(0, 100)     #=> 0
+  #
+  #     'd'.clamp('a', 'f')      #=> 'd'
+  #     'z'.clamp('a', 'f')      #=> 'f'
+  #
+  def clamp(min, max)
+    if (min <=> max) > 0
+      raise ArgumentError, "min argument must be smaller than max argument"
+    end
+    c = self <=> min
+    if c == 0
+      return self
+    elsif c < 0
+      return min
+    end
+    c = self <=> max
+    if c > 0
+      return max
+    else
+      return self
+    end
+  end
+end
index a1045c3..eb82110 100644 (file)
@@ -66,9 +66,7 @@ typedef struct scope {
   int icapa;
 
   mrb_irep *irep;
-  size_t pcapa;
-  size_t scapa;
-  size_t rcapa;
+  int pcapa, scapa, rcapa;
 
   uint16_t nlocals;
   uint16_t nregs;
@@ -151,8 +149,14 @@ new_label(codegen_scope *s)
 static inline int
 genop(codegen_scope *s, mrb_code i)
 {
-  if (s->pc == s->icapa) {
+  if (s->pc >= s->icapa) {
     s->icapa *= 2;
+    if (s->pc >= MAXARG_sBx) {
+      codegen_error(s, "too big code block");
+    }
+    if (s->icapa > MAXARG_sBx) {
+      s->icapa = MAXARG_sBx;
+    }
     s->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->icapa);
     if (s->lines) {
       s->lines = (uint16_t*)codegen_realloc(s, s->lines, sizeof(short)*s->icapa);
@@ -280,6 +284,19 @@ genop_peep(codegen_scope *s, mrb_code i, int val)
         }
       }
       break;
+    case OP_GETUPVAR:
+      if (c0 == OP_SETUPVAR) {
+        if (GETARG_B(i) == GETARG_B(i0) && GETARG_C(i) == GETARG_C(i0)) {
+          if (GETARG_A(i) == GETARG_A(i0)) {
+            /* just skip OP_SETUPVAR */
+            return 0;
+          }
+          else {
+            return genop(s, MKOP_AB(OP_MOVE, GETARG_A(i), GETARG_A(i0)));
+          }
+        }
+      }
+      break;
     case OP_EPOP:
       if (c0 == OP_EPOP) {
         s->iseq[s->pc-1] = MKOP_A(OP_EPOP, GETARG_A(i0)+GETARG_A(i));
@@ -337,6 +354,14 @@ genop_peep(codegen_scope *s, mrb_code i, int val)
           s->iseq[s->pc-1] = MKOP_ABC(OP_SUBI, GETARG_A(i), GETARG_B(i), -c);
         return 0;
       }
+      break;
+    case OP_ARYCAT:
+    case OP_ARYPUSH:
+      if (c0 == OP_MOVE && GETARG_A(i0) >= s->nlocals) {
+        s->iseq[s->pc-1] = MKOP_AB(c1, GETARG_A(i), GETARG_B(i0));
+        return 0;
+      }
+      break;
     case OP_STRCAT:
       if (c0 == OP_STRING) {
         mrb_value v = s->irep->pool[GETARG_Bx(i0)];
@@ -373,6 +398,14 @@ scope_error(codegen_scope *s)
   exit(EXIT_FAILURE);
 }
 
+static void
+distcheck(codegen_scope *s, int diff)
+{
+  if (diff > MAXARG_sBx || diff < -MAXARG_sBx) {
+    codegen_error(s, "too distant jump address");
+  }
+}
+
 static inline void
 dispatch(codegen_scope *s, int pc)
 {
@@ -394,9 +427,7 @@ dispatch(codegen_scope *s, int pc)
     scope_error(s);
     break;
   }
-  if (diff > MAXARG_sBx) {
-    codegen_error(s, "too distant jump address");
-  }
+  distcheck(s, diff);
   s->iseq[pc] = MKOP_AsBx(c, GETARG_A(i), diff);
 }
 
@@ -428,7 +459,7 @@ push_(codegen_scope *s)
 }
 
 static void
-push_n_(codegen_scope *s, size_t n)
+push_n_(codegen_scope *s, int n)
 {
   if (s->sp+n > 511) {
     codegen_error(s, "too complex expression");
@@ -447,7 +478,7 @@ push_n_(codegen_scope *s, size_t n)
 static inline int
 new_lit(codegen_scope *s, mrb_value val)
 {
-  size_t i;
+  int i;
   mrb_value *pv;
 
   switch (mrb_type(val)) {
@@ -462,6 +493,7 @@ new_lit(codegen_scope *s, mrb_value val)
         return i;
     }
     break;
+#ifndef MRB_WITHOUT_FLOAT
   case MRB_TT_FLOAT:
     for (i=0; i<s->irep->plen; i++) {
       pv = &s->irep->pool[i];
@@ -469,6 +501,7 @@ new_lit(codegen_scope *s, mrb_value val)
       if (mrb_float(*pv) == mrb_float(val)) return i;
     }
     break;
+#endif
   case MRB_TT_FIXNUM:
     for (i=0; i<s->irep->plen; i++) {
       pv = &s->irep->pool[i];
@@ -494,11 +527,13 @@ new_lit(codegen_scope *s, mrb_value val)
     *pv = mrb_str_pool(s->mrb, val);
     break;
 
+#ifndef MRB_WITHOUT_FLOAT
   case MRB_TT_FLOAT:
 #ifdef MRB_WORD_BOXING
     *pv = mrb_float_pool(s->mrb, mrb_float(val));
     break;
 #endif
+#endif
   case MRB_TT_FIXNUM:
     *pv = val;
     break;
@@ -518,7 +553,7 @@ new_lit(codegen_scope *s, mrb_value val)
 static int
 new_msym(codegen_scope *s, mrb_sym sym)
 {
-  size_t i, len;
+  int i, len;
 
   mrb_assert(s->irep);
 
@@ -539,7 +574,7 @@ new_msym(codegen_scope *s, mrb_sym sym)
 static int
 new_sym(codegen_scope *s, mrb_sym sym)
 {
-  size_t i;
+  int i;
 
   for (i=0; i<s->irep->slen; i++) {
     if (s->irep->syms[i] == sym) return i;
@@ -573,8 +608,8 @@ node_len(node *tree)
   return n;
 }
 
-#define sym(x) ((mrb_sym)(intptr_t)(x))
-#define lv_name(lv) sym((lv)->car)
+#define nsym(x) ((mrb_sym)(intptr_t)(x))
+#define lv_name(lv) nsym((lv)->car)
 static int
 lv_idx(codegen_scope *s, mrb_sym id)
 {
@@ -633,6 +668,7 @@ for_body(codegen_scope *s, node *tree)
   scope_finish(s);
   s = prev;
   genop(s, MKOP_Abc(OP_LAMBDA, cursp(), s->irep->rlen-1, OP_L_BLOCK));
+  push();pop(); /* space for a block */
   pop();
   idx = new_msym(s, mrb_intern_lit(s->mrb, "each"));
   genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, 0));
@@ -701,7 +737,7 @@ lambda_body(codegen_scope *s, node *tree, int blk)
 
       dispatch(s, pos+i);
       codegen(s, opt->car->cdr, VAL);
-      idx = lv_idx(s, (mrb_sym)(intptr_t)opt->car->car);
+      idx = lv_idx(s, nsym(opt->car->car));
       pop();
       genop_peep(s, MKOP_AB(OP_MOVE, idx, cursp()), NOVAL);
       i++;
@@ -764,11 +800,14 @@ scope_body(codegen_scope *s, node *tree, int val)
   return s->irep->rlen - 1;
 }
 
+#define nint(x) ((int)(intptr_t)(x))
+#define nchar(x) ((char)(intptr_t)(x))
+
 static mrb_bool
 nosplat(node *t)
 {
   while (t) {
-    if ((intptr_t)t->car->car == NODE_SPLAT) return FALSE;
+    if (nint(t->car->car) == NODE_SPLAT) return FALSE;
     t = t->cdr;
   }
   return TRUE;
@@ -804,12 +843,12 @@ gen_values(codegen_scope *s, node *t, int val, int extra)
   int is_splat;
 
   while (t) {
-    is_splat = (intptr_t)t->car->car == NODE_SPLAT; /* splat mode */
+    is_splat = nint(t->car->car) == NODE_SPLAT; /* splat mode */
     if (
       n+extra >= CALL_MAXARGS - 1 /* need to subtract one because vm.c expects an array if n == CALL_MAXARGS */
       || is_splat) {
       if (val) {
-        if (is_splat && n == 0 && (intptr_t)t->car->cdr->car == NODE_ARRAY) {
+        if (is_splat && n == 0 && nint(t->car->cdr->car) == NODE_ARRAY) {
           codegen(s, t->car->cdr, VAL);
           pop();
         }
@@ -820,10 +859,10 @@ gen_values(codegen_scope *s, node *t, int val, int extra)
           codegen(s, t->car, VAL);
           pop(); pop();
           if (is_splat) {
-            genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
+            genop_peep(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1), NOVAL);
           }
           else {
-            genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+            genop_peep(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1), NOVAL);
           }
         }
         t = t->cdr;
@@ -831,11 +870,11 @@ gen_values(codegen_scope *s, node *t, int val, int extra)
           push();
           codegen(s, t->car, VAL);
           pop(); pop();
-          if ((intptr_t)t->car->car == NODE_SPLAT) {
-            genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
+          if (nint(t->car->car) == NODE_SPLAT) {
+            genop_peep(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1), NOVAL);
           }
           else {
-            genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+            genop_peep(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1), NOVAL);
           }
           t = t->cdr;
         }
@@ -859,7 +898,7 @@ gen_values(codegen_scope *s, node *t, int val, int extra)
 static void
 gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
 {
-  mrb_sym sym = name ? name : sym(tree->cdr->car);
+  mrb_sym sym = name ? name : nsym(tree->cdr->car);
   int idx, skip = 0;
   int n = 0, noop = 0, sendv = 0, blk = 0;
 
@@ -869,7 +908,7 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
     genop(s, MKOP_A(OP_LOADNIL, cursp()));
     push();
     genop(s, MKOP_AB(OP_MOVE, cursp(), recv));
-    push(); pop();              /* space for a block */
+    push_n(2); pop_n(2); /* space for one arg and a block */
     pop();
     idx = new_msym(s, mrb_intern_lit(s->mrb, "=="));
     genop(s, MKOP_ABC(OP_EQ, cursp(), idx, 1));
@@ -959,16 +998,16 @@ static void
 gen_assignment(codegen_scope *s, node *tree, int sp, int val)
 {
   int idx;
-  int type = (intptr_t)tree->car;
+  int type = nint(tree->car);
 
   tree = tree->cdr;
   switch (type) {
   case NODE_GVAR:
-    idx = new_sym(s, sym(tree));
+    idx = new_sym(s, nsym(tree));
     genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val);
     break;
   case NODE_LVAR:
-    idx = lv_idx(s, sym(tree));
+    idx = lv_idx(s, nsym(tree));
     if (idx > 0) {
       if (idx != sp) {
         genop_peep(s, MKOP_AB(OP_MOVE, idx, sp), val);
@@ -980,7 +1019,7 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
       codegen_scope *up = s->prev;
 
       while (up) {
-        idx = lv_idx(up, sym(tree));
+        idx = lv_idx(up, nsym(tree));
         if (idx > 0) {
           genop_peep(s, MKOP_ABC(OP_SETUPVAR, sp, idx, lv), val);
           break;
@@ -991,19 +1030,19 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
     }
     break;
   case NODE_IVAR:
-    idx = new_sym(s, sym(tree));
+    idx = new_sym(s, nsym(tree));
     genop_peep(s, MKOP_ABx(OP_SETIV, sp, idx), val);
     break;
   case NODE_CVAR:
-    idx = new_sym(s, sym(tree));
+    idx = new_sym(s, nsym(tree));
     genop_peep(s, MKOP_ABx(OP_SETCV, sp, idx), val);
     break;
   case NODE_CONST:
-    idx = new_sym(s, sym(tree));
+    idx = new_sym(s, nsym(tree));
     genop_peep(s, MKOP_ABx(OP_SETCONST, sp, idx), val);
     break;
   case NODE_COLON2:
-    idx = new_sym(s, sym(tree->cdr));
+    idx = new_sym(s, nsym(tree->cdr));
     genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), NOVAL);
     push();
     codegen(s, tree->car, VAL);
@@ -1014,7 +1053,7 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
   case NODE_CALL:
   case NODE_SCALL:
     push();
-    gen_call(s, tree, attrsym(s, sym(tree->cdr->car)), sp, NOVAL,
+    gen_call(s, tree, attrsym(s, nsym(tree->cdr->car)), sp, NOVAL,
              type == NODE_SCALL);
     pop();
     if (val) {
@@ -1094,6 +1133,7 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
 static void
 gen_send_intern(codegen_scope *s)
 {
+  push();pop(); /* space for a block */
   pop();
   genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "intern")), 0));
   push();
@@ -1105,9 +1145,9 @@ gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
     int i = 0, j = 0;
 
     while (tree) {
-      switch ((intptr_t)tree->car->car) {
+      switch (nint(tree->car->car)) {
       case NODE_STR:
-        if ((tree->cdr == NULL) && ((intptr_t)tree->car->cdr->cdr == 0))
+        if ((tree->cdr == NULL) && (nint(tree->car->cdr->cdr) == 0))
           break;
         /* fall through */
       case NODE_BEGIN:
@@ -1143,7 +1183,7 @@ gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
   }
   else {
     while (tree) {
-      switch ((intptr_t)tree->car->car) {
+      switch (nint(tree->car->car)) {
       case NODE_BEGIN: case NODE_BLOCK:
         codegen(s, tree->car, NOVAL);
       }
@@ -1160,6 +1200,7 @@ raise_error(codegen_scope *s, const char *msg)
   genop(s, MKOP_ABx(OP_ERR, 1, idx));
 }
 
+#ifndef MRB_WITHOUT_FLOAT
 static double
 readint_float(codegen_scope *s, const char *p, int base)
 {
@@ -1185,6 +1226,7 @@ readint_float(codegen_scope *s, const char *p, int base)
   }
   return f;
 }
+#endif
 
 static mrb_int
 readint_mrb_int(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_bool *overflow)
@@ -1232,7 +1274,7 @@ readint_mrb_int(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_boo
 static void
 gen_retval(codegen_scope *s, node *tree)
 {
-  if ((intptr_t)tree->car == NODE_SPLAT) {
+  if (nint(tree->car) == NODE_SPLAT) {
     genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), 0));
     push();
     codegen(s, tree, VAL);
@@ -1271,7 +1313,7 @@ codegen(codegen_scope *s, node *tree, int val)
     s->filename = mrb_parser_get_filename(s->parser, tree->filename_index);
   }
 
-  nt = (intptr_t)tree->car;
+  nt = nint(tree->car);
   s->lineno = tree->lineno;
   tree = tree->cdr;
   switch (nt) {
@@ -1316,9 +1358,10 @@ codegen(codegen_scope *s, node *tree, int val)
           if (pos1) dispatch(s, pos1);
           pos2 = 0;
           do {
-            if (n4 && n4->car && (intptr_t)n4->car->car == NODE_SPLAT) {
+            if (n4 && n4->car && nint(n4->car->car) == NODE_SPLAT) {
               codegen(s, n4->car, VAL);
               genop(s, MKOP_AB(OP_MOVE, cursp(), exc));
+              push_n(2); pop_n(2); /* space for one arg and a block */
               pop();
               genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1));
             }
@@ -1333,6 +1376,7 @@ codegen(codegen_scope *s, node *tree, int val)
               pop();
               genop(s, MKOP_ABC(OP_RESCUE, exc, cursp(), 1));
             }
+            distcheck(s, pos2);
             tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
             pos2 = tmp;
             if (n4) {
@@ -1350,6 +1394,7 @@ codegen(codegen_scope *s, node *tree, int val)
             codegen(s, n3->cdr->cdr->car, val);
             if (val) pop();
           }
+          distcheck(s, exend);
           tmp = genop(s, MKOP_sBx(OP_JMP, exend));
           exend = tmp;
           n2 = n2->cdr;
@@ -1377,7 +1422,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
   case NODE_ENSURE:
     if (!tree->cdr || !tree->cdr->cdr ||
-        ((intptr_t)tree->cdr->cdr->car == NODE_BEGIN &&
+        (nint(tree->cdr->cdr->car) == NODE_BEGIN &&
          tree->cdr->cdr->cdr)) {
       int idx;
       int epush = s->pc;
@@ -1422,7 +1467,7 @@ codegen(codegen_scope *s, node *tree, int val)
         codegen(s, e, val);
         goto exit;
       }
-      switch ((intptr_t)tree->car->car) {
+      switch (nint(tree->car->car)) {
       case NODE_TRUE:
       case NODE_INT:
       case NODE_STR:
@@ -1495,6 +1540,7 @@ codegen(codegen_scope *s, node *tree, int val)
       dispatch(s, lp->pc1);
       codegen(s, tree->car, VAL);
       pop();
+      distcheck(s, lp->pc2 - s->pc);
       genop(s, MKOP_AsBx(OP_JMPIF, cursp(), lp->pc2 - s->pc));
 
       loop_pop(s, val);
@@ -1511,6 +1557,7 @@ codegen(codegen_scope *s, node *tree, int val)
       dispatch(s, lp->pc1);
       codegen(s, tree->car, VAL);
       pop();
+      distcheck(s, lp->pc2 - s->pc);
       genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), lp->pc2 - s->pc));
 
       loop_pop(s, val);
@@ -1541,8 +1588,9 @@ codegen(codegen_scope *s, node *tree, int val)
           codegen(s, n->car, VAL);
           if (head) {
             genop(s, MKOP_AB(OP_MOVE, cursp(), head));
+            push_n(2); pop_n(2); /* space for one arg and a block */
             pop();
-            if ((intptr_t)n->car->car == NODE_SPLAT) {
+            if (nint(n->car->car) == NODE_SPLAT) {
               genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1));
             }
             else {
@@ -1552,6 +1600,7 @@ codegen(codegen_scope *s, node *tree, int val)
           else {
             pop();
           }
+          distcheck(s, pos2);
           tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
           pos2 = tmp;
           n = n->cdr;
@@ -1562,6 +1611,7 @@ codegen(codegen_scope *s, node *tree, int val)
         }
         codegen(s, tree->car->cdr, val);
         if (val) pop();
+        distcheck(s, pos3);
         tmp = genop(s, MKOP_sBx(OP_JMP, pos3));
         pos3 = tmp;
         if (pos1) dispatch(s, pos1);
@@ -1622,7 +1672,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
   case NODE_COLON2:
     {
-      int sym = new_sym(s, sym(tree->cdr));
+      int sym = new_sym(s, nsym(tree->cdr));
 
       codegen(s, tree->car, VAL);
       pop();
@@ -1633,7 +1683,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
   case NODE_COLON3:
     {
-      int sym = new_sym(s, sym(tree));
+      int sym = new_sym(s, nsym(tree));
 
       genop(s, MKOP_A(OP_OCLASS, cursp()));
       genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
@@ -1709,7 +1759,7 @@ codegen(codegen_scope *s, node *tree, int val)
       node *t = tree->cdr, *p;
       int rhs = cursp();
 
-      if ((intptr_t)t->car == NODE_ARRAY && t->cdr && nosplat(t->cdr)) {
+      if (nint(t->car) == NODE_ARRAY && t->cdr && nosplat(t->cdr)) {
         /* fixed rhs */
         t = t->cdr;
         while (t) {
@@ -1783,14 +1833,14 @@ codegen(codegen_scope *s, node *tree, int val)
 
   case NODE_OP_ASGN:
     {
-      mrb_sym sym = sym(tree->cdr->car);
+      mrb_sym sym = nsym(tree->cdr->car);
       mrb_int len;
       const char *name = mrb_sym2name_len(s->mrb, sym, &len);
       int idx, callargs = -1, vsp = -1;
 
       if ((len == 2 && name[0] == '|' && name[1] == '|') &&
-          ((intptr_t)tree->car->car == NODE_CONST ||
-           (intptr_t)tree->car->car == NODE_CVAR)) {
+          (nint(tree->car->car) == NODE_CONST ||
+           nint(tree->car->car) == NODE_CVAR)) {
         int onerr, noexc, exc;
         struct loopinfo *lp;
 
@@ -1808,45 +1858,36 @@ codegen(codegen_scope *s, node *tree, int val)
         dispatch(s, noexc);
         loop_pop(s, NOVAL);
       }
-      else if ((intptr_t)tree->car->car == NODE_CALL) {
+      else if (nint(tree->car->car) == NODE_CALL) {
         node *n = tree->car->cdr;
+        int base, i, nargs = 0;
+        callargs = 0;
 
         if (val) {
           vsp = cursp();
           push();
         }
         codegen(s, n->car, VAL);   /* receiver */
-        idx = new_msym(s, sym(n->cdr->car));
+        idx = new_msym(s, nsym(n->cdr->car));
+        base = cursp()-1;
         if (n->cdr->cdr->car) {
-          int base = cursp()-1;
-          int nargs = gen_values(s, n->cdr->cdr->car->car, VAL, 1);
-
-          /* copy receiver and arguments */
+          nargs = gen_values(s, n->cdr->cdr->car->car, VAL, 1);
           if (nargs >= 0) {
-            int i;
-
-            genop(s, MKOP_AB(OP_MOVE, cursp(), base));
-            for (i=0; i<nargs; i++) {
-              genop(s, MKOP_AB(OP_MOVE, cursp()+i+1, base+i+1));
-            }
-            push_n(nargs+1);
-            pop_n(nargs+1);
             callargs = nargs;
           }
-          else {
-            /* varargs */
+          else { /* varargs */
             push();
-            genop(s, MKOP_AB(OP_MOVE, cursp(), base));
-            genop(s, MKOP_AB(OP_MOVE, cursp()+1, base+1));
+            nargs = 1;
             callargs = CALL_MAXARGS;
           }
-          genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
         }
-        else {
-          genop(s, MKOP_AB(OP_MOVE, cursp(), cursp()-1));
-          genop(s, MKOP_ABC(OP_SEND, cursp(), idx, 0));
-          callargs = 0;
+        /* copy receiver and arguments */
+        genop(s, MKOP_AB(OP_MOVE, cursp(), base));
+        for (i=0; i<nargs; i++) {
+          genop(s, MKOP_AB(OP_MOVE, cursp()+i+1, base+i+1));
         }
+        push_n(nargs+2);pop_n(nargs+2); /* space for receiver, arguments and a block */
+        genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
         push();
       }
       else {
@@ -1872,21 +1913,18 @@ codegen(codegen_scope *s, node *tree, int val)
         if (val && vsp >= 0) {
           genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
         }
-        if ((intptr_t)tree->car->car == NODE_CALL) {
-          mrb_sym m = sym(tree->car->cdr->cdr->car);
-          mrb_sym m2 = attrsym(s, m);
-
-          idx = new_msym(s, m2);
-          pop();
+        if (nint(tree->car->car) == NODE_CALL) {
           if (callargs == CALL_MAXARGS) {
-            genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
             pop();
-            genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+            genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
           }
           else {
             pop_n(callargs);
-            genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs+1));
+            callargs++;
           }
+          pop();
+          idx = new_msym(s, attrsym(s, nsym(tree->car->cdr->cdr->car)));
+          genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
         }
         else {
           gen_assignment(s, tree->car, cursp(), val);
@@ -1942,7 +1980,7 @@ codegen(codegen_scope *s, node *tree, int val)
           callargs++;
         }
         pop();
-        idx = new_msym(s, attrsym(s,sym(tree->car->cdr->cdr->car)));
+        idx = new_msym(s, attrsym(s,nsym(tree->car->cdr->cdr->car)));
         genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
       }
     }
@@ -2048,6 +2086,7 @@ codegen(codegen_scope *s, node *tree, int val)
           push();
         }
       }
+      push();pop(); /* space for a block */
       pop_n(n+1);
       genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf)));
       if (sendv) n = CALL_MAXARGS;
@@ -2070,6 +2109,7 @@ codegen(codegen_scope *s, node *tree, int val)
         genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
       }
       codegen(s, tree, NOVAL);
+      distcheck(s, s->loop->pc1 - s->pc);
       genop(s, MKOP_sBx(OP_JMP, s->loop->pc1 - s->pc));
     }
     else {
@@ -2093,6 +2133,7 @@ codegen(codegen_scope *s, node *tree, int val)
       if (s->ensure_level > s->loop->ensure_level) {
         genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
       }
+      distcheck(s, s->loop->pc2 - s->pc);
       genop(s, MKOP_sBx(OP_JMP, s->loop->pc2 - s->pc));
     }
     if (val) push();
@@ -2120,13 +2161,12 @@ codegen(codegen_scope *s, node *tree, int val)
         }
         else {
           if (n > 0) {
-            while (n--) {
-              genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL);
-            }
+            genop_peep(s, MKOP_A(OP_POPERR, n), NOVAL);
           }
           if (s->ensure_level > lp->ensure_level) {
             genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - lp->ensure_level), NOVAL);
           }
+          distcheck(s, s->loop->pc1 - s->pc);
           genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc));
         }
       }
@@ -2136,7 +2176,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
   case NODE_LVAR:
     if (val) {
-      int idx = lv_idx(s, sym(tree));
+      int idx = lv_idx(s, nsym(tree));
 
       if (idx > 0) {
         genop_peep(s, MKOP_AB(OP_MOVE, cursp(), idx), NOVAL);
@@ -2146,9 +2186,9 @@ codegen(codegen_scope *s, node *tree, int val)
         codegen_scope *up = s->prev;
 
         while (up) {
-          idx = lv_idx(up, sym(tree));
+          idx = lv_idx(up, nsym(tree));
           if (idx > 0) {
-            genop(s, MKOP_ABC(OP_GETUPVAR, cursp(), idx, lv));
+            genop_peep(s, MKOP_ABC(OP_GETUPVAR, cursp(), idx, lv), VAL);
             break;
           }
           lv++;
@@ -2161,7 +2201,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
   case NODE_GVAR:
     if (val) {
-      int sym = new_sym(s, sym(tree));
+      int sym = new_sym(s, nsym(tree));
 
       genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
       push();
@@ -2170,7 +2210,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
   case NODE_IVAR:
     if (val) {
-      int sym = new_sym(s, sym(tree));
+      int sym = new_sym(s, nsym(tree));
 
       genop(s, MKOP_ABx(OP_GETIV, cursp(), sym));
       push();
@@ -2179,7 +2219,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
   case NODE_CVAR:
     if (val) {
-      int sym = new_sym(s, sym(tree));
+      int sym = new_sym(s, nsym(tree));
 
       genop(s, MKOP_ABx(OP_GETCV, cursp(), sym));
       push();
@@ -2188,7 +2228,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
   case NODE_CONST:
     {
-      int sym = new_sym(s, sym(tree));
+      int sym = new_sym(s, nsym(tree));
 
       genop(s, MKOP_ABx(OP_GETCONST, cursp(), sym));
       if (val) {
@@ -2207,7 +2247,7 @@ codegen(codegen_scope *s, node *tree, int val)
       int sym;
 
       buf[0] = '$';
-      buf[1] = (char)(intptr_t)tree;
+      buf[1] = nchar(tree);
       buf[2] = 0;
       sym = new_sym(s, mrb_intern_cstr(s->mrb, buf));
       genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
@@ -2221,7 +2261,7 @@ codegen(codegen_scope *s, node *tree, int val)
       mrb_value str;
       int sym;
 
-      str = mrb_format(mrb, "$%S", mrb_fixnum_value((mrb_int)(intptr_t)tree));
+      str = mrb_format(mrb, "$%S", mrb_fixnum_value(nint(tree)));
       sym = new_sym(s, mrb_intern_str(mrb, str));
       genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
       push();
@@ -2239,19 +2279,22 @@ codegen(codegen_scope *s, node *tree, int val)
   case NODE_INT:
     if (val) {
       char *p = (char*)tree->car;
-      int base = (intptr_t)tree->cdr->car;
+      int base = nint(tree->cdr->car);
       mrb_int i;
       mrb_code co;
       mrb_bool overflow;
 
       i = readint_mrb_int(s, p, base, FALSE, &overflow);
+#ifndef MRB_WITHOUT_FLOAT
       if (overflow) {
         double f = readint_float(s, p, base);
         int off = new_lit(s, mrb_float_value(s->mrb, f));
 
         genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
       }
-      else {
+      else
+#endif
+      {
         if (i < MAXARG_sBx && i > -MAXARG_sBx) {
           co = MKOP_AsBx(OP_LOADI, cursp(), i);
         }
@@ -2265,6 +2308,7 @@ codegen(codegen_scope *s, node *tree, int val)
     }
     break;
 
+#ifndef MRB_WITHOUT_FLOAT
   case NODE_FLOAT:
     if (val) {
       char *p = (char*)tree;
@@ -2275,12 +2319,14 @@ codegen(codegen_scope *s, node *tree, int val)
       push();
     }
     break;
+#endif
 
   case NODE_NEGATE:
     {
-      nt = (intptr_t)tree->car;
+      nt = nint(tree->car);
       tree = tree->cdr;
       switch (nt) {
+#ifndef MRB_WITHOUT_FLOAT
       case NODE_FLOAT:
         if (val) {
           char *p = (char*)tree;
@@ -2291,16 +2337,18 @@ codegen(codegen_scope *s, node *tree, int val)
           push();
         }
         break;
+#endif
 
       case NODE_INT:
         if (val) {
           char *p = (char*)tree->car;
-          int base = (intptr_t)tree->cdr->car;
+          int base = nint(tree->cdr->car);
           mrb_int i;
           mrb_code co;
           mrb_bool overflow;
 
           i = readint_mrb_int(s, p, base, TRUE, &overflow);
+#ifndef MRB_WITHOUT_FLOAT
           if (overflow) {
             double f = readint_float(s, p, base);
             int off = new_lit(s, mrb_float_value(s->mrb, -f));
@@ -2308,6 +2356,7 @@ codegen(codegen_scope *s, node *tree, int val)
             genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
           }
           else {
+#endif
             if (i < MAXARG_sBx && i > -MAXARG_sBx) {
               co = MKOP_AsBx(OP_LOADI, cursp(), i);
             }
@@ -2316,7 +2365,9 @@ codegen(codegen_scope *s, node *tree, int val)
               co = MKOP_ABx(OP_LOADL, cursp(), off);
             }
             genop(s, co);
+#ifndef MRB_WITHOUT_FLOAT
           }
+#endif
           push();
         }
         break;
@@ -2378,7 +2429,7 @@ codegen(codegen_scope *s, node *tree, int val)
       node *n = tree;
 
       while (n) {
-        if ((intptr_t)n->car->car != NODE_STR) {
+        if (nint(n->car->car) != NODE_STR) {
           codegen(s, n->car, NOVAL);
         }
         n = n->cdr;
@@ -2405,7 +2456,7 @@ codegen(codegen_scope *s, node *tree, int val)
       codegen(s, tree->car, VAL);
       n = tree->cdr;
       while (n) {
-        if ((intptr_t)n->car->car == NODE_XSTR) {
+        if (nint(n->car->car) == NODE_XSTR) {
           n->car->car = (struct mrb_ast_node*)(intptr_t)NODE_STR;
           mrb_assert(!n->cdr); /* must be the end */
         }
@@ -2458,26 +2509,26 @@ codegen(codegen_scope *s, node *tree, int val)
       genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
       push();
       genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+      push();
       if (p2 || p3) {
-        push();
-        if (p2) {
+        if (p2) { /* opt */
           off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
           genop(s, MKOP_ABx(OP_STRING, cursp(), off));
         }
         else {
           genop(s, MKOP_A(OP_LOADNIL, cursp()));
         }
+        push();
         argc++;
-        if (p3) {
-          push();
+        if (p3) { /* enc */
           off = new_lit(s, mrb_str_new(s->mrb, p3, 1));
           genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+          push();
           argc++;
-          pop();
         }
-        pop();
       }
-      pop();
+      push(); /* space for a block */
+      pop_n(argc+2);
       sym = new_sym(s, mrb_intern_lit(s->mrb, "compile"));
       genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc));
       mrb_gc_arena_restore(s->mrb, ai);
@@ -2507,31 +2558,31 @@ codegen(codegen_scope *s, node *tree, int val)
         n = n->cdr;
       }
       n = tree->cdr->cdr;
-      if (n->car) {
+      if (n->car) { /* tail */
         p = (char*)n->car;
         off = new_lit(s, mrb_str_new_cstr(s->mrb, p));
         codegen(s, tree->car, VAL);
         genop(s, MKOP_ABx(OP_STRING, cursp(), off));
         pop();
         genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+        push();
       }
-      if (n->cdr->car) {
+      if (n->cdr->car) { /* opt */
         char *p2 = (char*)n->cdr->car;
-
-        push();
         off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
         genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+        push();
         argc++;
       }
-      if (n->cdr->cdr) {
+      if (n->cdr->cdr) { /* enc */
         char *p2 = (char*)n->cdr->cdr;
-
-        push();
         off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
         genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+        push();
         argc++;
       }
-      pop_n(argc);
+      push(); /* space for a block */
+      pop_n(argc+2);
       sym = new_sym(s, mrb_intern_lit(s->mrb, "compile"));
       genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc));
       mrb_gc_arena_restore(s->mrb, ai);
@@ -2541,7 +2592,7 @@ codegen(codegen_scope *s, node *tree, int val)
       node *n = tree->car;
 
       while (n) {
-        if ((intptr_t)n->car->car != NODE_STR) {
+        if (nint(n->car->car) != NODE_STR) {
           codegen(s, n->car, NOVAL);
         }
         n = n->cdr;
@@ -2551,7 +2602,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
   case NODE_SYM:
     if (val) {
-      int sym = new_sym(s, sym(tree));
+      int sym = new_sym(s, nsym(tree));
 
       genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
       push();
@@ -2595,8 +2646,8 @@ codegen(codegen_scope *s, node *tree, int val)
 
   case NODE_ALIAS:
     {
-      int a = new_msym(s, sym(tree->car));
-      int b = new_msym(s, sym(tree->cdr));
+      int a = new_msym(s, nsym(tree->car));
+      int b = new_msym(s, nsym(tree->cdr));
       int c = new_msym(s, mrb_intern_lit(s->mrb, "alias_method"));
 
       genop(s, MKOP_A(OP_TCLASS, cursp()));
@@ -2606,7 +2657,7 @@ codegen(codegen_scope *s, node *tree, int val)
       genop(s, MKOP_ABx(OP_LOADSYM, cursp(), b));
       push();
       genop(s, MKOP_A(OP_LOADNIL, cursp()));
-      push();
+      push(); /* space for a block */
       pop_n(4);
       genop(s, MKOP_ABC(OP_SEND, cursp(), c, 2));
       if (val) {
@@ -2629,7 +2680,7 @@ codegen(codegen_scope *s, node *tree, int val)
           pop_n(num);
           genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), num));
           while (t) {
-            symbol = new_msym(s, sym(t->car));
+            symbol = new_msym(s, nsym(t->car));
             push();
             genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol));
             pop();
@@ -2639,12 +2690,13 @@ codegen(codegen_scope *s, node *tree, int val)
           num = CALL_MAXARGS;
           break;
         }
-        symbol = new_msym(s, sym(t->car));
+        symbol = new_msym(s, nsym(t->car));
         genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol));
         push();
         t = t->cdr;
         num++;
       }
+      push();pop(); /* space for a block */
       pop();
       if (num < CALL_MAXARGS) {
         pop_n(num);
@@ -2679,7 +2731,7 @@ codegen(codegen_scope *s, node *tree, int val)
         push();
       }
       pop(); pop();
-      idx = new_msym(s, sym(tree->car->cdr));
+      idx = new_msym(s, nsym(tree->car->cdr));
       genop(s, MKOP_AB(OP_CLASS, cursp(), idx));
       idx = scope_body(s, tree->cdr->cdr->car, val);
       genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
@@ -2705,7 +2757,7 @@ codegen(codegen_scope *s, node *tree, int val)
         codegen(s, tree->car->car, VAL);
       }
       pop();
-      idx = new_msym(s, sym(tree->car->cdr));
+      idx = new_msym(s, nsym(tree->car->cdr));
       genop(s, MKOP_AB(OP_MODULE, cursp(), idx));
       idx = scope_body(s, tree->cdr->car, val);
       genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
@@ -2732,7 +2784,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
   case NODE_DEF:
     {
-      int sym = new_msym(s, sym(tree->car));
+      int sym = new_msym(s, nsym(tree->car));
       int idx = lambda_body(s, tree->cdr, 0);
 
       genop(s, MKOP_A(OP_TCLASS, cursp()));
@@ -2751,7 +2803,7 @@ codegen(codegen_scope *s, node *tree, int val)
   case NODE_SDEF:
     {
       node *recv = tree->car;
-      int sym = new_msym(s, sym(tree->cdr->car));
+      int sym = new_msym(s, nsym(tree->cdr->car));
       int idx = lambda_body(s, tree->cdr->cdr, 0);
 
       codegen(s, recv, VAL);
@@ -2938,23 +2990,32 @@ loop_break(codegen_scope *s, node *tree)
   }
   else {
     struct loopinfo *loop;
+    int n = 0;
 
     if (tree) {
       gen_retval(s, tree);
     }
 
     loop = s->loop;
-    while (loop && loop->type == LOOP_BEGIN) {
-      genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL);
-      loop = loop->prev;
-    }
-    while (loop && (loop->type == LOOP_RESCUE || loop->type == LOOP_BEGIN)) {
-      loop = loop->prev;
+    while (loop) {
+      if (loop->type == LOOP_BEGIN) {
+        n++;
+        loop = loop->prev;
+      }
+      else if (loop->type == LOOP_RESCUE) {
+        loop = loop->prev;
+      }
+      else{
+        break;
+      }
     }
     if (!loop) {
       raise_error(s, "unexpected break");
       return;
     }
+    if (n > 0) {
+      genop_peep(s, MKOP_A(OP_POPERR, n), NOVAL);
+    }
 
     if (loop->type == LOOP_NORMAL) {
       int tmp;
@@ -2965,6 +3026,7 @@ loop_break(codegen_scope *s, node *tree)
       if (tree) {
         genop_peep(s, MKOP_AB(OP_MOVE, loop->acc, cursp()), NOVAL);
       }
+      distcheck(s, s->loop->pc3);
       tmp = genop(s, MKOP_sBx(OP_JMP, loop->pc3));
       loop->pc3 = tmp;
     }
@@ -3011,6 +3073,9 @@ mrb_generate_code(mrb_state *mrb, parser_state *p)
     mrb_irep_decref(mrb, scope->irep);
     mrb_pool_close(scope->mpool);
     proc->c = NULL;
+    if (mrb->c->cibase && mrb->c->cibase->proc == proc->upper) {
+      proc->upper = NULL;
+    }
     mrb->jmp = prev_jmp;
     return proc;
   }
index 58e3029..2ff2664 100644 (file)
@@ -52,7 +52,7 @@ inline
 #endif
 #endif
 static unsigned int
-hash (register const char *str, register unsigned int len)
+hash (const char *str, unsigned int len)
 {
   static const unsigned char asso_values[] =
     {
@@ -83,7 +83,7 @@ hash (register const char *str, register unsigned int len)
       51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
       51, 51, 51, 51, 51, 51
     };
-  register int hval = len;
+  int hval = len;
 
   switch (hval)
     {
@@ -105,7 +105,7 @@ __attribute__ ((__gnu_inline__))
 #endif
 #endif
 const struct kwtable *
-mrb_reserved_word (register const char *str, register unsigned int len)
+mrb_reserved_word (const char *str, unsigned int len)
 {
   static const struct kwtable wordlist[] =
     {
@@ -196,11 +196,11 @@ mrb_reserved_word (register const char *str, register unsigned int len)
 
   if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
     {
-      register int key = hash (str, len);
+      int key = hash (str, len);
 
       if (key <= MAX_HASH_VALUE && key >= 0)
         {
-          register const char *s = wordlist[key].name;
+          const char *s = wordlist[key].name;
 
           if (*str == *s && !strcmp (str + 1, s + 1))
             return &wordlist[key];
@@ -209,4 +209,3 @@ mrb_reserved_word (register const char *str, register unsigned int len)
   return 0;
 }
 #line 50 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
-
index 5d5ef29..7f848b4 100644 (file)
@@ -740,16 +740,18 @@ new_int(parser_state *p, const char *s, int base)
   return list3((node*)NODE_INT, (node*)strdup(s), nint(base));
 }
 
+#ifndef MRB_WITHOUT_FLOAT
 /* (:float . i) */
 static node*
 new_float(parser_state *p, const char *s)
 {
   return cons((node*)NODE_FLOAT, (node*)strdup(s));
 }
+#endif
 
 /* (:str . (s . len)) */
 static node*
-new_str(parser_state *p, const char *s, int len)
+new_str(parser_state *p, const char *s, size_t len)
 {
   return cons((node*)NODE_STR, cons((node*)strndup(s, len), nint(len)));
 }
@@ -779,7 +781,7 @@ new_dxstr(parser_state *p, node *a)
 static node*
 new_dsym(parser_state *p, node *a)
 {
-  return cons((node*)NODE_DSYM, new_dstr(p, a));
+  return cons((node*)NODE_DSYM, a);
 }
 
 /* (:regx . (s . (opt . enc))) */
@@ -1104,9 +1106,9 @@ heredoc_end(parser_state *p)
         keyword__FILE__
         keyword__ENCODING__
 
-%token <id>  tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL
+%token <id>  tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL_TAG
 %token <nd>  tINTEGER tFLOAT tCHAR tXSTRING tREGEXP
-%token <nd>  tSTRING tSTRING_PART tSTRING_MID tLABEL_END
+%token <nd>  tSTRING tSTRING_PART tSTRING_MID
 %token <nd>  tNTH_REF tBACK_REF
 %token <num> tREGEXP_END
 
@@ -1178,7 +1180,7 @@ heredoc_end(parser_state *p)
 %right keyword_not
 %right '=' tOP_ASGN
 %left modifier_rescue
-%right '?' ':'
+%right '?' ':' tLABEL_TAG
 %nonassoc tDOT2 tDOT3
 %left  tOROP
 %left  tANDOP
@@ -1384,7 +1386,7 @@ command_asgn    : lhs '=' command_rhs
                       backref_error(p, $1);
                       $$ = new_begin(p, 0);
                     }
-               ;
+                ;
 
 command_rhs     : command_call   %prec tOP_ASGN
                 | command_call modifier_rescue stmt
@@ -1924,6 +1926,10 @@ arg             : lhs '=' arg_rhs
                     {
                       $$ = new_if(p, cond($1), $3, $6);
                     }
+                | arg '?' arg opt_nl tLABEL_TAG arg
+                    {
+                      $$ = new_if(p, cond($1), $3, $6);
+                    }
                 | primary
                     {
                       $$ = $1;
@@ -2566,7 +2572,7 @@ lambda_body     : tLAMBEG compstmt '}'
                     {
                       $$ = $2;
                     }
-                | keyword_do_LAMBDA compstmt keyword_end
+                | keyword_do_LAMBDA bodystmt keyword_end
                     {
                       $$ = $2;
                     }
@@ -2577,7 +2583,7 @@ do_block        : keyword_do_block
                       local_nest(p);
                     }
                   opt_block_param
-                  compstmt
+                  bodystmt
                   keyword_end
                     {
                       $$ = new_block(p,$3,$4);
@@ -2667,7 +2673,7 @@ brace_block     : '{'
                       $<num>$ = p->lineno;
                     }
                   opt_block_param
-                  compstmt keyword_end
+                  bodystmt keyword_end
                     {
                       $$ = new_block(p,$3,$4);
                       SET_LINENO($$, $<num>2);
@@ -2853,18 +2859,18 @@ words           : tWORDS_BEG tSTRING
 
 symbol          : basic_symbol
                     {
+                      p->lstate = EXPR_ENDARG;
                       $$ = new_sym(p, $1);
                     }
                 | tSYMBEG tSTRING_BEG string_rep tSTRING
                     {
-                      p->lstate = EXPR_END;
-                      $$ = new_dsym(p, push($3, $4));
+                      p->lstate = EXPR_ENDARG;
+                      $$ = new_dsym(p, new_dstr(p, push($3, $4)));
                     }
                 ;
 
 basic_symbol    : tSYMBEG sym
                     {
-                      p->lstate = EXPR_END;
                       $$ = $2;
                     }
                 ;
@@ -2968,6 +2974,15 @@ var_ref         : variable
                       snprintf(buf, sizeof(buf), "%d", p->lineno);
                       $$ = new_int(p, buf, 10);
                     }
+                | keyword__ENCODING__
+                    {
+#ifdef MRB_UTF8_STRING
+                      const char *enc = "UTF-8";
+#else
+                      const char *enc = "ASCII-8BIT";
+#endif
+                      $$ = new_str(p, enc, strlen(enc));
+                    }
                 ;
 
 backref         : tNTH_REF
@@ -3255,25 +3270,20 @@ assoc           : arg tASSOC arg
                       void_expr_error(p, $3);
                       $$ = cons($1, $3);
                     }
-                | tLABEL arg
-                    {
-                      void_expr_error(p, $2);
-                      $$ = cons(new_sym(p, $1), $2);
-                    }
-                | tLABEL_END arg
-                    {
-                      void_expr_error(p, $2);
-                      $$ = cons(new_sym(p, new_strsym(p, $1)), $2);
-                    }
-                | tSTRING_BEG tLABEL_END arg
+                | tIDENTIFIER tLABEL_TAG arg
                     {
                       void_expr_error(p, $3);
-                      $$ = cons(new_sym(p, new_strsym(p, $2)), $3);
+                      $$ = cons(new_sym(p, $1), $3);
                     }
-                | tSTRING_BEG string_rep tLABEL_END arg
+                | string tLABEL_TAG arg
                     {
-                      void_expr_error(p, $4);
-                      $$ = cons(new_dsym(p, push($2, $3)), $4);
+                      void_expr_error(p, $3);
+                      if ($1->car == (node*)NODE_DSTR) {
+                        $$ = cons(new_dsym(p, $1), $3);
+                      }
+                      else {
+                        $$ = cons(new_sym(p, new_strsym(p, $1)), $3);
+                      }
                     }
                 ;
 
@@ -3361,7 +3371,7 @@ static void
 yyerror(parser_state *p, const char *s)
 {
   char* c;
-  int n;
+  size_t n;
 
   if (! p->capture_errors) {
 #ifndef MRB_DISABLE_STDIO
@@ -3397,7 +3407,7 @@ static void
 yywarn(parser_state *p, const char *s)
 {
   char* c;
-  int n;
+  size_t n;
 
   if (! p->capture_errors) {
 #ifndef MRB_DISABLE_STDIO
@@ -3443,7 +3453,7 @@ backref_error(parser_state *p, node *n)
   c = (int)(intptr_t)n->car;
 
   if (c == NODE_NTH_REF) {
-    yyerror_i(p, "can't set variable $%" MRB_PRId, (mrb_int)(intptr_t)n->cdr);
+    yyerror_i(p, "can't set variable $%" MRB_PRId, (int)(intptr_t)n->cdr);
   }
   else if (c == NODE_BACK_REF) {
     yyerror_i(p, "can't set variable $%c", (int)(intptr_t)n->cdr);
@@ -3593,7 +3603,7 @@ peek_n(parser_state *p, int c, int n)
 static mrb_bool
 peeks(parser_state *p, const char *s)
 {
-  int len = strlen(s);
+  size_t len = strlen(s);
 
 #ifndef MRB_DISABLE_STDIO
   if (p->f) {
@@ -3629,7 +3639,7 @@ skips(parser_state *p, const char *s)
     }
     s++;
     if (peeks(p, s)) {
-      int len = strlen(s);
+      size_t len = strlen(s);
 
       while (len--) {
         if (nextc(p) == '\n') {
@@ -3751,28 +3761,28 @@ toklen(parser_state *p)
 #define IS_LABEL_POSSIBLE() ((p->lstate == EXPR_BEG && !cmd_state) || IS_ARG())
 #define IS_LABEL_SUFFIX(n) (peek_n(p, ':',(n)) && !peek_n(p, ':', (n)+1))
 
-static int
+static int32_t
 scan_oct(const int *start, int len, int *retlen)
 {
   const int *s = start;
-  int retval = 0;
+  int32_t retval = 0;
 
   /* mrb_assert(len <= 3) */
   while (len-- && *s >= '0' && *s <= '7') {
     retval <<= 3;
     retval |= *s++ - '0';
   }
-  *retlen = s - start;
+  *retlen = (int)(s - start);
 
   return retval;
 }
 
 static int32_t
-scan_hex(const int *start, int len, int *retlen)
+scan_hex(parser_state *p, const int *start, int len, int *retlen)
 {
   static const char hexdigit[] = "0123456789abcdef0123456789ABCDEF";
   const int *s = start;
-  int32_t retval = 0;
+  uint32_t retval = 0;
   char *tmp;
 
   /* mrb_assert(len <= 8) */
@@ -3781,22 +3791,26 @@ scan_hex(const int *start, int len, int *retlen)
     retval |= (tmp - hexdigit) & 15;
     s++;
   }
-  *retlen = s - start;
+  *retlen = (int)(s - start);
 
-  return retval;
+  return (int32_t)retval;
 }
 
 static int32_t
 read_escape_unicode(parser_state *p, int limit)
 {
-  int32_t c;
   int buf[9];
   int i;
+  int32_t hex;
 
   /* Look for opening brace */
   i = 0;
   buf[0] = nextc(p);
-  if (buf[0] < 0) goto eof;
+  if (buf[0] < 0) {
+  eof:
+    yyerror(p, "invalid escape character syntax");
+    return -1;
+  }
   if (ISXDIGIT(buf[0])) {
     /* \uxxxx form */
     for (i=1; i<limit; i++) {
@@ -3811,17 +3825,12 @@ read_escape_unicode(parser_state *p, int limit)
   else {
     pushback(p, buf[0]);
   }
-  c = scan_hex(buf, i, &i);
-  if (i == 0) {
-  eof:
-    yyerror(p, "Invalid escape character syntax");
-    return -1;
-  }
-  if (c < 0 || c > 0x10FFFF || (c & 0xFFFFF800) == 0xD800) {
-    yyerror(p, "Invalid Unicode code point");
+  hex = scan_hex(p, buf, i, &i);
+  if (i == 0 || hex > 0x10FFFF || (hex & 0xFFFFF800) == 0xD800) {
+    yyerror(p, "invalid Unicode code point");
     return -1;
   }
-  return c;
+  return hex;
 }
 
 /* Return negative to indicate Unicode code point */
@@ -3887,13 +3896,12 @@ read_escape(parser_state *p)
         break;
       }
     }
-    c = scan_hex(buf, i, &i);
     if (i == 0) {
-      yyerror(p, "Invalid escape character syntax");
-      return 0;
+      yyerror(p, "invalid hex escape");
+      return -1;
     }
+    return scan_hex(p, buf, i, &i);
   }
-  return c;
 
   case 'u':     /* Unicode */
     if (peek(p, '{')) {
@@ -3960,11 +3968,10 @@ parse_string(parser_state *p)
 {
   int c;
   string_type type = (string_type)(intptr_t)p->lex_strterm->car;
-  int nest_level = (intptr_t)p->lex_strterm->cdr->car;
-  int beg = (intptr_t)p->lex_strterm->cdr->cdr->car;
-  int end = (intptr_t)p->lex_strterm->cdr->cdr->cdr;
+  int nest_level = intn(p->lex_strterm->cdr->car);
+  int beg = intn(p->lex_strterm->cdr->cdr->car);
+  int end = intn(p->lex_strterm->cdr->cdr->cdr);
   parser_heredoc_info *hinf = (type & STR_FUNC_HEREDOC) ? parsing_heredoc_inf(p) : NULL;
-  int cmd_state = p->cmd_start;
 
   if (beg == 0) beg = -3;       /* should never happen */
   if (end == 0) end = -3;
@@ -4118,7 +4125,7 @@ parse_string(parser_state *p)
   }
 
   tokfix(p);
-  p->lstate = EXPR_END;
+  p->lstate = EXPR_ENDARG;
   end_strterm(p);
 
   if (type & STR_FUNC_XQUOTE) {
@@ -4179,13 +4186,6 @@ parse_string(parser_state *p)
     return tREGEXP;
   }
   pylval.nd = new_str(p, tok(p), toklen(p));
-  if (IS_LABEL_POSSIBLE()) {
-    if (IS_LABEL_SUFFIX(0)) {
-      p->lstate = EXPR_BEG;
-      nextc(p);
-      return tLABEL_END;
-    }
-  }
 
   return tSTRING;
 }
@@ -4605,7 +4605,7 @@ parser_yylex(parser_state *p)
     }
     tokfix(p);
     pylval.nd = new_str(p, tok(p), toklen(p));
-    p->lstate = EXPR_END;
+    p->lstate = EXPR_ENDARG;
     return tCHAR;
 
   case '&':
@@ -4754,7 +4754,7 @@ parser_yylex(parser_state *p)
     int is_float, seen_point, seen_e, nondigit;
 
     is_float = seen_point = seen_e = nondigit = 0;
-    p->lstate = EXPR_END;
+    p->lstate = EXPR_ENDARG;
     newtok(p);
     if (c == '-' || c == '+') {
       tokadd(p, c);
@@ -4955,6 +4955,11 @@ parser_yylex(parser_state *p)
     }
     tokfix(p);
     if (is_float) {
+#ifdef MRB_WITHOUT_FLOAT
+      yywarning_s(p, "floating point numbers are not supported", tok(p));
+      pylval.nd = new_int(p, "0", 10);
+      return tINTEGER;
+#else
       double d;
       char *endp;
 
@@ -4969,6 +4974,7 @@ parser_yylex(parser_state *p)
       }
       pylval.nd = new_float(p, tok(p));
       return tFLOAT;
+#endif
     }
     pylval.nd = new_int(p, tok(p), 10);
     return tINTEGER;
@@ -4984,7 +4990,7 @@ parser_yylex(parser_state *p)
     if (c == ')')
       p->lstate = EXPR_ENDFN;
     else
-      p->lstate = EXPR_ENDARG;
+      p->lstate = EXPR_END;
     return c;
 
   case ':':
@@ -4997,14 +5003,19 @@ parser_yylex(parser_state *p)
       p->lstate = EXPR_DOT;
       return tCOLON2;
     }
-    if (IS_END() || ISSPACE(c)) {
+    if (!space_seen && IS_END()) {
       pushback(p, c);
       p->lstate = EXPR_BEG;
-      return ':';
+      return tLABEL_TAG;
+    }
+    if (!ISSPACE(c) || IS_BEG()) {
+      pushback(p, c);
+      p->lstate = EXPR_FNAME;
+      return tSYMBEG;
     }
     pushback(p, c);
-    p->lstate = EXPR_FNAME;
-    return tSYMBEG;
+    p->lstate = EXPR_BEG;
+    return ':';
 
   case '/':
     if (IS_BEG()) {
@@ -5071,6 +5082,9 @@ parser_yylex(parser_state *p)
     else if (IS_SPCARG(-1)) {
       c = tLPAREN_ARG;
     }
+    else if (p->lstate == EXPR_END && space_seen) {
+      c = tLPAREN_ARG;
+    }
     p->paren_nest++;
     COND_PUSH(0);
     CMDARG_PUSH(0);
@@ -5425,11 +5439,10 @@ parser_yylex(parser_state *p)
 
       if (IS_LABEL_POSSIBLE()) {
         if (IS_LABEL_SUFFIX(0)) {
-          p->lstate = EXPR_BEG;
-          nextc(p);
+          p->lstate = EXPR_END;
           tokfix(p);
           pylval.id = intern_cstr(tok(p));
-          return tLABEL;
+          return tIDENTIFIER;
         }
       }
       if (p->lstate != EXPR_DOT) {
@@ -5490,11 +5503,9 @@ parser_yylex(parser_state *p)
       mrb_sym ident = intern_cstr(tok(p));
 
       pylval.id = ident;
-#if 0
-      if (last_state != EXPR_DOT && islower(tok(p)[0]) && lvar_defined(ident)) {
+      if (last_state != EXPR_DOT && islower(tok(p)[0]) && local_var_p(p, ident)) {
         p->lstate = EXPR_END;
       }
-#endif
     }
     return result;
   }
@@ -5674,7 +5685,7 @@ MRB_API const char*
 mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s)
 {
   if (s) {
-    int len = strlen(s);
+    size_t len = strlen(s);
     char *p = (char *)mrb_malloc(mrb, len + 1);
 
     memcpy(p, s, len + 1);
@@ -5706,16 +5717,16 @@ mrb_parser_set_filename(struct mrb_parser_state *p, const char *f)
 
   for (i = 0; i < p->filename_table_length; ++i) {
     if (p->filename_table[i] == sym) {
-      p->current_filename_index = i;
+      p->current_filename_index = (int)i;
       return;
     }
   }
 
-  p->current_filename_index = p->filename_table_length++;
+  p->current_filename_index = (int)p->filename_table_length++;
 
   new_table = (mrb_sym*)parser_palloc(p, sizeof(mrb_sym) * p->filename_table_length);
   if (p->filename_table) {
-    memmove(new_table, p->filename_table, sizeof(mrb_sym) * p->filename_table_length);
+    memmove(new_table, p->filename_table, sizeof(mrb_sym) * p->current_filename_index);
   }
   p->filename_table = new_table;
   p->filename_table[p->filename_table_length - 1] = sym;
@@ -5746,7 +5757,7 @@ mrb_parse_file(mrb_state *mrb, FILE *f, mrbc_context *c)
 #endif
 
 MRB_API parser_state*
-mrb_parse_nstring(mrb_state *mrb, const char *s, int len, mrbc_context *c)
+mrb_parse_nstring(mrb_state *mrb, const char *s, size_t len, mrbc_context *c)
 {
   parser_state *p;
 
@@ -5777,7 +5788,7 @@ mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c)
     return mrb_undef_value();
   }
   if (!p->tree || p->nerr) {
-    c->parser_nerr = p->nerr;
+    if (c) c->parser_nerr = p->nerr;
     if (p->capture_errors) {
       char buf[256];
       int n;
@@ -5817,7 +5828,7 @@ mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c)
       c->keep_lv = TRUE;
     }
   }
-  proc->target_class = target;
+  MRB_PROC_SET_TARGET_CLASS(proc, target);
   if (mrb->c->ci) {
     mrb->c->ci->target_class = target;
   }
@@ -5841,13 +5852,13 @@ mrb_load_file(mrb_state *mrb, FILE *f)
 #endif
 
 MRB_API mrb_value
-mrb_load_nstring_cxt(mrb_state *mrb, const char *s, int len, mrbc_context *c)
+mrb_load_nstring_cxt(mrb_state *mrb, const char *s, size_t len, mrbc_context *c)
 {
   return mrb_load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c);
 }
 
 MRB_API mrb_value
-mrb_load_nstring(mrb_state *mrb, const char *s, int len)
+mrb_load_nstring(mrb_state *mrb, const char *s, size_t len)
 {
   return mrb_load_nstring_cxt(mrb, s, len, NULL);
 }
index 7741e51..a840ade 100644 (file)
@@ -201,7 +201,7 @@ module Enumerable
       ary.push([block.call(e), i])
     }
     if ary.size > 1
-      __sort_sub__(ary, 0, ary.size - 1) do |a,b|
+      ary.__sort_sub__(0, ary.size - 1) do |a,b|
         a <=> b
       end
     end
@@ -451,20 +451,30 @@ module Enumerable
   ##
   #  call-seq:
   #     enum.none? [{ |obj| block }]   -> true or false
+  #     enum.none?(pattern)            -> true or false
   #
   #  Passes each element of the collection to the given block. The method
   #  returns <code>true</code> if the block never returns <code>true</code>
   #  for all elements. If the block is not given, <code>none?</code> will return
   #  <code>true</code> only if none of the collection members is true.
   #
+  #  If a pattern is supplied instead, the method returns whether
+  #  <code>pattern === element</code> for none of the collection members.
+  #
   #     %w(ant bear cat).none? { |word| word.length == 5 } #=> true
   #     %w(ant bear cat).none? { |word| word.length >= 4 } #=> false
+  #     %w{ant bear cat}.none?(/d/)                        #=> true
+  #     [1, 3.14, 42].none?(Float)                         #=> false
   #     [].none?                                           #=> true
   #     [nil, false].none?                                 #=> true
   #     [nil, true].none?                                  #=> false
 
-  def none?(&block)
-    if block
+  def none?(pat=NONE, &block)
+    if pat != NONE
+      self.each do |*val|
+        return false if pat === val.__svalue
+      end
+    elsif block
       self.each do |*val|
         return false if block.call(*val)
       end
@@ -479,6 +489,7 @@ module Enumerable
   ##
   #  call-seq:
   #    enum.one? [{ |obj| block }]   -> true or false
+  #    enum.one?(pattern)            -> true or false
   #
   # Passes each element of the collection to the given block. The method
   # returns <code>true</code> if the block returns <code>true</code>
@@ -486,16 +497,26 @@ module Enumerable
   # <code>true</code> only if exactly one of the collection members is
   # true.
   #
+  # If a pattern is supplied instead, the method returns whether
+  # <code>pattern === element</code> for exactly one collection member.
+  #
   #    %w(ant bear cat).one? { |word| word.length == 4 }  #=> true
   #    %w(ant bear cat).one? { |word| word.length > 4 }   #=> false
   #    %w(ant bear cat).one? { |word| word.length < 4 }   #=> false
+  #    %w{ant bear cat}.one?(/t/)                         #=> false
   #    [nil, true, 99].one?                               #=> false
   #    [nil, true, false].one?                            #=> true
-  #
+  #    [ nil, true, 99 ].one?(Integer)                    #=> true
+  #    [].one?                                            #=> false
 
-  def one?(&block)
+  def one?(pat=NONE, &block)
     count = 0
-    if block
+    if pat!=NONE
+      self.each do |*val|
+        count += 1 if pat === val.__svalue
+        return false if count > 1
+      end
+    elsif block
       self.each do |*val|
         count += 1 if block.call(*val)
         return false if count > 1
@@ -510,6 +531,71 @@ module Enumerable
     count == 1 ? true : false
   end
 
+  # ISO 15.3.2.2.1
+  #  call-seq:
+  #     enum.all? [{ |obj| block } ]   -> true or false
+  #     enum.all?(pattern)             -> true or false
+  #
+  #  Passes each element of the collection to the given block. The method
+  #  returns <code>true</code> if the block never returns
+  #  <code>false</code> or <code>nil</code>. If the block is not given,
+  #  Ruby adds an implicit block of <code>{ |obj| obj }</code> which will
+  #  cause #all? to return +true+ when none of the collection members are
+  #  +false+ or +nil+.
+  #
+  #  If a pattern is supplied instead, the method returns whether
+  #  <code>pattern === element</code> for every collection member.
+  #
+  #     %w[ant bear cat].all? { |word| word.length >= 3 } #=> true
+  #     %w[ant bear cat].all? { |word| word.length >= 4 } #=> false
+  #     %w[ant bear cat].all?(/t/)                        #=> false
+  #     [1, 2i, 3.14].all?(Numeric)                       #=> true
+  #     [nil, true, 99].all?                              #=> false
+  #
+  def all?(pat=NONE, &block)
+    if pat != NONE
+      self.each{|*val| return false unless pat === val.__svalue}
+    elsif block
+      self.each{|*val| return false unless block.call(*val)}
+    else
+      self.each{|*val| return false unless val.__svalue}
+    end
+    true
+  end
+
+  # ISO 15.3.2.2.2
+  #  call-seq:
+  #     enum.any? [{ |obj| block }]   -> true or false
+  #     enum.any?(pattern)            -> true or false
+  #
+  #  Passes each element of the collection to the given block. The method
+  #  returns <code>true</code> if the block ever returns a value other
+  #  than <code>false</code> or <code>nil</code>. If the block is not
+  #  given, Ruby adds an implicit block of <code>{ |obj| obj }</code> that
+  #  will cause #any? to return +true+ if at least one of the collection
+  #  members is not +false+ or +nil+.
+  #
+  #  If a pattern is supplied instead, the method returns whether
+  #  <code>pattern === element</code> for any collection member.
+  #
+  #     %w[ant bear cat].any? { |word| word.length >= 3 } #=> true
+  #     %w[ant bear cat].any? { |word| word.length >= 4 } #=> true
+  #     %w[ant bear cat].any?(/d/)                        #=> false
+  #     [nil, true, 99].any?(Integer)                     #=> true
+  #     [nil, true, 99].any?                              #=> true
+  #     [].any?                                           #=> false
+  #
+  def any?(pat=NONE, &block)
+    if pat != NONE
+      self.each{|*val| return true if pat === val.__svalue}
+    elsif block
+      self.each{|*val| return true if block.call(*val)}
+    else
+      self.each{|*val| return true if val.__svalue}
+    end
+    false
+  end
+
   ##
   #  call-seq:
   #    enum.each_with_object(obj) { |(*args), memo_obj| ... }  ->  obj
@@ -524,9 +610,7 @@ module Enumerable
   #     #=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
   #
 
-  def each_with_object(obj=nil, &block)
-    raise ArgumentError, "wrong number of arguments (0 for 1)" if obj.nil?
-
+  def each_with_object(obj, &block)
     return to_enum(:each_with_object, obj) unless block
 
     self.each {|*val| block.call(val.__svalue, obj) }
@@ -656,18 +740,39 @@ module Enumerable
   ##
   #  call-seq:
   #     enum.zip(arg, ...)                  -> an_array_of_array
+  #     enum.zip(arg, ...) { |arr| block }  -> nil
   #
   #  Takes one element from <i>enum</i> and merges corresponding
   #  elements from each <i>args</i>.  This generates a sequence of
   #  <em>n</em>-element arrays, where <em>n</em> is one more than the
   #  count of arguments.  The length of the resulting sequence will be
   #  <code>enum#size</code>.  If the size of any argument is less than
-  #  <code>enum#size</code>, <code>nil</code> values are supplied.
+  #  <code>enum#size</code>, <code>nil</code> values are supplied. If
+  #  a block is given, it is invoked for each output array, otherwise
+  #  an array of arrays is returned.
+  #
+  #     a = [ 4, 5, 6 ]
+  #     b = [ 7, 8, 9 ]
+  #
+  #     a.zip(b)                 #=> [[4, 7], [5, 8], [6, 9]]
+  #     [1, 2, 3].zip(a, b)      #=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
+  #     [1, 2].zip(a, b)         #=> [[1, 4, 7], [2, 5, 8]]
+  #     a.zip([1, 2], [8])       #=> [[4, 1, 8], [5, 2, nil], [6, nil, nil]]
+  #
+  #     c = []
+  #     a.zip(b) { |x, y| c << x + y }  #=> nil
+  #     c                               #=> [11, 13, 15]
   #
 
-  def zip(*arg)
-    ary = []
-    arg = arg.map{|a|a.to_a}
+  def zip(*arg, &block)
+    result = block ? nil : []
+    arg = arg.map do |a|
+      unless a.respond_to?(:to_a)
+        raise TypeError, "wrong argument type #{a.class} (must respond to :to_a)"
+      end
+      a.to_a
+    end
+
     i = 0
     self.each do |*val|
       a = []
@@ -677,10 +782,14 @@ module Enumerable
         a.push(arg[idx][i])
         idx += 1
       end
-      ary.push(a)
       i += 1
+      if result.nil?
+        block.call(a)
+      else
+        result.push(a)
+      end
     end
-    ary
+    result
   end
 
   ##
@@ -708,4 +817,20 @@ module Enumerable
   def nil.to_h
     {}
   end
+
+  def uniq(&block)
+    hash = {}
+    if block
+      self.each do|*v|
+        v = v.__svalue
+        hash[block.call(v)] ||= v
+      end
+    else
+      self.each do|*v|
+        v = v.__svalue
+        hash[v] ||= v
+      end
+    end
+    hash.values
+  end
 end
index e772f85..46ed5f0 100644 (file)
@@ -100,6 +100,7 @@ end
 assert("Enumerable#none?") do
   assert_true %w(ant bear cat).none? { |word| word.length == 5 }
   assert_false %w(ant bear cat).none? { |word| word.length >= 4 }
+  assert_false [1, 3.14, 42].none?(Float)
   assert_true [].none?
   assert_true [nil, false].none?
   assert_false [nil, true].none?
@@ -109,8 +110,21 @@ assert("Enumerable#one?") do
   assert_true %w(ant bear cat).one? { |word| word.length == 4 }
   assert_false %w(ant bear cat).one? { |word| word.length > 4 }
   assert_false %w(ant bear cat).one? { |word| word.length < 4 }
+  assert_true [1, 3.14, 42].one?(Float)
   assert_false [nil, true, 99].one?
   assert_true [nil, true, false].one?
+  assert_true [ nil, true, 99 ].one?(Integer)
+  assert_false [].one?
+end
+
+assert("Enumerable#all? (enhancement)") do
+  assert_false [1, 2, 3.14].all?(Integer)
+  assert_true [1, 2, 3.14].all?(Numeric)
+end
+
+assert("Enumerable#any? (enhancement)") do
+  assert_false [1, 2, 3].all?(Float)
+  assert_true [nil, true, 99].any?(Integer)
 end
 
 assert("Enumerable#each_with_object") do
@@ -152,6 +166,12 @@ assert("Enumerable#zip") do
   assert_equal [[1, 4, 7], [2, 5, 8], [3, 6, 9]], [1, 2, 3].zip(a, b)
   assert_equal [[1, 4, 7], [2, 5, 8]], [1, 2].zip(a, b)
   assert_equal [[4, 1, 8], [5, 2, nil], [6, nil, nil]], a.zip([1, 2], [8])
+
+  ret = []
+  assert_equal nil, a.zip([1, 2], [8]) { |i| ret << i }
+  assert_equal [[4, 1, 8], [5, 2, nil], [6, nil, nil]], ret
+
+  assert_raise(TypeError) { [1].zip(1) }
 end
 
 assert("Enumerable#to_h") do
index 8a7ac7f..9227abe 100644 (file)
@@ -43,6 +43,9 @@ class Enumerator
     end
 
     def to_enum(meth=:each, *args, &block)
+      unless self.respond_to?(meth)
+        raise NoMethodError, "undefined method #{meth}"
+      end
       lz = Lazy.new(self, &block)
       lz.obj = self
       lz.meth = meth
@@ -155,6 +158,21 @@ class Enumerator
       }
     end
 
+    def uniq(&block)
+      hash = {}
+      Lazy.new(self){|yielder, val|
+        if block
+          v = block.call(val)
+        else
+          v = val
+        end
+        unless hash.include?(v)
+          yielder << val
+          hash[v] = val
+        end
+      }
+    end
+
     alias force to_a
   end
 end
index 940d070..1a55d30 100644 (file)
@@ -40,7 +40,7 @@ assert("Enumerator::Lazy laziness") do
   assert_equal [10,20], a.b
 end
 
-assert("Enumrator::Lazy#to_enum") do
+assert("Enumerator::Lazy#to_enum") do
   lazy_enum = (0..Float::INFINITY).lazy.to_enum(:each_slice, 2)
   assert_kind_of Enumerator::Lazy, lazy_enum
   assert_equal [0*1, 2*3, 4*5, 6*7], lazy_enum.map { |a| a.first * a.last }.first(4)
index a60f19a..7ca1d5e 100644 (file)
@@ -110,11 +110,14 @@ class Enumerator
   #     p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
   #
   def initialize(obj=nil, meth=:each, *args, &block)
-    if block_given?
+    if block
       obj = Generator.new(&block)
     else
       raise ArgumentError unless obj
     end
+    if @obj and !self.respond_to?(meth)
+      raise NoMethodError, "undefined method #{meth}"
+    end
 
     @obj = obj
     @meth = meth
@@ -151,8 +154,9 @@ class Enumerator
   #
   # +offset+:: the starting index to use
   #
-  def with_index(offset=0)
-    return to_enum :with_index, offset unless block_given?
+  def with_index(offset=0, &block)
+    return to_enum :with_index, offset unless block
+
     offset = if offset.nil?
       0
     elsif offset.respond_to?(:to_int)
@@ -164,7 +168,7 @@ class Enumerator
     n = offset - 1
     enumerator_block_call do |*i|
       n += 1
-      yield i.__svalue, n
+      block.call i.__svalue, n
     end
   end
 
@@ -209,11 +213,11 @@ class Enumerator
   #   # => foo:1
   #   # => foo:2
   #
-  def with_object(object)
-    return to_enum(:with_object, object) unless block_given?
+  def with_object(object, &block)
+    return to_enum(:with_object, object) unless block
 
     enumerator_block_call do |i|
-      yield [i,object]
+      block.call [i,object]
     end
     object
   end
@@ -277,7 +281,7 @@ class Enumerator
       end
       obj.args = args
     end
-    return obj unless block_given?
+    return obj unless block
     enumerator_block_call(&block)
   end
 
@@ -538,7 +542,7 @@ class Enumerator
   # just for internal
   class Yielder
     def initialize(&block)
-      raise LocalJumpError, "no block given" unless block_given?
+      raise LocalJumpError, "no block given" unless block
 
       @proc = block
     end
@@ -617,25 +621,40 @@ end
 
 module Enumerable
   # use Enumerator to use infinite sequence
-  def zip(*arg)
-    ary = []
-    arg = arg.map{|a|a.each}
-    i = 0
-    self.each do |*val|
-      a = []
-      a.push(val.__svalue)
-      idx = 0
-      while idx < arg.size
-        begin
-          a.push(arg[idx].next)
-        rescue StopIteration
-          a.push(nil)
+  def zip(*args, &block)
+    args = args.map do |a|
+      if a.respond_to?(:to_ary)
+        a.to_ary.to_enum(:each)
+      elsif a.respond_to?(:each)
+        a.to_enum(:each)
+      else
+        raise TypeError, "wrong argument type #{a.class} (must respond to :each)"
+      end
+    end
+
+    result = block ? nil : []
+
+    each do |*val|
+      tmp = [val.__svalue]
+      args.each do |arg|
+        v = if arg.nil?
+          nil
+        else
+          begin
+            arg.next
+          rescue StopIteration
+            nil
+          end
         end
-        idx += 1
+        tmp.push(v)
+      end
+      if result.nil?
+        block.call(tmp)
+      else
+        result.push(tmp)
       end
-      ary.push(a)
-      i += 1
     end
-    ary
+
+    result
   end
 end
index 763cd36..428ea03 100644 (file)
@@ -33,7 +33,7 @@ assert 'Enumerator.new' do
       a, b = b, a + b
     end
   end
-  assert_equal fib.take(10), [1,1,2,3,5,8,13,21,34,55]
+  assert_equal [1,1,2,3,5,8,13,21,34,55], fib.take(10)
 end
 
 assert 'Enumerator#initialize_copy' do
@@ -509,28 +509,28 @@ end
 
 assert 'Hash#select' do
   h = {1=>2,3=>4,5=>6}
-  hret = h.select.with_index {|a,b| a[1] == 4}
+  hret = h.select.with_index {|a,_b| a[1] == 4}
   assert_equal({3=>4}, hret)
   assert_equal({1=>2,3=>4,5=>6}, h)
 end
 
 assert 'Hash#select!' do
   h = {1=>2,3=>4,5=>6}
-  hret = h.select!.with_index {|a,b| a[1] == 4}
+  hret = h.select!.with_index {|a,_b| a[1] == 4}
   assert_equal h, hret
   assert_equal({3=>4}, h)
 end
 
 assert 'Hash#reject' do
   h = {1=>2,3=>4,5=>6}
-  hret = h.reject.with_index {|a,b| a[1] == 4}
+  hret = h.reject.with_index {|a,_b| a[1] == 4}
   assert_equal({1=>2,5=>6}, hret)
   assert_equal({1=>2,3=>4,5=>6}, h)
 end
 
 assert 'Hash#reject!' do
   h = {1=>2,3=>4,5=>6}
-  hret = h.reject!.with_index {|a,b| a[1] == 4}
+  hret = h.reject!.with_index {|a,_b| a[1] == 4}
   assert_equal h, hret
   assert_equal({1=>2,5=>6}, h)
 end
@@ -544,3 +544,13 @@ assert 'Range#each' do
   end
   assert_equal [1,2,3,4,5], c
 end
+
+assert 'Enumerable#zip' do
+  assert_equal [[1, 10], [2, 11], [3, 12]], [1,2,3].zip(10..Float::INFINITY)
+
+  ret = []
+  assert_equal nil, [1,2,3].zip(10..Float::INFINITY) { |i| ret << i }
+  assert_equal [[1, 10], [2, 11], [3, 12]], ret
+
+  assert_raise(TypeError) { [1].zip(1) }
+end
index 146c6df..14e89ac 100644 (file)
@@ -12,29 +12,14 @@ mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self);
 static struct mrb_irep *
 get_closure_irep(mrb_state *mrb, int level)
 {
-  struct mrb_context *c = mrb->c;
-  struct REnv *e = c->ci[-1].proc->env;
-  struct RProc *proc;
+  struct RProc *proc = mrb->c->ci[-1].proc;
 
-  if (level == 0) {
-    proc = c->ci[-1].proc;
-    if (MRB_PROC_CFUNC_P(proc)) {
-      return NULL;
-    }
-    return proc->body.irep;
+  while (level--) {
+    if (!proc) return NULL;
+    proc = proc->upper;
   }
-
-  while (--level) {
-    e = (struct REnv*)e->c;
-    if (!e) return NULL;
-  }
-
-  if (!e) return NULL;
-  if (!MRB_ENV_STACK_SHARED_P(e)) return NULL;
-  c = e->cxt.c;
-  proc = c->cibase[e->cioff].proc;
-
-  if (!proc || MRB_PROC_CFUNC_P(proc)) {
+  if (!proc) return NULL;
+  if (MRB_PROC_CFUNC_P(proc)) {
     return NULL;
   }
   return proc->body.irep;
@@ -44,7 +29,7 @@ get_closure_irep(mrb_state *mrb, int level)
 static mrb_irep*
 search_irep(mrb_irep *top, int bnest, int lev, mrb_irep *bottom)
 {
-  size_t i;
+  int i;
 
   for (i=0; i<top->rlen; i++) {
     mrb_irep* tmp = top->reps[i];
@@ -67,7 +52,7 @@ search_variable(mrb_state *mrb, mrb_sym vsym, int bnest)
   int pos;
 
   for (level = 0; (virep = get_closure_irep(mrb, level)); level++) {
-    if (!virep || virep->lv == NULL) {
+    if (virep->lv == NULL) {
       continue;
     }
     for (pos = 0; pos < virep->nlocals - 1; pos++) {
@@ -106,7 +91,7 @@ potential_upvar_p(struct mrb_locals *lv, uint16_t v, int argc, uint16_t nlocals)
 static void
 patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, mrb_irep *top)
 {
-  size_t i;
+  int i;
   mrb_code c;
   int argc = irep_argc(irep);
 
@@ -130,7 +115,7 @@ patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, mrb_irep *top)
       if (GETARG_C(c) != 0) {
         break;
       }
-      {
+      else {
         mrb_code arg = search_variable(mrb, irep->syms[GETARG_B(c)], bnest);
         if (arg != 0) {
           /* must replace */
@@ -198,20 +183,22 @@ patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, mrb_irep *top)
 void mrb_codedump_all(mrb_state*, struct RProc*);
 
 static struct RProc*
-create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, const char *file, mrb_int line)
+create_proc_from_string(mrb_state *mrb, char *s, mrb_int len, mrb_value binding, const char *file, mrb_int line)
 {
   mrbc_context *cxt;
   struct mrb_parser_state *p;
   struct RProc *proc;
   struct REnv *e;
-  struct mrb_context *c = mrb->c;
+  mrb_callinfo *ci = &mrb->c->ci[-1]; /* callinfo of eval caller */
+  struct RClass *target_class = NULL;
+  int bidx;
 
   if (!mrb_nil_p(binding)) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "Binding of eval must be nil.");
   }
 
   cxt = mrbc_context_new(mrb);
-  cxt->lineno = line;
+  cxt->lineno = (short)line;
 
   mrbc_filename(mrb, cxt, file ? file : "(eval)");
   cxt->capture_errors = TRUE;
@@ -251,19 +238,29 @@ create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, con
     mrbc_context_free(mrb, cxt);
     mrb_raise(mrb, E_SCRIPT_ERROR, "codegen error");
   }
-  if (c->ci[-1].proc->target_class) {
-    proc->target_class = c->ci[-1].proc->target_class;
+  target_class = MRB_PROC_TARGET_CLASS(ci->proc);
+  if (!MRB_PROC_CFUNC_P(ci->proc)) {
+    if (ci->env) {
+      e = ci->env;
+    }
+    else {
+      e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV,
+                                      (struct RClass*)target_class);
+      e->mid = ci->mid;
+      e->stack = ci[1].stackent;
+      e->cxt = mrb->c;
+      MRB_ENV_SET_STACK_LEN(e, ci->proc->body.irep->nlocals);
+      bidx = ci->argc;
+      if (ci->argc < 0) bidx = 2;
+      else bidx += 1;
+      MRB_ENV_SET_BIDX(e, bidx);
+    }
+    proc->e.env = e;
+    proc->flags |= MRB_PROC_ENVSET;
+    mrb_field_write_barrier(mrb, (struct RBasic*)proc, (struct RBasic*)e);
   }
-  e = c->ci[-1].proc->env;
-  if (!e) e = c->ci[-1].env;
-  e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)e);
-  e->cxt.c = c;
-  e->cioff = c->ci - c->cibase;
-  e->stack = c->ci->stackent;
-  MRB_SET_ENV_STACK_LEN(e, c->ci->proc->body.irep->nlocals);
-  c->ci->target_class = proc->target_class;
-  c->ci->env = 0;
-  proc->env = e;
+  proc->upper = ci->proc;
+  mrb->c->ci->target_class = target_class;
   patch_irep(mrb, proc->body.irep, 0, proc->body.irep);
   /* mrb_codedump_all(mrb, proc); */
 
@@ -276,13 +273,17 @@ create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, con
 static mrb_value
 exec_irep(mrb_state *mrb, mrb_value self, struct RProc *proc)
 {
+  /* no argument passed from eval() */
+  mrb->c->ci->argc = 0;
   if (mrb->c->ci->acc < 0) {
-    mrb_value ret = mrb_top_run(mrb, proc, mrb->c->stack[0], 0);
+    mrb_value ret = mrb_top_run(mrb, proc, self, 0);
     if (mrb->exc) {
       mrb_exc_raise(mrb, mrb_obj_value(mrb->exc));
     }
     return ret;
   }
+  /* clear block */
+  mrb->c->stack[1] = mrb_nil_value();
   return mrb_exec_irep(mrb, self, proc);
 }
 
@@ -322,8 +323,7 @@ f_instance_eval(mrb_state *mrb, mrb_value self)
     mrb_get_args(mrb, "s|zi", &s, &len, &file, &line);
     cv = mrb_singleton_class(mrb, self);
     proc = create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line);
-    proc->target_class = mrb_class_ptr(cv);
-    mrb->c->ci->env = NULL;
+    MRB_PROC_SET_TARGET_CLASS(proc, mrb_class_ptr(cv));
     mrb_assert(!MRB_PROC_CFUNC_P(proc));
     return exec_irep(mrb, self, proc);
   }
index 3e147f8..05c9296 100644 (file)
@@ -7,7 +7,7 @@ f_exit(mrb_state *mrb, mrb_value self)
   mrb_int i = EXIT_SUCCESS;
 
   mrb_get_args(mrb, "|i", &i);
-  exit(i);
+  exit((int)i);
   /* not reached */
   return mrb_nil_value();
 }
index 9de175f..83153a9 100644 (file)
@@ -123,7 +123,7 @@ fiber_init(mrb_state *mrb, mrb_value self)
 
   /* adjust return callinfo */
   ci = c->ci;
-  ci->target_class = p->target_class;
+  ci->target_class = MRB_PROC_TARGET_CLASS(p);
   ci->proc = p;
   mrb_field_write_barrier(mrb, (struct RBasic*)mrb_obj_ptr(self), (struct RBasic*)p);
   ci->pc = p->body.irep->iseq;
@@ -175,9 +175,6 @@ fiber_check_cfunc(mrb_state *mrb, struct mrb_context *c)
 static void
 fiber_switch_context(mrb_state *mrb, struct mrb_context *c)
 {
-  if (mrb->c->fib) {
-    mrb_write_barrier(mrb, (struct RBasic*)mrb->c->fib);
-  }
   c->status = MRB_FIBER_RUNNING;
   mrb->c = c;
 }
@@ -212,8 +209,8 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr
     while (b<e) {
       *b++ = *a++;
     }
-    c->cibase->argc = len;
-    value = c->stack[0] = c->ci->proc->env->stack[0];
+    c->cibase->argc = (int)len;
+    value = c->stack[0] = MRB_PROC_ENV(c->ci->proc)->stack[0];
   }
   else {
     value = fiber_result(mrb, a, len);
@@ -274,12 +271,13 @@ mrb_fiber_resume(mrb_state *mrb, mrb_value fib, mrb_int len, const mrb_value *a)
  *  Returns true if the fiber can still be resumed. After finishing
  *  execution of the fiber block this method will always return false.
  */
-static mrb_value
-fiber_alive_p(mrb_state *mrb, mrb_value self)
+MRB_API mrb_value
+mrb_fiber_alive_p(mrb_state *mrb, mrb_value self)
 {
   struct mrb_context *c = fiber_check(mrb, self);
   return mrb_bool_value(c->status != MRB_FIBER_TERMINATED);
 }
+#define fiber_alive_p mrb_fiber_alive_p
 
 static mrb_value
 fiber_eq(mrb_state *mrb, mrb_value self)
index 87817f8..549bca0 100644 (file)
@@ -115,6 +115,22 @@ class Hash
   alias update merge!
 
   ##
+  # call-seq:
+  #    hsh.compact     -> new_hsh
+  #
+  # Returns a new hash with the nil values/key pairs removed
+  #
+  #    h = { a: 1, b: false, c: nil }
+  #    h.compact     #=> { a: 1, b: false }
+  #    h             #=> { a: 1, b: false, c: nil }
+  #
+  def compact
+    result = self.dup
+    result.compact!
+    result
+  end
+
+  ##
   #  call-seq:
   #     hsh.fetch(key [, default] )       -> obj
   #     hsh.fetch(key) {| key | block }   -> obj
@@ -149,7 +165,7 @@ class Hash
       elsif none != NONE
         none
       else
-        raise KeyError, "Key not found: #{key}"
+        raise KeyError, "Key not found: #{key.inspect}"
       end
     else
       self[key]
@@ -171,7 +187,7 @@ class Hash
   #
 
   def delete_if(&block)
-    return to_enum :delete_if unless block_given?
+    return to_enum :delete_if unless block
 
     self.each do |k, v|
       self.delete(k) if block.call(k, v)
@@ -228,7 +244,7 @@ class Hash
   #
 
   def keep_if(&block)
-    return to_enum :keep_if unless block_given?
+    return to_enum :keep_if unless block
 
     keys = []
     self.each do |k, v|
@@ -393,11 +409,11 @@ class Hash
   #
   # If no block is given, an enumerator is returned instead.
   #
-  def transform_keys(&b)
-    return to_enum :transform_keys unless block_given?
+  def transform_keys(&block)
+    return to_enum :transform_keys unless block
     hash = {}
     self.keys.each do |k|
-      new_key = yield(k)
+      new_key = block.call(k)
       hash[new_key] = self[k]
     end
     hash
@@ -412,11 +428,11 @@ class Hash
   #
   # If no block is given, an enumerator is returned instead.
   #
-  def transform_keys!(&b)
-    return to_enum :transform_keys! unless block_given?
+  def transform_keys!(&block)
+    return to_enum :transform_keys! unless block
     self.keys.each do |k|
       value = self[k]
-      new_key = yield(k)
+      new_key = block.call(k)
       self.__delete(k)
       self[new_key] = value
     end
@@ -458,4 +474,29 @@ class Hash
     end
     self
   end
+
+  def to_proc
+    ->x{self[x]}
+  end
+
+  ##
+  # call-seq:
+  #   hsh.fetch_values(key, ...)                 -> array
+  #   hsh.fetch_values(key, ...) { |key| block } -> array
+  #
+  # Returns an array containing the values associated with the given keys
+  # but also raises <code>KeyError</code> when one of keys can't be found.
+  # Also see <code>Hash#values_at</code> and <code>Hash#fetch</code>.
+  #
+  #   h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" }
+  #
+  #   h.fetch_values("cow", "cat")                   #=> ["bovine", "feline"]
+  #   h.fetch_values("cow", "bird")                  # raises KeyError
+  #   h.fetch_values("cow", "bird") { |k| k.upcase } #=> ["bovine", "BIRD"]
+  #
+  def fetch_values(*keys, &block)
+    keys.map do |k|
+      self.fetch(k, &block)
+    end
+  end
 end
index 61abc08..6619f52 100644 (file)
@@ -26,7 +26,7 @@ hash_values_at(mrb_state *mrb, mrb_value hash)
   mrb_int argc, i;
   int ai;
 
-  mrb_get_args(mrb, "*!", &argv, &argc);
+  mrb_get_args(mrb, "*", &argv, &argc);
   result = mrb_ary_new_capa(mrb, argc);
   ai = mrb_gc_arena_save(mrb);
   for (i = 0; i < argc; i++) {
@@ -36,6 +36,81 @@ hash_values_at(mrb_state *mrb, mrb_value hash)
   return result;
 }
 
+/*
+ * call-seq:
+ *   hsh.compact!    -> hsh
+ *
+ * Removes all nil values from the hash. Returns the hash.
+ *
+ *   h = { a: 1, b: false, c: nil }
+ *   h.compact!     #=> { a: 1, b: false }
+ */
+static mrb_value
+hash_compact_bang(mrb_state *mrb, mrb_value hash)
+{
+  khiter_t k;
+  khash_t(ht) *h = RHASH_TBL(hash);
+  mrb_int n = -1;
+
+  if (!h) return mrb_nil_value();
+  for (k = kh_begin(h); k != kh_end(h); k++) {
+    if (kh_exist(h, k)) {
+      mrb_value val = kh_value(h, k).v;
+      khiter_t k2;
+
+      if (mrb_nil_p(val)) {
+        kh_del(ht, mrb, h, k);
+        n = kh_value(h, k).n;
+        for (k2 = kh_begin(h); k2 != kh_end(h); k2++) {
+          if (!kh_exist(h, k2)) continue;
+          if (kh_value(h, k2).n > n) kh_value(h, k2).n--;
+        }
+      }
+    }
+  }
+  if (n < 0) return mrb_nil_value();
+  return hash;
+}
+
+/*
+ *  call-seq:
+ *     hsh.slice(*keys) -> a_hash
+ *
+ *  Returns a hash containing only the given keys and their values.
+ *
+ *     h = { a: 100, b: 200, c: 300 }
+ *     h.slice(:a)           #=> {:a=>100}
+ *     h.slice(:b, :c, :d)   #=> {:b=>200, :c=>300}
+ */
+static mrb_value
+hash_slice(mrb_state *mrb, mrb_value hash)
+{
+  khash_t(ht) *h = RHASH_TBL(hash);
+  mrb_value *argv, result;
+  mrb_int argc, i;
+  khiter_t k;
+  int ai;
+
+  mrb_get_args(mrb, "*", &argv, &argc);
+  if (argc == 0 || h == NULL) {
+    return mrb_hash_new_capa(mrb, argc);
+  }
+  result = mrb_hash_new_capa(mrb, argc);
+  ai = mrb_gc_arena_save(mrb);
+  for (i = 0; i < argc; i++) {
+    mrb_value key = argv[i];
+
+    k = kh_get(ht, mrb, h, key);
+    if (k != kh_end(h)) {
+      mrb_value val = kh_value(h, k).v;
+
+      mrb_hash_set(mrb, result, key, val);
+    }
+    mrb_gc_arena_restore(mrb, ai);
+  }
+  return result;
+}
+
 void
 mrb_mruby_hash_ext_gem_init(mrb_state *mrb)
 {
@@ -43,6 +118,8 @@ mrb_mruby_hash_ext_gem_init(mrb_state *mrb)
 
   h = mrb->hash_class;
   mrb_define_method(mrb, h, "values_at", hash_values_at, MRB_ARGS_ANY());
+  mrb_define_method(mrb, h, "compact!",  hash_compact_bang, MRB_ARGS_NONE());
+  mrb_define_method(mrb, h, "slice",     hash_slice, MRB_ARGS_ANY());
 }
 
 void
index 2ae88c3..269da80 100644 (file)
@@ -82,6 +82,20 @@ assert('Hash#values_at') do
   assert_equal keys, h.values_at(*keys)
 end
 
+assert('Hash#compact') do
+  h = { "cat" => "feline", "dog" => nil, "cow" => false }
+
+  assert_equal({ "cat" => "feline", "cow" => false }, h.compact)
+  assert_equal({ "cat" => "feline", "dog" => nil, "cow" => false }, h)
+end
+
+assert('Hash#compact!') do
+  h = { "cat" => "feline", "dog" => nil, "cow" => false }
+
+  h.compact!
+  assert_equal({ "cat" => "feline", "cow" => false }, h)
+end
+
 assert('Hash#fetch') do
   h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" }
   assert_equal "feline", h.fetch("cat")
@@ -257,24 +271,30 @@ end
 
 assert("Hash#transform_keys") do
   h = {"1" => 100, "2" => 200}
-  assert_equal(h.transform_keys{|k| k+"!"},
-               {"1!" => 100, "2!" => 200})
-  assert_equal(h.transform_keys{|k|k.to_i},
-               {1 => 100, 2 => 200})
-  assert_equal(h.transform_keys.with_index{|k, i| "#{k}.#{i}"},
-               {"1.0" => 100, "2.1" => 200})
-  assert_equal(h.transform_keys!{|k|k.to_i}, h)
+  assert_equal({"1!" => 100, "2!" => 200},
+               h.transform_keys{|k| k+"!"})
+  assert_equal({1 => 100, 2 => 200},
+               h.transform_keys{|k|k.to_i})
+  assert_equal({"1.0" => 100, "2.1" => 200},
+               h.transform_keys.with_index{|k, i| "#{k}.#{i}"})
+  assert_equal(h, h.transform_keys!{|k|k.to_i})
   assert_equal(h, {1 => 100, 2 => 200})
 end
 
 assert("Hash#transform_values") do
   h = {a: 1, b: 2, c: 3}
-  assert_equal(h.transform_values{|v| v * v + 1},
-               {a: 2, b: 5, c: 10})
-  assert_equal(h.transform_values{|v|v.to_s},
-               {a: "1", b: "2", c: "3"})
-  assert_equal(h.transform_values.with_index{|v, i| "#{v}.#{i}"},
-               {a: "1.0", b: "2.1", c: "3.2"})
-  assert_equal(h.transform_values!{|v|v.to_s}, h)
-  assert_equal(h, {a: "1", b: "2", c: "3"})
+  assert_equal({a: 2, b: 5, c: 10},
+               h.transform_values{|v| v * v + 1})
+  assert_equal({a: "1", b: "2", c: "3"},
+               h.transform_values{|v|v.to_s})
+  assert_equal({a: "1.0", b: "2.1", c: "3.2"},
+               h.transform_values.with_index{|v, i| "#{v}.#{i}"})
+  assert_equal(h, h.transform_values!{|v|v.to_s})
+  assert_equal({a: "1", b: "2", c: "3"}, h)
+end
+
+assert("Hash#slice") do
+  h = { a: 100, b: 200, c: 300 }
+  assert_equal({:a=>100}, h.slice(:a))
+  assert_equal({:b=>200, :c=>300}, h.slice(:b, :c, :d))
 end
diff --git a/third-party/mruby/mrbgems/mruby-io/.gitignore b/third-party/mruby/mrbgems/mruby-io/.gitignore
new file mode 100644 (file)
index 0000000..ceeb05b
--- /dev/null
@@ -0,0 +1 @@
+/tmp
diff --git a/third-party/mruby/mrbgems/mruby-io/.travis.yml b/third-party/mruby/mrbgems/mruby-io/.travis.yml
new file mode 100644 (file)
index 0000000..ffe2272
--- /dev/null
@@ -0,0 +1,2 @@
+script:
+  - "ruby run_test.rb all test"
diff --git a/third-party/mruby/mrbgems/mruby-io/README.md b/third-party/mruby/mrbgems/mruby-io/README.md
new file mode 100644 (file)
index 0000000..256fb81
--- /dev/null
@@ -0,0 +1,193 @@
+mruby-io
+========
+[![Build Status](https://travis-ci.org/iij/mruby-io.svg?branch=master)](https://travis-ci.org/iij/mruby-io)
+
+
+`IO` and `File` classes for mruby
+
+## Installation
+Add the line below to your `build_config.rb`:
+
+```
+  conf.gem :github => 'iij/mruby-io'
+```
+
+## Implemented methods
+
+### IO
+ - http://doc.ruby-lang.org/ja/1.9.3/class/IO.html
+
+| method                     | mruby-io | memo |
+| -------------------------  | -------- | ---- |
+| IO.binread                 |          |      |
+| IO.binwrite                |          |      |
+| IO.copy_stream             |          |      |
+| IO.new, IO.for_fd, IO.open |  o  |     |
+| IO.foreach                 |          |      |
+| IO.pipe                    |    o     |      |
+| IO.popen                   |    o     |      |
+| IO.read                    |    o     |      |
+| IO.readlines               |          |      |
+| IO.select                  |    o     |      |
+| IO.sysopen                 |    o     |      |
+| IO.try_convert             |          |      |
+| IO.write                   |          |      |
+| IO#<<                      |          |      |
+| IO#advise                  |          |      |
+| IO#autoclose=              |          |      |
+| IO#autoclose?              |          |      |
+| IO#binmode                 |          |      |
+| IO#binmode?                |          |      |
+| IO#bytes                   |          | obsolete |
+| IO#chars                   |          | obsolete |
+| IO#clone, IO#dup           |    o     |      |
+| IO#close                   |    o     |      |
+| IO#close_on_exec=          |    o     |      |
+| IO#close_on_exec?          |    o     |      |
+| IO#close_read              |          |      |
+| IO#close_write             |          |      |
+| IO#closed?                 |    o     |      |
+| IO#codepoints              |          | obsolete |
+| IO#each_byte               |    o     |      |
+| IO#each_char               |    o     |      |
+| IO#each_codepoint          |          |      |
+| IO#each_line               |    o     |      |
+| IO#eof, IO#eof?            |    o     |      |
+| IO#external_encoding       |          |      |
+| IO#fcntl                   |          |      |
+| IO#fdatasync               |          |      |
+| IO#fileno, IO#to_i         |    o     |      |
+| IO#flush                   |    o     |      |
+| IO#fsync                   |          |      |
+| IO#getbyte                 |          |      |
+| IO#getc                    |    o     |      |
+| IO#gets                    |    o     |      |
+| IO#internal_encoding       |          |      |
+| IO#ioctl                   |          |      |
+| IO#isatty, IO#tty?         |    o     |      |
+| IO#lineno                  |          |      |
+| IO#lineno=                 |          |      |
+| IO#lines                   |          | obsolete |
+| IO#pid                     |    o     |      |
+| IO#pos, IO#tell            |    o     |      |
+| IO#pos=                    |    o     |      |
+| IO#print                   |    o     |      |
+| IO#printf                  |    o     |      |
+| IO#putc                    |          |      |
+| IO#puts                    |    o     |      |
+| IO#read                    |    o     |      |
+| IO#read_nonblock           |          |      |
+| IO#readbyte                |          |      |
+| IO#readchar                |    o     |      |
+| IO#readline                |    o     |      |
+| IO#readlines               |    o     |      |
+| IO#readpartial             |          |      |
+| IO#reopen                  |          |      |
+| IO#rewind                  |          |      |
+| IO#seek                    |    o     |      |
+| IO#set_encoding            |          |      |
+| IO#stat                    |          |      |
+| IO#sync                    |    o     |      |
+| IO#sync=                   |    o     |      |
+| IO#sysread                 |    o     |      |
+| IO#sysseek                 |    o     |      |
+| IO#syswrite                |    o     |      |
+| IO#to_io                   |          |      |
+| IO#ungetbyte               |          |      |
+| IO#ungetc                  |    o     |      |
+| IO#write                   |    o     |      |
+| IO#write_nonblock          |          |      |
+
+### File
+ - http://doc.ruby-lang.org/ja/1.9.3/class/File.html
+
+| method                      | mruby-io | memo |
+| --------------------------- | -------- | ---- |
+| File.absolute_path          |          |      |
+| File.atime                  |          |      |
+| File.basename               |   o      |      |
+| File.blockdev?              |          | FileTest |
+| File.chardev?               |          | FileTest |
+| File.chmod                  |   o      |      |
+| File.chown                  |          |      |
+| File.ctime                  |          |      |
+| File.delete, File.unlink    |   o      |      |
+| File.directory?             |   o      | FileTest |
+| File.dirname                |   o      |      |
+| File.executable?            |          | FileTest |
+| File.executable_real?       |          | FileTest |
+| File.exist?, exists?        |   o      | FileTest |
+| File.expand_path            |   o      |      |
+| File.extname                |   o      |      |
+| File.file?                  |   o      | FileTest |
+| File.fnmatch, File.fnmatch? |          |      |
+| File.ftype                  |          |      |
+| File.grpowned?              |          | FileTest |
+| File.identical?             |          | FileTest |
+| File.join                   |   o      |      |
+| File.lchmod                 |          |      |
+| File.lchown                 |          |      |
+| File.link                   |          |      |
+| File.lstat                  |          |      |
+| File.mtime                  |          |      |
+| File.new, File.open         |   o      |      |
+| File.owned?                 |          | FileTest |
+| File.path                   |          |      |
+| File.pipe?                  |   o      | FileTest |
+| File.readable?              |          | FileTest |
+| File.readable_real?         |          | FileTest |
+| File.readlink               |   o      |      |
+| File.realdirpath            |          |      |
+| File.realpath               |   o      |      |
+| File.rename                 |   o      |      |
+| File.setgid?                |          | FileTest |
+| File.setuid?                |          | FileTest |
+| File.size                   |   o      |      |
+| File.size?                  |   o      | FileTest |
+| File.socket?                |   o      | FileTest |
+| File.split                  |          |      |
+| File.stat                   |          |      |
+| File.sticky?                |          | FileTest |
+| File.symlink                |          |      |
+| File.symlink?               |   o      | FileTest |
+| File.truncate               |          |      |
+| File.umask                  |   o      |      |
+| File.utime                  |          |      |
+| File.world_readable?        |          |      |
+| File.world_writable?        |          |      |
+| File.writable?              |          | FileTest |
+| File.writable_real?         |          | FileTest |
+| File.zero?                  |   o      | FileTest |
+| File#atime                  |          |      |
+| File#chmod                  |          |      |
+| File#chown                  |          |      |
+| File#ctime                  |          |      |
+| File#flock                  |   o      |      |
+| File#lstat                  |          |      |
+| File#mtime                  |          |      |
+| File#path, File#to_path     |   o      |      |
+| File#size                   |          |      |
+| File#truncate               |          |      |
+
+
+## License
+
+Copyright (c) 2013 Internet Initiative Japan Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a 
+copy of this software and associated documentation files (the "Software"), 
+to deal in the Software without restriction, including without limitation 
+the rights to use, copy, modify, merge, publish, distribute, sublicense, 
+and/or sell copies of the Software, and to permit persons to whom the 
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in 
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+DEALINGS IN THE SOFTWARE.
diff --git a/third-party/mruby/mrbgems/mruby-io/include/mruby/ext/io.h b/third-party/mruby/mrbgems/mruby-io/include/mruby/ext/io.h
new file mode 100644 (file)
index 0000000..ba08815
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+** io.h - IO class
+*/
+
+#ifndef MRUBY_IO_H
+#define MRUBY_IO_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct mrb_io {
+  int fd;   /* file descriptor, or -1 */
+  int fd2;  /* file descriptor to write if it's different from fd, or -1 */
+  int pid;  /* child's pid (for pipes)  */
+  unsigned int readable:1,
+               writable:1,
+               sync:1,
+               is_socket:1;
+};
+
+#define FMODE_READABLE             0x00000001
+#define FMODE_WRITABLE             0x00000002
+#define FMODE_READWRITE            (FMODE_READABLE|FMODE_WRITABLE)
+#define FMODE_BINMODE              0x00000004
+#define FMODE_APPEND               0x00000040
+#define FMODE_CREATE               0x00000080
+#define FMODE_TRUNC                0x00000800
+
+#define E_IO_ERROR                 (mrb_class_get(mrb, "IOError"))
+#define E_EOF_ERROR                (mrb_class_get(mrb, "EOFError"))
+
+mrb_value mrb_io_fileno(mrb_state *mrb, mrb_value io);
+
+#if defined(__cplusplus)
+} /* extern "C" { */
+#endif
+#endif /* MRUBY_IO_H */
diff --git a/third-party/mruby/mrbgems/mruby-io/mrbgem.rake b/third-party/mruby/mrbgems/mruby-io/mrbgem.rake
new file mode 100644 (file)
index 0000000..50fa496
--- /dev/null
@@ -0,0 +1,17 @@
+MRuby::Gem::Specification.new('mruby-io') do |spec|
+  spec.license = 'MIT'
+  spec.authors = 'Internet Initiative Japan Inc.'
+  spec.summary = 'IO and File class'
+
+  spec.cc.include_paths << "#{build.root}/src"
+  
+  case RUBY_PLATFORM
+  when /mingw|mswin/
+    spec.linker.libraries += ['Ws2_32']
+    #spec.cc.include_paths += ["C:/Windows/system/include"]
+    spec.linker.library_paths += ["C:/Windows/system"]
+  end
+  if build.kind_of?(MRuby::CrossBuild) && %w(x86_64-w64-mingw32 i686-w64-mingw32).include?(build.host_target)
+    spec.linker.libraries += ['ws2_32']
+  end
+end
diff --git a/third-party/mruby/mrbgems/mruby-io/mrblib/file.rb b/third-party/mruby/mrbgems/mruby-io/mrblib/file.rb
new file mode 100644 (file)
index 0000000..514efc1
--- /dev/null
@@ -0,0 +1,208 @@
+class File < IO
+  class FileError < Exception; end
+  class NoFileError < FileError; end
+  class UnableToStat < FileError; end
+  class PermissionError < FileError; end
+
+  attr_accessor :path
+
+  def initialize(fd_or_path, mode = "r", perm = 0666)
+    if fd_or_path.kind_of? Fixnum
+      super(fd_or_path, mode)
+    else
+      @path = fd_or_path
+      fd = IO.sysopen(@path, mode, perm)
+      super(fd, mode)
+    end
+  end
+
+  def self.join(*names)
+    return "" if names.empty?
+
+    names.map! do |name|
+      case name
+      when String
+        name
+      when Array
+        if names == name
+          raise ArgumentError, "recursive array"
+        end
+        join(*name)
+      else
+        raise TypeError, "no implicit conversion of #{name.class} into String"
+      end
+    end
+
+    return names[0] if names.size == 1
+
+    if names[0][-1] == File::SEPARATOR
+      s = names[0][0..-2]
+    else
+      s = names[0].dup
+    end
+
+    (1..names.size-2).each { |i|
+      t = names[i]
+      if t[0] == File::SEPARATOR and t[-1] == File::SEPARATOR
+        t = t[1..-2]
+      elsif t[0] == File::SEPARATOR
+        t = t[1..-1]
+      elsif t[-1] == File::SEPARATOR
+        t = t[0..-2]
+      end
+      s += File::SEPARATOR + t if t != ""
+    }
+    if names[-1][0] == File::SEPARATOR
+      s += File::SEPARATOR + names[-1][1..-1]
+    else
+      s += File::SEPARATOR + names[-1]
+    end
+    s
+  end
+
+  def self.expand_path(path, default_dir = '.')
+    def concat_path(path, base_path)
+      if path[0] == "/" || path[1] == ':' # Windows root!
+        expanded_path = path
+      elsif path[0] == "~"
+        if (path[1] == "/" || path[1] == nil)
+          dir = path[1, path.size]
+          home_dir = _gethome
+
+          unless home_dir
+            raise ArgumentError, "couldn't find HOME environment -- expanding '~'"
+          end
+
+          expanded_path = home_dir
+          expanded_path += dir if dir
+          expanded_path += "/"
+        else
+          splitted_path = path.split("/")
+          user = splitted_path[0][1, splitted_path[0].size]
+          dir = "/" + splitted_path[1, splitted_path.size].join("/")
+
+          home_dir = _gethome(user)
+
+          unless home_dir
+            raise ArgumentError, "user #{user} doesn't exist"
+          end
+
+          expanded_path = home_dir
+          expanded_path += dir if dir
+          expanded_path += "/"
+        end
+      else
+        expanded_path = concat_path(base_path, _getwd)
+        expanded_path += "/" + path
+      end
+
+      expanded_path
+    end
+
+    expanded_path = concat_path(path, default_dir)
+    drive_prefix = ""
+    if File::ALT_SEPARATOR && expanded_path.size > 2 &&
+        ("A".."Z").include?(expanded_path[0].upcase) && expanded_path[1] == ":"
+      drive_prefix = expanded_path[0, 2]
+      expanded_path = expanded_path[2, expanded_path.size]
+    end
+    expand_path_array = []
+    if File::ALT_SEPARATOR && expanded_path.include?(File::ALT_SEPARATOR)
+      expanded_path.gsub!(File::ALT_SEPARATOR, '/')
+    end
+    while expanded_path.include?('//')
+      expanded_path = expanded_path.gsub('//', '/')
+    end
+
+    if expanded_path != "/"
+      expanded_path.split('/').each do |path_token|
+        if path_token == '..'
+          if expand_path_array.size > 1
+            expand_path_array.pop
+          end
+        elsif path_token == '.'
+          # nothing to do.
+        else
+          expand_path_array << path_token
+        end
+      end
+
+      expanded_path = expand_path_array.join("/")
+      if expanded_path.empty?
+        expanded_path = '/'
+      end
+    end
+    if drive_prefix.empty?
+      expanded_path
+    else
+      drive_prefix + expanded_path.gsub("/", File::ALT_SEPARATOR)
+    end
+  end
+
+  def self.foreach(file)
+    if block_given?
+      self.open(file) do |f|
+        f.each {|l| yield l}
+      end
+    else
+      return self.new(file)
+    end
+  end
+
+  def self.directory?(file)
+    FileTest.directory?(file)
+  end
+
+  def self.exist?(file)
+    FileTest.exist?(file)
+  end
+
+  def self.exists?(file)
+    FileTest.exists?(file)
+  end
+
+  def self.file?(file)
+    FileTest.file?(file)
+  end
+
+  def self.pipe?(file)
+    FileTest.pipe?(file)
+  end
+
+  def self.size(file)
+    FileTest.size(file)
+  end
+
+  def self.size?(file)
+    FileTest.size?(file)
+  end
+
+  def self.socket?(file)
+    FileTest.socket?(file)
+  end
+
+  def self.symlink?(file)
+    FileTest.symlink?(file)
+  end
+
+  def self.zero?(file)
+    FileTest.zero?(file)
+  end
+
+  def self.extname(filename)
+    fname = self.basename(filename)
+    return '' if fname[0] == '.' || fname.index('.').nil?
+    ext = fname.split('.').last
+    ext.empty? ? '' : ".#{ext}"
+  end
+
+  def self.path(filename)
+    if filename.kind_of?(String)
+      filename
+    elsif filename.respond_to?(:to_path)
+      filename.to_path
+    else
+      raise TypeError, "no implicit conversion of #{filename.class} into String"
+    end
+  end
+end
diff --git a/third-party/mruby/mrbgems/mruby-io/mrblib/file_constants.rb b/third-party/mruby/mrbgems/mruby-io/mrblib/file_constants.rb
new file mode 100644 (file)
index 0000000..a68ee25
--- /dev/null
@@ -0,0 +1,29 @@
+class File
+  module Constants
+    RDONLY   = 0
+    WRONLY   = 1
+    RDWR     = 2
+    NONBLOCK = 4
+    APPEND   = 8
+
+    BINARY   = 0
+    SYNC     = 128
+    NOFOLLOW = 256
+    CREAT    = 512
+    TRUNC    = 1024
+    EXCL     = 2048
+
+    NOCTTY   = 131072
+    DSYNC    = 4194304
+
+    FNM_SYSCASE  = 0
+    FNM_NOESCAPE = 1
+    FNM_PATHNAME = 2
+    FNM_DOTMATCH = 4
+    FNM_CASEFOLD = 8
+  end
+end
+
+class File
+  include File::Constants
+end
diff --git a/third-party/mruby/mrbgems/mruby-io/mrblib/io.rb b/third-party/mruby/mrbgems/mruby-io/mrblib/io.rb
new file mode 100644 (file)
index 0000000..6211bf1
--- /dev/null
@@ -0,0 +1,388 @@
+##
+# IO
+
+class IOError < StandardError; end
+class EOFError < IOError; end
+
+class IO
+  SEEK_SET = 0
+  SEEK_CUR = 1
+  SEEK_END = 2
+
+  BUF_SIZE = 4096
+
+  def self.open(*args, &block)
+    io = self.new(*args)
+
+    return io unless block
+
+    begin
+      yield io
+    ensure
+      begin
+        io.close unless io.closed?
+      rescue StandardError
+      end
+    end
+  end
+
+  def self.popen(command, mode = 'r', opts={}, &block)
+    if !self.respond_to?(:_popen)
+      raise NotImplementedError, "popen is not supported on this platform"
+    end
+    io = self._popen(command, mode, opts)
+    return io unless block
+
+    begin
+      yield io
+    ensure
+      begin
+        io.close unless io.closed?
+      rescue IOError
+        # nothing
+      end
+    end
+  end
+
+  def self.pipe(&block)
+    if !self.respond_to?(:_pipe)
+      raise NotImplementedError, "pipe is not supported on this platform"
+    end
+    if block
+      begin
+        r, w = IO._pipe
+        yield r, w
+      ensure
+        r.close unless r.closed?
+        w.close unless w.closed?
+      end
+    else
+      IO._pipe
+    end
+  end
+
+  def self.read(path, length=nil, offset=nil, opt=nil)
+    if not opt.nil?        # 4 arguments
+      offset ||= 0
+    elsif not offset.nil?  # 3 arguments
+      if offset.is_a? Hash
+        opt = offset
+        offset = 0
+      else
+        opt = {}
+      end
+    elsif not length.nil?  # 2 arguments
+      if length.is_a? Hash
+        opt = length
+        offset = 0
+        length = nil
+      else
+        offset = 0
+        opt = {}
+      end
+    else                   # only 1 argument
+      opt = {}
+      offset = 0
+      length = nil
+    end
+
+    str = ""
+    fd = -1
+    io = nil
+    begin
+      if path[0] == "|"
+        io = IO.popen(path[1..-1], (opt[:mode] || "r"))
+      else
+        mode = opt[:mode] || "r"
+        fd = IO.sysopen(path, mode)
+        io = IO.open(fd, mode)
+      end
+      io.seek(offset) if offset > 0
+      str = io.read(length)
+    ensure
+      if io
+        io.close
+      elsif fd != -1
+        IO._sysclose(fd)
+      end
+    end
+    str
+  end
+
+  def flush
+    # mruby-io always writes immediately (no output buffer).
+    raise IOError, "closed stream" if self.closed?
+    self
+  end
+
+  def hash
+    # We must define IO#hash here because IO includes Enumerable and
+    # Enumerable#hash will call IO#read...
+    self.__id__
+  end
+
+  def write(string)
+    str = string.is_a?(String) ? string : string.to_s
+    return str.size unless str.size > 0
+    if 0 < @buf.length
+      # reset real pos ignore buf
+      seek(pos, SEEK_SET)
+    end
+    len = syswrite(str)
+    len
+  end
+
+  def <<(str)
+    write(str)
+    self
+  end
+
+  def eof?
+    _check_readable
+    begin
+      buf = _read_buf
+      return buf.size == 0
+    rescue EOFError
+      return true
+    end
+  end
+  alias_method :eof, :eof?
+
+  def pos
+    raise IOError if closed?
+    sysseek(0, SEEK_CUR) - @buf.length
+  end
+  alias_method :tell, :pos
+
+  def pos=(i)
+    seek(i, SEEK_SET)
+  end
+
+  def rewind
+    seek(0, SEEK_SET)
+  end
+
+  def seek(i, whence = SEEK_SET)
+    raise IOError if closed?
+    sysseek(i, whence)
+    @buf = ''
+    0
+  end
+
+  def _read_buf
+    return @buf if @buf && @buf.size > 0
+    @buf = sysread(BUF_SIZE)
+  end
+
+  def ungetc(substr)
+    raise TypeError.new "expect String, got #{substr.class}" unless substr.is_a?(String)
+    if @buf.empty?
+      @buf = substr.dup
+    else
+      @buf = substr + @buf
+    end
+    nil
+  end
+
+  def read(length = nil, outbuf = "")
+    unless length.nil?
+      unless length.is_a? Fixnum
+        raise TypeError.new "can't convert #{length.class} into Integer"
+      end
+      if length < 0
+        raise ArgumentError.new "negative length: #{length} given"
+      end
+      if length == 0
+        return ""   # easy case
+      end
+    end
+
+    array = []
+    while 1
+      begin
+        _read_buf
+      rescue EOFError
+        array = nil if array.empty? and (not length.nil?) and length != 0
+        break
+      end
+
+      if length
+        consume = (length <= @buf.size) ? length : @buf.size
+        array.push @buf[0, consume]
+        @buf = @buf[consume, @buf.size - consume]
+        length -= consume
+        break if length == 0
+      else
+        array.push @buf
+        @buf = ''
+      end
+    end
+
+    if array.nil?
+      outbuf.replace("")
+      nil
+    else
+      outbuf.replace(array.join)
+    end
+  end
+
+  def readline(arg = $/, limit = nil)
+    case arg
+    when String
+      rs = arg
+    when Fixnum
+      rs = $/
+      limit = arg
+    else
+      raise ArgumentError
+    end
+
+    if rs.nil?
+      return read
+    end
+
+    if rs == ""
+      rs = $/ + $/
+    end
+
+    array = []
+    while 1
+      begin
+        _read_buf
+      rescue EOFError
+        array = nil if array.empty?
+        break
+      end
+
+      if limit && limit <= @buf.size
+        array.push @buf[0, limit]
+        @buf = @buf[limit, @buf.size - limit]
+        break
+      elsif idx = @buf.index(rs)
+        len = idx + rs.size
+        array.push @buf[0, len]
+        @buf = @buf[len, @buf.size - len]
+        break
+      else
+        array.push @buf
+        @buf = ''
+      end
+    end
+
+    raise EOFError.new "end of file reached" if array.nil?
+
+    array.join
+  end
+
+  def gets(*args)
+    begin
+      readline(*args)
+    rescue EOFError
+      nil
+    end
+  end
+
+  def readchar
+    _read_buf
+    c = @buf[0]
+    @buf = @buf[1, @buf.size]
+    c
+  end
+
+  def getc
+    begin
+      readchar
+    rescue EOFError
+      nil
+    end
+  end
+
+  # 15.2.20.5.3
+  def each(&block)
+    while line = self.gets
+      block.call(line)
+    end
+    self
+  end
+
+  # 15.2.20.5.4
+  def each_byte(&block)
+    while char = self.getc
+      block.call(char)
+    end
+    self
+  end
+
+  # 15.2.20.5.5
+  alias each_line each
+
+  alias each_char each_byte
+
+  def readlines
+    ary = []
+    while (line = gets)
+      ary << line
+    end
+    ary
+  end
+
+  def puts(*args)
+    i = 0
+    len = args.size
+    while i < len
+      s = args[i].to_s
+      write s
+      write "\n" if (s[-1] != "\n")
+      i += 1
+    end
+    write "\n" if len == 0
+    nil
+  end
+
+  def print(*args)
+    i = 0
+    len = args.size
+    while i < len
+      write args[i].to_s
+      i += 1
+    end
+  end
+
+  def printf(*args)
+    write sprintf(*args)
+    nil
+  end
+
+  alias_method :to_i, :fileno
+  alias_method :tty?, :isatty
+end
+
+STDIN  = IO.open(0, "r")
+STDOUT = IO.open(1, "w")
+STDERR = IO.open(2, "w")
+
+$stdin  = STDIN
+$stdout = STDOUT
+$stderr = STDERR
+
+module Kernel
+  def print(*args)
+    $stdout.print(*args)
+  end
+
+  def puts(*args)
+    $stdout.puts(*args)
+  end
+
+  def printf(*args)
+    $stdout.printf(*args)
+  end
+
+  def gets(*args)
+    $stdin.gets(*args)
+  end
+
+  def getc(*args)
+    $stdin.getc(*args)
+  end
+end
diff --git a/third-party/mruby/mrbgems/mruby-io/mrblib/kernel.rb b/third-party/mruby/mrbgems/mruby-io/mrblib/kernel.rb
new file mode 100644 (file)
index 0000000..373b76f
--- /dev/null
@@ -0,0 +1,15 @@
+module Kernel
+  def `(cmd)
+    IO.popen(cmd) { |io| io.read }
+  end
+
+  def open(file, *rest, &block)
+    raise ArgumentError unless file.is_a?(String)
+
+    if file[0] == "|"
+      IO.popen(file[1..-1], *rest, &block)
+    else
+      File.open(file, *rest, &block)
+    end
+  end
+end
diff --git a/third-party/mruby/mrbgems/mruby-io/run_test.rb b/third-party/mruby/mrbgems/mruby-io/run_test.rb
new file mode 100644 (file)
index 0000000..83d8029
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/env ruby
+#
+# mrbgems test runner
+#
+
+if __FILE__ == $0
+  repository, dir = 'https://github.com/mruby/mruby.git', 'tmp/mruby'
+  build_args = ARGV
+
+  Dir.mkdir 'tmp'  unless File.exist?('tmp')
+  unless File.exist?(dir)
+    system "git clone #{repository} #{dir}"
+  end
+
+  exit system(%Q[cd #{dir}; MRUBY_CONFIG=#{File.expand_path __FILE__} ruby minirake #{build_args.join(' ')}])
+end
+
+MRuby::Build.new do |conf|
+  toolchain :gcc
+  conf.gembox 'default'
+
+  conf.gem :git => 'https://github.com/iij/mruby-env.git'
+  conf.enable_test
+
+  conf.gem File.expand_path(File.dirname(__FILE__))
+end
diff --git a/third-party/mruby/mrbgems/mruby-io/src/file.c b/third-party/mruby/mrbgems/mruby-io/src/file.c
new file mode 100644 (file)
index 0000000..3dcfe3a
--- /dev/null
@@ -0,0 +1,504 @@
+/*
+** file.c - File class
+*/
+
+#include "mruby.h"
+#include "mruby/class.h"
+#include "mruby/data.h"
+#include "mruby/string.h"
+#include "mruby/ext/io.h"
+
+#if MRUBY_RELEASE_NO < 10000
+#include "error.h"
+#else
+#include "mruby/error.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <limits.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(_WIN32) || defined(_WIN64)
+  #include <windows.h>
+  #include <io.h>
+  #define NULL_FILE "NUL"
+  #define UNLINK _unlink
+  #define GETCWD _getcwd
+  #define CHMOD(a, b) 0
+  #define MAXPATHLEN 1024
+ #if !defined(PATH_MAX)
+  #define PATH_MAX _MAX_PATH
+ #endif
+  #define realpath(N,R) _fullpath((R),(N),_MAX_PATH)
+  #include <direct.h>
+#else
+  #define NULL_FILE "/dev/null"
+  #include <unistd.h>
+  #define UNLINK unlink
+  #define GETCWD getcwd
+  #define CHMOD(a, b) chmod(a,b)
+  #include <sys/file.h>
+  #include <libgen.h>
+  #include <sys/param.h>
+  #include <pwd.h>
+#endif
+
+#define FILE_SEPARATOR "/"
+
+#if defined(_WIN32) || defined(_WIN64)
+  #define PATH_SEPARATOR ";"
+  #define FILE_ALT_SEPARATOR "\\"
+#else
+  #define PATH_SEPARATOR ":"
+#endif
+
+#ifndef LOCK_SH
+#define LOCK_SH 1
+#endif
+#ifndef LOCK_EX
+#define LOCK_EX 2
+#endif
+#ifndef LOCK_NB
+#define LOCK_NB 4
+#endif
+#ifndef LOCK_UN
+#define LOCK_UN 8
+#endif
+
+#define STAT(p, s)        stat(p, s)
+
+#ifdef _WIN32
+static int
+flock(int fd, int operation) {
+  OVERLAPPED ov;
+  HANDLE h = (HANDLE)_get_osfhandle(fd);
+  DWORD flags;
+  flags = ((operation & LOCK_NB) ? LOCKFILE_FAIL_IMMEDIATELY : 0)
+          | ((operation & LOCK_SH) ? LOCKFILE_EXCLUSIVE_LOCK : 0);
+  memset(&ov, 0, sizeof(ov));
+  return LockFileEx(h, flags, 0, 0xffffffff, 0xffffffff, &ov) ? 0 : -1;
+}
+#endif
+
+mrb_value
+mrb_file_s_umask(mrb_state *mrb, mrb_value klass)
+{
+#if defined(_WIN32) || defined(_WIN64)
+  /* nothing to do on windows */
+  return mrb_fixnum_value(0);
+
+#else
+  mrb_int mask, omask;
+  if (mrb_get_args(mrb, "|i", &mask) == 0) {
+    omask = umask(0);
+    umask(omask);
+  } else {
+    omask = umask(mask);
+  }
+  return mrb_fixnum_value(omask);
+#endif
+}
+
+static mrb_value
+mrb_file_s_unlink(mrb_state *mrb, mrb_value obj)
+{
+  mrb_value *argv;
+  mrb_value pathv;
+  mrb_int argc, i;
+  char *path;
+
+  mrb_get_args(mrb, "*", &argv, &argc);
+  for (i = 0; i < argc; i++) {
+    pathv = mrb_convert_type(mrb, argv[i], MRB_TT_STRING, "String", "to_str");
+    path = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &pathv), -1);
+    if (UNLINK(path) < 0) {
+      mrb_locale_free(path);
+      mrb_sys_fail(mrb, path);
+    }
+    mrb_locale_free(path);
+  }
+  return mrb_fixnum_value(argc);
+}
+
+static mrb_value
+mrb_file_s_rename(mrb_state *mrb, mrb_value obj)
+{
+  mrb_value from, to;
+  char *src, *dst;
+
+  mrb_get_args(mrb, "SS", &from, &to);
+  src = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &from), -1);
+  dst = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &to), -1);
+  if (rename(src, dst) < 0) {
+#if defined(_WIN32) || defined(_WIN64)
+    if (CHMOD(dst, 0666) == 0 && UNLINK(dst) == 0 && rename(src, dst) == 0) {
+      mrb_locale_free(src);
+      mrb_locale_free(dst);
+      return mrb_fixnum_value(0);
+    }
+#endif
+    mrb_locale_free(src);
+    mrb_locale_free(dst);
+    mrb_sys_fail(mrb, mrb_str_to_cstr(mrb, mrb_format(mrb, "(%S, %S)", from, to)));
+  }
+  mrb_locale_free(src);
+  mrb_locale_free(dst);
+  return mrb_fixnum_value(0);
+}
+
+static mrb_value
+mrb_file_dirname(mrb_state *mrb, mrb_value klass)
+{
+#if defined(_WIN32) || defined(_WIN64)
+  char dname[_MAX_DIR], vname[_MAX_DRIVE];
+  char buffer[_MAX_DRIVE + _MAX_DIR];
+  char *path;
+  size_t ridx;
+  mrb_value s;
+  mrb_get_args(mrb, "S", &s);
+  path = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, s), -1);
+  _splitpath((const char*)path, vname, dname, NULL, NULL);
+  snprintf(buffer, _MAX_DRIVE + _MAX_DIR, "%s%s", vname, dname);
+  mrb_locale_free(path);
+  ridx = strlen(buffer);
+  if (ridx == 0) {
+    strncpy(buffer, ".", 2);  /* null terminated */
+  } else if (ridx > 1) {
+    ridx--;
+    while (ridx > 0 && (buffer[ridx] == '/' || buffer[ridx] == '\\')) {
+      buffer[ridx] = '\0';  /* remove last char */
+      ridx--;
+    }
+  }
+  return mrb_str_new_cstr(mrb, buffer);
+#else
+  char *dname, *path;
+  mrb_value s;
+  mrb_get_args(mrb, "S", &s);
+  path = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, s), -1);
+
+  if ((dname = dirname(path)) == NULL) {
+    mrb_locale_free(path);
+    mrb_sys_fail(mrb, "dirname");
+  }
+  mrb_locale_free(path);
+  return mrb_str_new_cstr(mrb, dname);
+#endif
+}
+
+static mrb_value
+mrb_file_basename(mrb_state *mrb, mrb_value klass)
+{
+  // NOTE: Do not use mrb_locale_from_utf8 here
+#if defined(_WIN32) || defined(_WIN64)
+  char bname[_MAX_DIR];
+  char extname[_MAX_EXT];
+  char *path;
+  size_t ridx;
+  char buffer[_MAX_DIR + _MAX_EXT];
+  mrb_value s;
+
+  mrb_get_args(mrb, "S", &s);
+  path = mrb_str_to_cstr(mrb, s);
+  ridx = strlen(path);
+  if (ridx > 0) {
+    ridx--;
+    while (ridx > 0 && (path[ridx] == '/' || path[ridx] == '\\')) {
+      path[ridx] = '\0';
+      ridx--;
+    }
+    if (strncmp(path, "/", 2) == 0) {
+      return mrb_str_new_cstr(mrb, path);
+    }
+  }
+  _splitpath((const char*)path, NULL, NULL, bname, extname);
+  snprintf(buffer, _MAX_DIR + _MAX_EXT, "%s%s", bname, extname);
+  return mrb_str_new_cstr(mrb, buffer);
+#else
+  char *bname, *path;
+  mrb_value s;
+  mrb_get_args(mrb, "S", &s);
+  path = mrb_str_to_cstr(mrb, s);
+  if ((bname = basename(path)) == NULL) {
+    mrb_sys_fail(mrb, "basename");
+  }
+  if (strncmp(bname, "//", 3) == 0) bname[1] = '\0';  /* patch for Cygwin */
+  return mrb_str_new_cstr(mrb, bname);
+#endif
+}
+
+static mrb_value
+mrb_file_realpath(mrb_state *mrb, mrb_value klass)
+{
+  mrb_value pathname, dir_string, s, result;
+  mrb_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_locale_from_utf8(mrb_str_to_cstr(mrb, pathname), -1);
+  result = mrb_str_buf_new(mrb, PATH_MAX);
+  if (realpath(cpath, RSTRING_PTR(result)) == NULL) {
+    mrb_locale_free(cpath);
+    mrb_sys_fail(mrb, cpath);
+  }
+  mrb_locale_free(cpath);
+  mrb_str_resize(mrb, result, strlen(RSTRING_PTR(result)));
+  return result;
+}
+
+mrb_value
+mrb_file__getwd(mrb_state *mrb, mrb_value klass)
+{
+  mrb_value path;
+  char buf[MAXPATHLEN], *utf8;
+
+  if (GETCWD(buf, MAXPATHLEN) == NULL) {
+    mrb_sys_fail(mrb, "getcwd(2)");
+  }
+  utf8 = mrb_utf8_from_locale(buf, -1);
+  path = mrb_str_new_cstr(mrb, utf8);
+  mrb_utf8_free(utf8);
+  return path;
+}
+
+static int
+mrb_file_is_absolute_path(const char *path)
+{
+  return (path[0] == '/');
+}
+
+static mrb_value
+mrb_file__gethome(mrb_state *mrb, mrb_value klass)
+{
+  mrb_int argc;
+  char *home;
+  mrb_value path;
+
+#ifndef _WIN32
+  mrb_value username;
+
+  argc = mrb_get_args(mrb, "|S", &username);
+  if (argc == 0) {
+    home = getenv("HOME");
+    if (home == NULL) {
+      return mrb_nil_value();
+    }
+    if (!mrb_file_is_absolute_path(home)) {
+      mrb_raise(mrb, E_ARGUMENT_ERROR, "non-absolute home");
+    }
+  } else {
+    const char *cuser = mrb_str_to_cstr(mrb, username);
+    struct passwd *pwd = getpwnam(cuser);
+    if (pwd == NULL) {
+      return mrb_nil_value();
+    }
+    home = pwd->pw_dir;
+    if (!mrb_file_is_absolute_path(home)) {
+      mrb_raisef(mrb, E_ARGUMENT_ERROR, "non-absolute home of ~%S", username);
+    }
+  }
+  home = mrb_locale_from_utf8(home, -1);
+  path = mrb_str_new_cstr(mrb, home);
+  mrb_utf8_free(home);
+  return path;
+#else
+  argc = mrb_get_argc(mrb);
+  if (argc == 0) {
+    home = getenv("USERPROFILE");
+    if (home == NULL) {
+      return mrb_nil_value();
+    }
+    if (!mrb_file_is_absolute_path(home)) {
+      mrb_raise(mrb, E_ARGUMENT_ERROR, "non-absolute home");
+    }
+  } else {
+    return mrb_nil_value();
+  }
+  home = mrb_locale_from_utf8(home, -1);
+  path = mrb_str_new_cstr(mrb, home);
+  mrb_utf8_free(home);
+  return path;
+#endif
+}
+
+static mrb_value
+mrb_file_mtime(mrb_state *mrb, mrb_value self)
+{
+  mrb_value obj;
+  struct stat st;
+  int fd;
+
+  obj = mrb_obj_value(mrb_class_get(mrb, "Time"));
+  fd = (int)mrb_fixnum(mrb_io_fileno(mrb, self));
+  if (fstat(fd, &st) == -1)
+    return mrb_false_value();
+  return mrb_funcall(mrb, obj, "at", 1, mrb_float_value(mrb, st.st_mtime));
+}
+
+mrb_value
+mrb_file_flock(mrb_state *mrb, mrb_value self)
+{
+#if defined(sun)
+  mrb_raise(mrb, E_NOTIMP_ERROR, "flock is not supported on Illumos/Solaris/Windows");
+#else
+  mrb_int operation;
+  int fd;
+
+  mrb_get_args(mrb, "i", &operation);
+  fd = (int)mrb_fixnum(mrb_io_fileno(mrb, self));
+
+  while (flock(fd, (int)operation) == -1) {
+    switch (errno) {
+      case EINTR:
+        /* retry */
+        break;
+      case EAGAIN:      /* NetBSD */
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+      case EWOULDBLOCK: /* FreeBSD OpenBSD Linux */
+#endif
+        if (operation & LOCK_NB) {
+          return mrb_false_value();
+        }
+        /* FALLTHRU - should not happen */
+      default:
+        mrb_sys_fail(mrb, "flock failed");
+        break;
+    }
+  }
+#endif
+  return mrb_fixnum_value(0);
+}
+
+static mrb_value
+mrb_file_s_symlink(mrb_state *mrb, mrb_value klass)
+{
+#if defined(_WIN32) || defined(_WIN64)
+  mrb_raise(mrb, E_NOTIMP_ERROR, "symlink is not supported on this platform");
+#else
+  mrb_value from, to;
+  const char *src, *dst;
+  int ai = mrb_gc_arena_save(mrb);
+
+  mrb_get_args(mrb, "SS", &from, &to);
+  src = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, from), -1);
+  dst = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, to), -1);
+
+  if (symlink(src, dst) == -1) {
+    mrb_locale_free(src);
+    mrb_locale_free(dst);
+    mrb_sys_fail(mrb, mrb_str_to_cstr(mrb, mrb_format(mrb, "(%S, %S)", from, to)));
+  }
+  mrb_locale_free(src);
+  mrb_locale_free(dst);
+  mrb_gc_arena_restore(mrb, ai);
+#endif
+  return mrb_fixnum_value(0);
+}
+
+static mrb_value
+mrb_file_s_chmod(mrb_state *mrb, mrb_value klass) {
+  mrb_int mode;
+  mrb_int argc, i;
+  mrb_value *filenames;
+  int ai = mrb_gc_arena_save(mrb);
+
+  mrb_get_args(mrb, "i*", &mode, &filenames, &argc);
+  for (i = 0; i < argc; i++) {
+    char *path = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, filenames[i]), -1);
+    if (CHMOD(path, mode) == -1) {
+      mrb_locale_free(path);
+      mrb_sys_fail(mrb, path);
+    }
+    mrb_locale_free(path);
+  }
+
+  mrb_gc_arena_restore(mrb, ai);
+  return mrb_fixnum_value(argc);
+}
+
+static mrb_value
+mrb_file_s_readlink(mrb_state *mrb, mrb_value klass) {
+#if defined(_WIN32) || defined(_WIN64)
+  mrb_raise(mrb, E_NOTIMP_ERROR, "readlink is not supported on this platform");
+  return mrb_nil_value(); // unreachable
+#else
+  char *path, *buf, *tmp;
+  size_t bufsize = 100;
+  ssize_t rc;
+  mrb_value ret;
+  int ai = mrb_gc_arena_save(mrb);
+
+  mrb_get_args(mrb, "z", &path);
+  tmp = mrb_locale_from_utf8(path, -1);
+
+  buf = (char *)mrb_malloc(mrb, bufsize);
+  while ((rc = readlink(tmp, buf, bufsize)) == (ssize_t)bufsize && rc != -1) {
+    bufsize *= 2;
+    buf = (char *)mrb_realloc(mrb, buf, bufsize);
+  }
+  mrb_locale_free(tmp);
+  if (rc == -1) {
+    mrb_free(mrb, buf);
+    mrb_sys_fail(mrb, path);
+  }
+  tmp = mrb_utf8_from_locale(buf, -1);
+  ret = mrb_str_new(mrb, tmp, rc);
+  mrb_locale_free(tmp);
+  mrb_free(mrb, buf);
+
+  mrb_gc_arena_restore(mrb, ai);
+  return ret;
+#endif
+}
+
+void
+mrb_init_file(mrb_state *mrb)
+{
+  struct RClass *io, *file, *cnst;
+
+  io   = mrb_class_get(mrb, "IO");
+  file = mrb_define_class(mrb, "File", io);
+  MRB_SET_INSTANCE_TT(file, MRB_TT_DATA);
+  mrb_define_class_method(mrb, file, "umask",  mrb_file_s_umask, MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, file, "delete", mrb_file_s_unlink, MRB_ARGS_ANY());
+  mrb_define_class_method(mrb, file, "unlink", mrb_file_s_unlink, MRB_ARGS_ANY());
+  mrb_define_class_method(mrb, file, "rename", mrb_file_s_rename, MRB_ARGS_REQ(2));
+  mrb_define_class_method(mrb, file, "symlink", mrb_file_s_symlink, MRB_ARGS_REQ(2));
+  mrb_define_class_method(mrb, file, "chmod", mrb_file_s_chmod, MRB_ARGS_REQ(1) | MRB_ARGS_REST());
+  mrb_define_class_method(mrb, file, "readlink", mrb_file_s_readlink, MRB_ARGS_REQ(1));
+
+  mrb_define_class_method(mrb, file, "dirname",   mrb_file_dirname,    MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, file, "basename",  mrb_file_basename,   MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, file, "realpath",  mrb_file_realpath,   MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+  mrb_define_class_method(mrb, file, "_getwd",    mrb_file__getwd,     MRB_ARGS_NONE());
+  mrb_define_class_method(mrb, file, "_gethome",  mrb_file__gethome,   MRB_ARGS_OPT(1));
+
+  mrb_define_method(mrb, file, "flock", mrb_file_flock, MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, file, "mtime", mrb_file_mtime, MRB_ARGS_NONE());
+
+  cnst = mrb_define_module_under(mrb, file, "Constants");
+  mrb_define_const(mrb, cnst, "LOCK_SH", mrb_fixnum_value(LOCK_SH));
+  mrb_define_const(mrb, cnst, "LOCK_EX", mrb_fixnum_value(LOCK_EX));
+  mrb_define_const(mrb, cnst, "LOCK_UN", mrb_fixnum_value(LOCK_UN));
+  mrb_define_const(mrb, cnst, "LOCK_NB", mrb_fixnum_value(LOCK_NB));
+  mrb_define_const(mrb, cnst, "SEPARATOR", mrb_str_new_cstr(mrb, FILE_SEPARATOR));
+  mrb_define_const(mrb, cnst, "PATH_SEPARATOR", mrb_str_new_cstr(mrb, PATH_SEPARATOR));
+#if defined(_WIN32) || defined(_WIN64)
+  mrb_define_const(mrb, cnst, "ALT_SEPARATOR", mrb_str_new_cstr(mrb, FILE_ALT_SEPARATOR));
+#else
+  mrb_define_const(mrb, cnst, "ALT_SEPARATOR", mrb_nil_value());
+#endif
+  mrb_define_const(mrb, cnst, "NULL", mrb_str_new_cstr(mrb, NULL_FILE));
+
+}
diff --git a/third-party/mruby/mrbgems/mruby-io/src/file_test.c b/third-party/mruby/mrbgems/mruby-io/src/file_test.c
new file mode 100644 (file)
index 0000000..e429b06
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+** file.c - File class
+*/
+
+#include "mruby.h"
+#include "mruby/class.h"
+#include "mruby/data.h"
+#include "mruby/string.h"
+#include "mruby/ext/io.h"
+
+#if MRUBY_RELEASE_NO < 10000
+#include "error.h"
+#else
+#include "mruby/error.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if defined(_WIN32) || defined(_WIN64)
+  #define LSTAT stat
+  #include <winsock.h>
+#else
+  #define LSTAT lstat
+  #include <sys/file.h>
+  #include <sys/param.h>
+  #include <sys/wait.h>
+  #include <libgen.h>
+  #include <pwd.h>
+  #include <unistd.h>
+#endif
+
+#include <fcntl.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+extern struct mrb_data_type mrb_io_type;
+
+static int
+mrb_stat0(mrb_state *mrb, mrb_value obj, struct stat *st, int do_lstat)
+{
+  mrb_value tmp;
+  mrb_value io_klass, str_klass;
+
+  io_klass  = mrb_obj_value(mrb_class_get(mrb, "IO"));
+  str_klass = mrb_obj_value(mrb_class_get(mrb, "String"));
+
+  tmp = mrb_funcall(mrb, obj, "is_a?", 1, io_klass);
+  if (mrb_test(tmp)) {
+    struct mrb_io *fptr;
+    fptr = (struct mrb_io *)mrb_get_datatype(mrb, obj, &mrb_io_type);
+
+    if (fptr && fptr->fd >= 0) {
+      return fstat(fptr->fd, st);
+    }
+
+    mrb_raise(mrb, E_IO_ERROR, "closed stream");
+    return -1;
+  }
+
+  tmp = mrb_funcall(mrb, obj, "is_a?", 1, str_klass);
+  if (mrb_test(tmp)) {
+    char *path = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, obj), -1);
+    int ret;
+    if (do_lstat) {
+      ret = LSTAT(path, st);
+    } else {
+      ret = stat(path, st);
+    }
+    mrb_locale_free(path);
+    return ret;
+  }
+
+  return -1;
+}
+
+static int
+mrb_stat(mrb_state *mrb, mrb_value obj, struct stat *st)
+{
+  return mrb_stat0(mrb, obj, st, 0);
+}
+
+static int
+mrb_lstat(mrb_state *mrb, mrb_value obj, struct stat *st)
+{
+  return mrb_stat0(mrb, obj, st, 1);
+}
+
+/*
+ * Document-method: directory?
+ *
+ * call-seq:
+ *   File.directory?(file_name)   ->  true or false
+ *
+ * Returns <code>true</code> if the named file is a directory,
+ * or a symlink that points at a directory, and <code>false</code>
+ * otherwise.
+ *
+ *    File.directory?(".")
+ */
+
+mrb_value
+mrb_filetest_s_directory_p(mrb_state *mrb, mrb_value klass)
+{
+#ifndef S_ISDIR
+#   define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+
+  struct stat st;
+  mrb_value obj;
+
+  mrb_get_args(mrb, "o", &obj);
+
+  if (mrb_stat(mrb, obj, &st) < 0)
+    return mrb_false_value();
+  if (S_ISDIR(st.st_mode))
+    return mrb_true_value();
+
+  return mrb_false_value();
+}
+
+/*
+ * call-seq:
+ *   File.pipe?(file_name)   ->  true or false
+ *
+ * Returns <code>true</code> if the named file is a pipe.
+ */
+
+mrb_value
+mrb_filetest_s_pipe_p(mrb_state *mrb, mrb_value klass)
+{
+#if defined(_WIN32) || defined(_WIN64)
+  mrb_raise(mrb, E_NOTIMP_ERROR, "pipe is not supported on this platform");
+#else
+#ifdef S_IFIFO
+#  ifndef S_ISFIFO
+#    define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
+#  endif
+
+  struct stat st;
+  mrb_value obj;
+
+  mrb_get_args(mrb, "o", &obj);
+
+  if (mrb_stat(mrb, obj, &st) < 0)
+    return mrb_false_value();
+  if (S_ISFIFO(st.st_mode))
+    return mrb_true_value();
+
+#endif
+  return mrb_false_value();
+#endif
+}
+
+/*
+ * call-seq:
+ *   File.symlink?(file_name)   ->  true or false
+ *
+ * Returns <code>true</code> if the named file is a symbolic link.
+ */
+
+mrb_value
+mrb_filetest_s_symlink_p(mrb_state *mrb, mrb_value klass)
+{
+#if defined(_WIN32) || defined(_WIN64)
+  mrb_raise(mrb, E_NOTIMP_ERROR, "symlink is not supported on this platform");
+#else
+#ifndef S_ISLNK
+#  ifdef _S_ISLNK
+#    define S_ISLNK(m) _S_ISLNK(m)
+#  else
+#    ifdef _S_IFLNK
+#      define S_ISLNK(m) (((m) & S_IFMT) == _S_IFLNK)
+#    else
+#      ifdef S_IFLNK
+#        define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#      endif
+#    endif
+#  endif
+#endif
+
+#ifdef S_ISLNK
+  struct stat st;
+  mrb_value obj;
+
+  mrb_get_args(mrb, "o", &obj);
+
+  if (mrb_lstat(mrb, obj, &st) == -1)
+    return mrb_false_value();
+  if (S_ISLNK(st.st_mode))
+    return mrb_true_value();
+#endif
+
+  return mrb_false_value();
+#endif
+}
+
+/*
+ * call-seq:
+ *   File.socket?(file_name)   ->  true or false
+ *
+ * Returns <code>true</code> if the named file is a socket.
+ */
+
+mrb_value
+mrb_filetest_s_socket_p(mrb_state *mrb, mrb_value klass)
+{
+#if defined(_WIN32) || defined(_WIN64)
+  mrb_raise(mrb, E_NOTIMP_ERROR, "socket is not supported on this platform");
+#else
+#ifndef S_ISSOCK
+#  ifdef _S_ISSOCK
+#    define S_ISSOCK(m) _S_ISSOCK(m)
+#  else
+#    ifdef _S_IFSOCK
+#      define S_ISSOCK(m) (((m) & S_IFMT) == _S_IFSOCK)
+#    else
+#      ifdef S_IFSOCK
+#        define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
+#      endif
+#    endif
+#  endif
+#endif
+
+#ifdef S_ISSOCK
+  struct stat st;
+  mrb_value obj;
+
+  mrb_get_args(mrb, "o", &obj);
+
+  if (mrb_stat(mrb, obj, &st) < 0)
+    return mrb_false_value();
+  if (S_ISSOCK(st.st_mode))
+    return mrb_true_value();
+#endif
+
+  return mrb_false_value();
+#endif
+}
+
+/*
+ * call-seq:
+ *    File.exist?(file_name)    ->  true or false
+ *    File.exists?(file_name)   ->  true or false
+ *
+ * Return <code>true</code> if the named file exists.
+ */
+
+mrb_value
+mrb_filetest_s_exist_p(mrb_state *mrb, mrb_value klass)
+{
+  struct stat st;
+  mrb_value obj;
+
+  mrb_get_args(mrb, "o", &obj);
+  if (mrb_stat(mrb, obj, &st) < 0)
+    return mrb_false_value();
+
+  return mrb_true_value();
+}
+
+/*
+ * call-seq:
+ *    File.file?(file_name)   -> true or false
+ *
+ * Returns <code>true</code> if the named file exists and is a
+ * regular file.
+ */
+
+mrb_value
+mrb_filetest_s_file_p(mrb_state *mrb, mrb_value klass)
+{
+#ifndef S_ISREG
+#   define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+
+  struct stat st;
+  mrb_value obj;
+
+  mrb_get_args(mrb, "o", &obj);
+
+  if (mrb_stat(mrb, obj, &st) < 0)
+    return mrb_false_value();
+  if (S_ISREG(st.st_mode))
+    return mrb_true_value();
+
+  return mrb_false_value();
+}
+
+/*
+ * call-seq:
+ *    File.zero?(file_name)   -> true or false
+ *
+ * Returns <code>true</code> if the named file exists and has
+ * a zero size.
+ */
+
+mrb_value
+mrb_filetest_s_zero_p(mrb_state *mrb, mrb_value klass)
+{
+  struct stat st;
+  mrb_value obj;
+
+  mrb_get_args(mrb, "o", &obj);
+
+  if (mrb_stat(mrb, obj, &st) < 0)
+    return mrb_false_value();
+  if (st.st_size == 0)
+    return mrb_true_value();
+
+  return mrb_false_value();
+}
+
+/*
+ * call-seq:
+ *    File.size(file_name)   -> integer
+ *
+ * Returns the size of <code>file_name</code>.
+ *
+ * _file_name_ can be an IO object.
+ */
+
+mrb_value
+mrb_filetest_s_size(mrb_state *mrb, mrb_value klass)
+{
+  struct stat st;
+  mrb_value obj;
+
+  mrb_get_args(mrb, "o", &obj);
+
+  if (mrb_stat(mrb, obj, &st) < 0)
+    mrb_sys_fail(mrb, "mrb_stat");
+
+  return mrb_fixnum_value(st.st_size);
+}
+
+/*
+ * call-seq:
+ *    File.size?(file_name)   -> Integer or nil
+ *
+ * Returns +nil+ if +file_name+ doesn't exist or has zero size, the size of the
+ * file otherwise.
+ */
+
+mrb_value
+mrb_filetest_s_size_p(mrb_state *mrb, mrb_value klass)
+{
+  struct stat st;
+  mrb_value obj;
+
+  mrb_get_args(mrb, "o", &obj);
+
+  if (mrb_stat(mrb, obj, &st) < 0)
+    return mrb_nil_value();
+  if (st.st_size == 0)
+    return mrb_nil_value();
+
+  return mrb_fixnum_value(st.st_size);
+}
+
+void
+mrb_init_file_test(mrb_state *mrb)
+{
+  struct RClass *f;
+
+  f = mrb_define_class(mrb, "FileTest", mrb->object_class);
+
+  mrb_define_class_method(mrb, f, "directory?", mrb_filetest_s_directory_p, MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, f, "exist?",     mrb_filetest_s_exist_p,     MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, f, "exists?",    mrb_filetest_s_exist_p,     MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, f, "file?",      mrb_filetest_s_file_p,      MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, f, "pipe?",      mrb_filetest_s_pipe_p,      MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, f, "size",       mrb_filetest_s_size,        MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, f, "size?",      mrb_filetest_s_size_p,      MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, f, "socket?",    mrb_filetest_s_socket_p,    MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, f, "symlink?",   mrb_filetest_s_symlink_p,   MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, f, "zero?",      mrb_filetest_s_zero_p,      MRB_ARGS_REQ(1));
+}
diff --git a/third-party/mruby/mrbgems/mruby-io/src/io.c b/third-party/mruby/mrbgems/mruby-io/src/io.c
new file mode 100644 (file)
index 0000000..bc17dd9
--- /dev/null
@@ -0,0 +1,1329 @@
+/*
+** io.c - IO class
+*/
+
+#include "mruby.h"
+#include "mruby/array.h"
+#include "mruby/class.h"
+#include "mruby/data.h"
+#include "mruby/hash.h"
+#include "mruby/string.h"
+#include "mruby/variable.h"
+#include "mruby/ext/io.h"
+
+#if MRUBY_RELEASE_NO < 10000
+#include "error.h"
+#else
+#include "mruby/error.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if defined(_WIN32) || defined(_WIN64)
+  #include <winsock.h>
+  #include <io.h>
+  #define open  _open
+  #define close _close
+  #define dup _dup
+  #define dup2 _dup2
+  #define read  _read
+  #define write _write
+  #define lseek _lseek
+  #define isatty _isatty
+  #define WEXITSTATUS(x) (x)
+  typedef int fsize_t;
+  typedef long ftime_t;
+  typedef long fsuseconds_t;
+  typedef int fmode_t;
+
+#else
+  #include <sys/wait.h>
+  #include <unistd.h>
+  typedef size_t fsize_t;
+  typedef time_t ftime_t;
+  typedef suseconds_t fsuseconds_t;
+  typedef mode_t fmode_t;
+#endif
+
+#ifdef _MSC_VER
+typedef mrb_int pid_t;
+#endif
+
+#include <fcntl.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+
+static void mrb_io_free(mrb_state *mrb, void *ptr);
+struct mrb_data_type mrb_io_type = { "IO", mrb_io_free };
+
+
+static struct mrb_io *io_get_open_fptr(mrb_state *mrb, mrb_value self);
+static int mrb_io_modestr_to_flags(mrb_state *mrb, const char *modestr);
+static int mrb_io_flags_to_modenum(mrb_state *mrb, int flags);
+static void fptr_finalize(mrb_state *mrb, struct mrb_io *fptr, int quiet);
+
+#if MRUBY_RELEASE_NO < 10000
+static struct RClass *
+mrb_module_get(mrb_state *mrb, const char *name)
+{
+  return mrb_class_get(mrb, name);
+}
+#endif
+
+static struct mrb_io *
+io_get_open_fptr(mrb_state *mrb, mrb_value self)
+{
+  struct mrb_io *fptr;
+
+  fptr = (struct mrb_io *)mrb_get_datatype(mrb, self, &mrb_io_type);
+  if (fptr->fd < 0) {
+    mrb_raise(mrb, E_IO_ERROR, "closed stream.");
+  }
+  return fptr;
+}
+
+static void
+io_set_process_status(mrb_state *mrb, pid_t pid, int status)
+{
+  struct RClass *c_process, *c_status;
+  mrb_value v;
+
+  c_status = NULL;
+  if (mrb_class_defined(mrb, "Process")) {
+    c_process = mrb_module_get(mrb, "Process");
+    if (mrb_const_defined(mrb, mrb_obj_value(c_process), mrb_intern_cstr(mrb, "Status"))) {
+      c_status = mrb_class_get_under(mrb, c_process, "Status");
+    }
+  }
+  if (c_status != NULL) {
+    v = mrb_funcall(mrb, mrb_obj_value(c_status), "new", 2, mrb_fixnum_value(pid), mrb_fixnum_value(status));
+  } else {
+    v = mrb_fixnum_value(WEXITSTATUS(status));
+  }
+  mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$?"), v);
+}
+
+static int
+mrb_io_modestr_to_flags(mrb_state *mrb, const char *mode)
+{
+  int flags = 0;
+  const char *m = mode;
+
+  switch (*m++) {
+    case 'r':
+      flags |= FMODE_READABLE;
+      break;
+    case 'w':
+      flags |= FMODE_WRITABLE | FMODE_CREATE | FMODE_TRUNC;
+      break;
+    case 'a':
+      flags |= FMODE_WRITABLE | FMODE_APPEND | FMODE_CREATE;
+      break;
+    default:
+      mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %S", mrb_str_new_cstr(mrb, mode));
+  }
+
+  while (*m) {
+    switch (*m++) {
+      case 'b':
+        flags |= FMODE_BINMODE;
+        break;
+      case '+':
+        flags |= FMODE_READWRITE;
+        break;
+      case ':':
+        /* XXX: PASSTHROUGH*/
+      default:
+        mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal access mode %S", mrb_str_new_cstr(mrb, mode));
+    }
+  }
+
+  return flags;
+}
+
+static int
+mrb_io_flags_to_modenum(mrb_state *mrb, int flags)
+{
+  int modenum = 0;
+
+  switch(flags & (FMODE_READABLE|FMODE_WRITABLE|FMODE_READWRITE)) {
+    case FMODE_READABLE:
+      modenum = O_RDONLY;
+      break;
+    case FMODE_WRITABLE:
+      modenum = O_WRONLY;
+      break;
+    case FMODE_READWRITE:
+      modenum = O_RDWR;
+      break;
+  }
+
+  if (flags & FMODE_APPEND) {
+    modenum |= O_APPEND;
+  }
+  if (flags & FMODE_TRUNC) {
+    modenum |= O_TRUNC;
+  }
+  if (flags & FMODE_CREATE) {
+    modenum |= O_CREAT;
+  }
+#ifdef O_BINARY
+  if (flags & FMODE_BINMODE) {
+    modenum |= O_BINARY;
+  }
+#endif
+
+  return modenum;
+}
+
+static void
+mrb_fd_cloexec(mrb_state *mrb, int fd)
+{
+#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
+  int flags, flags2;
+
+  flags = fcntl(fd, F_GETFD);
+  if (flags == -1) {
+    mrb_bug(mrb, "mrb_fd_cloexec: fcntl(%S, F_GETFD) failed: %S",
+      mrb_fixnum_value(fd), mrb_fixnum_value(errno));
+  }
+  if (fd <= 2) {
+    flags2 = flags & ~FD_CLOEXEC; /* Clear CLOEXEC for standard file descriptors: 0, 1, 2. */
+  }
+  else {
+    flags2 = flags | FD_CLOEXEC; /* Set CLOEXEC for non-standard file descriptors: 3, 4, 5, ... */
+  }
+  if (flags != flags2) {
+    if (fcntl(fd, F_SETFD, flags2) == -1) {
+      mrb_bug(mrb, "mrb_fd_cloexec: fcntl(%S, F_SETFD, %S) failed: %S",
+        mrb_fixnum_value(fd), mrb_fixnum_value(flags2), mrb_fixnum_value(errno));
+    }
+  }
+#endif
+}
+
+#ifndef _WIN32
+static int
+mrb_cloexec_pipe(mrb_state *mrb, int fildes[2])
+{
+  int ret;
+  ret = pipe(fildes);
+  if (ret == -1)
+    return -1;
+  mrb_fd_cloexec(mrb, fildes[0]);
+  mrb_fd_cloexec(mrb, fildes[1]);
+  return ret;
+}
+
+static int
+mrb_pipe(mrb_state *mrb, int pipes[2])
+{
+  int ret;
+  ret = mrb_cloexec_pipe(mrb, pipes);
+  if (ret == -1) {
+    if (errno == EMFILE || errno == ENFILE) {
+      mrb_garbage_collect(mrb);
+      ret = mrb_cloexec_pipe(mrb, pipes);
+    }
+  }
+  return ret;
+}
+
+static int
+mrb_proc_exec(const char *pname)
+{
+  const char *s;
+  s = pname;
+
+  while (*s == ' ' || *s == '\t' || *s == '\n')
+    s++;
+
+  if (!*s) {
+    errno = ENOENT;
+    return -1;
+  }
+
+  execl("/bin/sh", "sh", "-c", pname, (char *)NULL);
+  return -1;
+}
+#endif
+
+static void
+mrb_io_free(mrb_state *mrb, void *ptr)
+{
+  struct mrb_io *io = (struct mrb_io *)ptr;
+  if (io != NULL) {
+    fptr_finalize(mrb, io, TRUE);
+    mrb_free(mrb, io);
+  }
+}
+
+static struct mrb_io *
+mrb_io_alloc(mrb_state *mrb)
+{
+  struct mrb_io *fptr;
+
+  fptr = (struct mrb_io *)mrb_malloc(mrb, sizeof(struct mrb_io));
+  fptr->fd = -1;
+  fptr->fd2 = -1;
+  fptr->pid = 0;
+  fptr->readable = 0;
+  fptr->writable = 0;
+  fptr->sync = 0;
+  fptr->is_socket = 0;
+  return fptr;
+}
+
+#ifndef NOFILE
+#define NOFILE 64
+#endif
+
+static int
+option_to_fd(mrb_state *mrb, mrb_value obj, const char *key)
+{
+  mrb_value opt = mrb_funcall(mrb, obj, "[]", 1, mrb_symbol_value(mrb_intern_static(mrb, key, strlen(key))));
+  if (mrb_nil_p(opt)) {
+    return -1;
+  }
+
+  switch (mrb_type(opt)) {
+    case MRB_TT_DATA: /* IO */
+      return (int)mrb_fixnum(mrb_io_fileno(mrb, opt));
+    case MRB_TT_FIXNUM:
+      return (int)mrb_fixnum(opt);
+    default:
+      mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong exec redirect action");
+      break;
+  }
+  return -1; /* never reached */
+}
+
+#ifndef _WIN32
+mrb_value
+mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
+{
+  mrb_value cmd, io, result;
+  mrb_value mode = mrb_str_new_cstr(mrb, "r");
+  mrb_value opt  = mrb_hash_new(mrb);
+
+  struct mrb_io *fptr;
+  const char *pname;
+  int pid, flags, fd, write_fd = -1;
+  int pr[2] = { -1, -1 };
+  int pw[2] = { -1, -1 };
+  int doexec;
+  int saved_errno;
+  int opt_in, opt_out, opt_err;
+
+  mrb_get_args(mrb, "S|SH", &cmd, &mode, &opt);
+  io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
+
+  pname = mrb_string_value_cstr(mrb, &cmd);
+  flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode));
+
+  doexec = (strcmp("-", pname) != 0);
+  opt_in = option_to_fd(mrb, opt, "in");
+  opt_out = option_to_fd(mrb, opt, "out");
+  opt_err = option_to_fd(mrb, opt, "err");
+
+  if (flags & FMODE_READABLE) {
+    if (pipe(pr) == -1) {
+      mrb_sys_fail(mrb, "pipe");
+    }
+    mrb_fd_cloexec(mrb, pr[0]);
+    mrb_fd_cloexec(mrb, pr[1]);
+  }
+
+  if (flags & FMODE_WRITABLE) {
+    if (pipe(pw) == -1) {
+      if (pr[0] != -1) close(pr[0]);
+      if (pr[1] != -1) close(pr[1]);
+      mrb_sys_fail(mrb, "pipe");
+    }
+    mrb_fd_cloexec(mrb, pw[0]);
+    mrb_fd_cloexec(mrb, pw[1]);
+  }
+
+  if (!doexec) {
+    // XXX
+    fflush(stdin);
+    fflush(stdout);
+    fflush(stderr);
+  }
+
+  result = mrb_nil_value();
+  switch (pid = fork()) {
+    case 0: /* child */
+      if (opt_in != -1) {
+        dup2(opt_in, 0);
+      }
+      if (opt_out != -1) {
+        dup2(opt_out, 1);
+      }
+      if (opt_err != -1) {
+        dup2(opt_err, 2);
+      }
+      if (flags & FMODE_READABLE) {
+        close(pr[0]);
+        if (pr[1] != 1) {
+          dup2(pr[1], 1);
+          close(pr[1]);
+        }
+      }
+      if (flags & FMODE_WRITABLE) {
+        close(pw[1]);
+        if (pw[0] != 0) {
+          dup2(pw[0], 0);
+          close(pw[0]);
+        }
+      }
+      if (doexec) {
+        for (fd = 3; fd < NOFILE; fd++) {
+          close(fd);
+        }
+        mrb_proc_exec(pname);
+        mrb_raisef(mrb, E_IO_ERROR, "command not found: %S", cmd);
+        _exit(127);
+      }
+      result = mrb_nil_value();
+      break;
+
+    default: /* parent */
+      if ((flags & FMODE_READABLE) && (flags & FMODE_WRITABLE)) {
+        close(pr[1]);
+        fd = pr[0];
+        close(pw[0]);
+        write_fd = pw[1];
+      } else if (flags & FMODE_READABLE) {
+        close(pr[1]);
+        fd = pr[0];
+      } else {
+        close(pw[0]);
+        fd = pw[1];
+      }
+
+      mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
+
+      fptr = mrb_io_alloc(mrb);
+      fptr->fd = fd;
+      fptr->fd2 = write_fd;
+      fptr->pid = pid;
+      fptr->readable = ((flags & FMODE_READABLE) != 0);
+      fptr->writable = ((flags & FMODE_WRITABLE) != 0);
+      fptr->sync = 0;
+
+      DATA_TYPE(io) = &mrb_io_type;
+      DATA_PTR(io)  = fptr;
+      result = io;
+      break;
+
+    case -1: /* error */
+      saved_errno = errno;
+      if (flags & FMODE_READABLE) {
+        close(pr[0]);
+        close(pr[1]);
+      }
+      if (flags & FMODE_WRITABLE) {
+        close(pw[0]);
+        close(pw[1]);
+      }
+      errno = saved_errno;
+      mrb_sys_fail(mrb, "pipe_open failed.");
+      break;
+  }
+  return result;
+}
+#else
+mrb_value
+mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
+{
+  mrb_value cmd, io;
+  mrb_value mode = mrb_str_new_cstr(mrb, "r");
+  mrb_value opt  = mrb_hash_new(mrb);
+
+  struct mrb_io *fptr;
+  const char *pname;
+  int pid = 0, flags;
+  STARTUPINFO si;
+  PROCESS_INFORMATION pi;
+  SECURITY_ATTRIBUTES saAttr;
+
+  HANDLE ifd[2];
+  HANDLE ofd[2];
+
+  int doexec;
+  int opt_in, opt_out, opt_err;
+
+  ifd[0] = INVALID_HANDLE_VALUE;
+  ifd[1] = INVALID_HANDLE_VALUE;
+  ofd[0] = INVALID_HANDLE_VALUE;
+  ofd[1] = INVALID_HANDLE_VALUE;
+
+  mrb_get_args(mrb, "S|SH", &cmd, &mode, &opt);
+  io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
+
+  pname = mrb_string_value_cstr(mrb, &cmd);
+  flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode));
+
+  doexec = (strcmp("-", pname) != 0);
+  opt_in = option_to_fd(mrb, opt, "in");
+  opt_out = option_to_fd(mrb, opt, "out");
+  opt_err = option_to_fd(mrb, opt, "err");
+
+  saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+  saAttr.bInheritHandle = TRUE;
+  saAttr.lpSecurityDescriptor = NULL;
+
+  if (flags & FMODE_READABLE) {
+    if (!CreatePipe(&ofd[0], &ofd[1], &saAttr, 0)
+        || !SetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0)) {
+      mrb_sys_fail(mrb, "pipe");
+    }
+  }
+
+  if (flags & FMODE_WRITABLE) {
+    if (!CreatePipe(&ifd[0], &ifd[1], &saAttr, 0)
+        || !SetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)) {
+      mrb_sys_fail(mrb, "pipe");
+    }
+  }
+
+  if (doexec) {
+    ZeroMemory(&pi, sizeof(pi));
+    ZeroMemory(&si, sizeof(si));
+    si.cb = sizeof(si);
+    si.dwFlags |= STARTF_USESHOWWINDOW;
+    si.wShowWindow = SW_HIDE;
+    si.dwFlags |= STARTF_USESTDHANDLES;
+    if (flags & FMODE_READABLE) {
+      si.hStdOutput = ofd[1];
+      si.hStdError = ofd[1];
+    }
+    if (flags & FMODE_WRITABLE) {
+      si.hStdInput = ifd[0];
+    }
+    if (!CreateProcess(
+        NULL, (char*)pname, NULL, NULL,
+        TRUE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi)) {
+      CloseHandle(ifd[0]);
+      CloseHandle(ifd[1]);
+      CloseHandle(ofd[0]);
+      CloseHandle(ofd[1]);
+      mrb_raisef(mrb, E_IO_ERROR, "command not found: %S", cmd);
+    }
+    CloseHandle(pi.hThread);
+    CloseHandle(ifd[0]);
+    CloseHandle(ofd[1]);
+    pid = pi.dwProcessId;
+  }
+
+  mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
+
+  fptr = mrb_io_alloc(mrb);
+  fptr->fd = _open_osfhandle((intptr_t)ofd[0], 0);
+  fptr->fd2 = _open_osfhandle((intptr_t)ifd[1], 0);
+  fptr->pid = pid;
+  fptr->readable = ((flags & FMODE_READABLE) != 0);
+  fptr->writable = ((flags & FMODE_WRITABLE) != 0);
+  fptr->sync = 0;
+
+  DATA_TYPE(io) = &mrb_io_type;
+  DATA_PTR(io)  = fptr;
+  return io;
+}
+#endif
+
+static int
+mrb_dup(mrb_state *mrb, int fd, mrb_bool *failed)
+{
+  int new_fd;
+
+  *failed = TRUE;
+  if (fd < 0)
+    return fd;
+
+  new_fd = dup(fd);
+  if (new_fd > 0) *failed = FALSE;
+  return new_fd;
+}
+
+mrb_value
+mrb_io_initialize_copy(mrb_state *mrb, mrb_value copy)
+{
+  mrb_value orig;
+  mrb_value buf;
+  struct mrb_io *fptr_copy;
+  struct mrb_io *fptr_orig;
+  mrb_bool failed = TRUE;
+
+  mrb_get_args(mrb, "o", &orig);
+  fptr_orig = io_get_open_fptr(mrb, orig);
+  fptr_copy = (struct mrb_io *)DATA_PTR(copy);
+  if (fptr_orig == fptr_copy) return copy;
+  if (fptr_copy != NULL) {
+    fptr_finalize(mrb, fptr_copy, FALSE);
+    mrb_free(mrb, fptr_copy);
+  }
+  fptr_copy = (struct mrb_io *)mrb_io_alloc(mrb);
+
+  DATA_TYPE(copy) = &mrb_io_type;
+  DATA_PTR(copy) = fptr_copy;
+
+  buf = mrb_iv_get(mrb, orig, mrb_intern_cstr(mrb, "@buf"));
+  mrb_iv_set(mrb, copy, mrb_intern_cstr(mrb, "@buf"), buf);
+
+  fptr_copy->fd = mrb_dup(mrb, fptr_orig->fd, &failed);
+  if (failed) {
+    mrb_sys_fail(mrb, 0);
+  }
+  mrb_fd_cloexec(mrb, fptr_copy->fd);
+
+  if (fptr_orig->fd2 != -1) {
+    fptr_copy->fd2 = mrb_dup(mrb, fptr_orig->fd2, &failed);
+    if (failed) {
+      close(fptr_copy->fd);
+      mrb_sys_fail(mrb, 0);
+    }
+    mrb_fd_cloexec(mrb, fptr_copy->fd2);
+  }
+
+  fptr_copy->pid = fptr_orig->pid;
+  fptr_copy->readable = fptr_orig->readable;
+  fptr_copy->writable = fptr_orig->writable;
+  fptr_copy->sync = fptr_orig->sync;
+  fptr_copy->is_socket = fptr_orig->is_socket;
+
+  return copy;
+}
+
+mrb_value
+mrb_io_initialize(mrb_state *mrb, mrb_value io)
+{
+  struct mrb_io *fptr;
+  mrb_int fd;
+  mrb_value mode, opt;
+  int flags;
+
+  mode = opt = mrb_nil_value();
+
+  mrb_get_args(mrb, "i|So", &fd, &mode, &opt);
+  if (mrb_nil_p(mode)) {
+    mode = mrb_str_new_cstr(mrb, "r");
+  }
+  if (mrb_nil_p(opt)) {
+    opt = mrb_hash_new(mrb);
+  }
+
+  flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode));
+
+  mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
+
+  fptr = (struct mrb_io *)DATA_PTR(io);
+  if (fptr != NULL) {
+    fptr_finalize(mrb, fptr, TRUE);
+    mrb_free(mrb, fptr);
+  }
+  fptr = mrb_io_alloc(mrb);
+
+  DATA_TYPE(io) = &mrb_io_type;
+  DATA_PTR(io) = fptr;
+
+  fptr->fd = (int)fd;
+  fptr->readable = ((flags & FMODE_READABLE) != 0);
+  fptr->writable = ((flags & FMODE_WRITABLE) != 0);
+  fptr->sync = 0;
+  return io;
+}
+
+static void
+fptr_finalize(mrb_state *mrb, struct mrb_io *fptr, int quiet)
+{
+  int saved_errno = 0;
+
+  if (fptr == NULL) {
+    return;
+  }
+
+  if (fptr->fd > 2) {
+#ifdef _WIN32
+    if (fptr->is_socket) {
+      if (closesocket(fptr->fd) != 0) {
+        saved_errno = WSAGetLastError();
+      }
+      fptr->fd = -1;
+    }
+#endif
+    if (fptr->fd != -1) {
+      if (close(fptr->fd) == -1) {
+        saved_errno = errno;
+      }
+    }
+    fptr->fd = -1;
+  }
+
+  if (fptr->fd2 > 2) {
+    if (close(fptr->fd2) == -1) {
+      if (saved_errno == 0) {
+        saved_errno = errno;
+      }
+    }
+    fptr->fd2 = -1;
+  }
+
+  if (fptr->pid != 0) {
+#if !defined(_WIN32) && !defined(_WIN64)
+    pid_t pid;
+    int status;
+    do {
+      pid = waitpid(fptr->pid, &status, 0);
+    } while (pid == -1 && errno == EINTR);
+    if (!quiet && pid == fptr->pid) {
+      io_set_process_status(mrb, pid, status);
+    }
+#else
+    HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, fptr->pid);
+    DWORD status;
+    if (WaitForSingleObject(h, INFINITE) && GetExitCodeProcess(h, &status))
+      if (!quiet)
+        io_set_process_status(mrb, fptr->pid, (int)status);
+    CloseHandle(h);
+#endif
+    fptr->pid = 0;
+    /* Note: we don't raise an exception when waitpid(3) fails */
+  }
+
+  if (!quiet && saved_errno != 0) {
+    errno = saved_errno;
+    mrb_sys_fail(mrb, "fptr_finalize failed.");
+  }
+}
+
+mrb_value
+mrb_io_check_readable(mrb_state *mrb, mrb_value self)
+{
+  struct mrb_io *fptr = io_get_open_fptr(mrb, self);
+  if (! fptr->readable) {
+    mrb_raise(mrb, E_IO_ERROR, "not opened for reading");
+  }
+  return mrb_nil_value();
+}
+
+mrb_value
+mrb_io_isatty(mrb_state *mrb, mrb_value self)
+{
+  struct mrb_io *fptr;
+
+  fptr = io_get_open_fptr(mrb, self);
+  if (isatty(fptr->fd) == 0)
+    return mrb_false_value();
+  return mrb_true_value();
+}
+
+mrb_value
+mrb_io_s_for_fd(mrb_state *mrb, mrb_value klass)
+{
+  struct RClass *c = mrb_class_ptr(klass);
+  enum mrb_vtype ttype = MRB_INSTANCE_TT(c);
+  mrb_value obj;
+
+  /* copied from mrb_instance_alloc() */
+  if (ttype == 0) ttype = MRB_TT_OBJECT;
+  obj = mrb_obj_value((struct RObject*)mrb_obj_alloc(mrb, ttype, c));
+  return mrb_io_initialize(mrb, obj);
+}
+
+mrb_value
+mrb_io_s_sysclose(mrb_state *mrb, mrb_value klass)
+{
+  mrb_int fd;
+  mrb_get_args(mrb, "i", &fd);
+  if (close((int)fd) == -1) {
+    mrb_sys_fail(mrb, "close");
+  }
+  return mrb_fixnum_value(0);
+}
+
+int
+mrb_cloexec_open(mrb_state *mrb, const char *pathname, mrb_int flags, mrb_int mode)
+{
+  mrb_value emsg;
+  int fd, retry = FALSE;
+  char* fname = mrb_locale_from_utf8(pathname, -1);
+
+#ifdef O_CLOEXEC
+  /* O_CLOEXEC is available since Linux 2.6.23.  Linux 2.6.18 silently ignore it. */
+  flags |= O_CLOEXEC;
+#elif defined O_NOINHERIT
+  flags |= O_NOINHERIT;
+#endif
+reopen:
+  fd = open(fname, (int)flags, (fmode_t)mode);
+  if (fd == -1) {
+    if (!retry) {
+      switch (errno) {
+        case ENFILE:
+        case EMFILE:
+        mrb_garbage_collect(mrb);
+        retry = TRUE;
+        goto reopen;
+      }
+    }
+
+    emsg = mrb_format(mrb, "open %S", mrb_str_new_cstr(mrb, pathname));
+    mrb_str_modify(mrb, mrb_str_ptr(emsg));
+    mrb_sys_fail(mrb, RSTRING_PTR(emsg));
+  }
+  mrb_utf8_free(fname);
+
+  if (fd <= 2) {
+    mrb_fd_cloexec(mrb, fd);
+  }
+  return fd;
+}
+
+mrb_value
+mrb_io_s_sysopen(mrb_state *mrb, mrb_value klass)
+{
+  mrb_value path = mrb_nil_value();
+  mrb_value mode = mrb_nil_value();
+  mrb_int fd, perm = -1;
+  const char *pat;
+  int flags, modenum;
+
+  mrb_get_args(mrb, "S|Si", &path, &mode, &perm);
+  if (mrb_nil_p(mode)) {
+    mode = mrb_str_new_cstr(mrb, "r");
+  }
+  if (perm < 0) {
+    perm = 0666;
+  }
+
+  pat = mrb_string_value_cstr(mrb, &path);
+  flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode));
+  modenum = mrb_io_flags_to_modenum(mrb, flags);
+  fd = mrb_cloexec_open(mrb, pat, modenum, perm);
+  return mrb_fixnum_value(fd);
+}
+
+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;
+}
+
+mrb_value
+mrb_io_sysseek(mrb_state *mrb, mrb_value io)
+{
+  struct mrb_io *fptr;
+  off_t pos;
+  mrb_int offset, whence = -1;
+
+  mrb_get_args(mrb, "i|i", &offset, &whence);
+  if (whence < 0) {
+    whence = 0;
+  }
+
+  fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type);
+  pos = lseek(fptr->fd, (off_t)offset, (int)whence);
+  if (pos == -1) {
+    mrb_sys_fail(mrb, "sysseek");
+  }
+  if (pos > MRB_INT_MAX) {
+#ifndef MRB_WITHOUT_FLOAT
+    return mrb_float_value(mrb, (mrb_float)pos);
+#else
+    mrb_raise(mrb, E_IO_ERROR, "sysseek reached too far for MRB_WITHOUT_FLOAT");
+#endif
+  } else {
+    return mrb_fixnum_value(pos);
+  }
+}
+
+mrb_value
+mrb_io_syswrite(mrb_state *mrb, mrb_value io)
+{
+  struct mrb_io *fptr;
+  mrb_value str, buf;
+  int fd, length;
+
+  fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type);
+  if (! fptr->writable) {
+    mrb_raise(mrb, E_IO_ERROR, "not opened for writing");
+  }
+
+  mrb_get_args(mrb, "S", &str);
+  if (mrb_type(str) != MRB_TT_STRING) {
+    buf = mrb_funcall(mrb, str, "to_s", 0);
+  } else {
+    buf = str;
+  }
+
+  if (fptr->fd2 == -1) {
+    fd = fptr->fd;
+  } else {
+    fd = fptr->fd2;
+  }
+  length = write(fd, RSTRING_PTR(buf), (fsize_t)RSTRING_LEN(buf));
+  if (length == -1) {
+    mrb_sys_fail(mrb, 0);
+  }
+
+  return mrb_fixnum_value(length);
+}
+
+mrb_value
+mrb_io_close(mrb_state *mrb, mrb_value self)
+{
+  struct mrb_io *fptr;
+  fptr = io_get_open_fptr(mrb, self);
+  fptr_finalize(mrb, fptr, FALSE);
+  return mrb_nil_value();
+}
+
+mrb_value
+mrb_io_close_write(mrb_state *mrb, mrb_value self)
+{
+  struct mrb_io *fptr;
+  fptr = io_get_open_fptr(mrb, self);
+  if (close((int)fptr->fd2) == -1) {
+    mrb_sys_fail(mrb, "close");
+  }
+  return mrb_nil_value();
+}
+
+mrb_value
+mrb_io_closed(mrb_state *mrb, mrb_value io)
+{
+  struct mrb_io *fptr;
+  fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type);
+  if (fptr->fd >= 0) {
+    return mrb_false_value();
+  }
+
+  return mrb_true_value();
+}
+
+mrb_value
+mrb_io_pid(mrb_state *mrb, mrb_value io)
+{
+  struct mrb_io *fptr;
+  fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type);
+
+  if (fptr->pid > 0) {
+    return mrb_fixnum_value(fptr->pid);
+  }
+
+  return mrb_nil_value();
+}
+
+static struct timeval
+time2timeval(mrb_state *mrb, mrb_value time)
+{
+  struct timeval t = { 0, 0 };
+
+  switch (mrb_type(time)) {
+    case MRB_TT_FIXNUM:
+      t.tv_sec = (ftime_t)mrb_fixnum(time);
+      t.tv_usec = 0;
+      break;
+
+#ifndef MRB_WITHOUT_FLOAT
+    case MRB_TT_FLOAT:
+      t.tv_sec = (ftime_t)mrb_float(time);
+      t.tv_usec = (fsuseconds_t)((mrb_float(time) - t.tv_sec) * 1000000.0);
+      break;
+#endif
+
+    default:
+      mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
+  }
+
+  return t;
+}
+
+static int
+mrb_io_read_data_pending(mrb_state *mrb, mrb_value io)
+{
+  mrb_value buf = mrb_iv_get(mrb, io, mrb_intern_cstr(mrb, "@buf"));
+  if (mrb_type(buf) == MRB_TT_STRING && RSTRING_LEN(buf) > 0) {
+    return 1;
+  }
+  return 0;
+}
+
+#ifndef _WIN32
+static mrb_value
+mrb_io_s_pipe(mrb_state *mrb, mrb_value klass)
+{
+  mrb_value r = mrb_nil_value();
+  mrb_value w = mrb_nil_value();
+  struct mrb_io *fptr_r;
+  struct mrb_io *fptr_w;
+  int pipes[2];
+
+  if (mrb_pipe(mrb, pipes) == -1) {
+    mrb_sys_fail(mrb, "pipe");
+  }
+
+  r = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
+  mrb_iv_set(mrb, r, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
+  fptr_r = mrb_io_alloc(mrb);
+  fptr_r->fd = pipes[0];
+  fptr_r->readable = 1;
+  fptr_r->writable = 0;
+  fptr_r->sync = 0;
+  DATA_TYPE(r) = &mrb_io_type;
+  DATA_PTR(r)  = fptr_r;
+
+  w = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
+  mrb_iv_set(mrb, w, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
+  fptr_w = mrb_io_alloc(mrb);
+  fptr_w->fd = pipes[1];
+  fptr_w->readable = 0;
+  fptr_w->writable = 1;
+  fptr_w->sync = 1;
+  DATA_TYPE(w) = &mrb_io_type;
+  DATA_PTR(w)  = fptr_w;
+
+  return mrb_assoc_new(mrb, r, w);
+}
+#endif
+
+static mrb_value
+mrb_io_s_select(mrb_state *mrb, mrb_value klass)
+{
+  mrb_value *argv;
+  mrb_int argc;
+  mrb_value read, read_io, write, except, timeout, list;
+  struct timeval *tp, timerec;
+  fd_set pset, rset, wset, eset;
+  fd_set *rp, *wp, *ep;
+  struct mrb_io *fptr;
+  int pending = 0;
+  mrb_value result;
+  int max = 0;
+  int interrupt_flag = 0;
+  int i, n;
+
+  mrb_get_args(mrb, "*", &argv, &argc);
+
+  if (argc < 1 || argc > 4) {
+    mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1..4)", mrb_fixnum_value(argc));
+  }
+
+  timeout = mrb_nil_value();
+  except = mrb_nil_value();
+  write = mrb_nil_value();
+  if (argc > 3)
+    timeout = argv[3];
+  if (argc > 2)
+    except = argv[2];
+  if (argc > 1)
+    write = argv[1];
+  read = argv[0];
+
+  if (mrb_nil_p(timeout)) {
+    tp = NULL;
+  } else {
+    timerec = time2timeval(mrb, timeout);
+    tp = &timerec;
+  }
+
+  FD_ZERO(&pset);
+  if (!mrb_nil_p(read)) {
+    mrb_check_type(mrb, read, MRB_TT_ARRAY);
+    rp = &rset;
+    FD_ZERO(rp);
+    for (i = 0; i < RARRAY_LEN(read); i++) {
+      read_io = RARRAY_PTR(read)[i];
+      fptr = (struct mrb_io *)mrb_get_datatype(mrb, read_io, &mrb_io_type);
+      FD_SET(fptr->fd, rp);
+      if (mrb_io_read_data_pending(mrb, read_io)) {
+        pending++;
+        FD_SET(fptr->fd, &pset);
+      }
+      if (max < fptr->fd)
+        max = fptr->fd;
+    }
+    if (pending) {
+      timerec.tv_sec = timerec.tv_usec = 0;
+      tp = &timerec;
+    }
+  } else {
+    rp = NULL;
+  }
+
+  if (!mrb_nil_p(write)) {
+    mrb_check_type(mrb, write, MRB_TT_ARRAY);
+    wp = &wset;
+    FD_ZERO(wp);
+    for (i = 0; i < RARRAY_LEN(write); i++) {
+      fptr = (struct mrb_io *)mrb_get_datatype(mrb, RARRAY_PTR(write)[i], &mrb_io_type);
+      FD_SET(fptr->fd, wp);
+      if (max < fptr->fd)
+        max = fptr->fd;
+      if (fptr->fd2 >= 0) {
+        FD_SET(fptr->fd2, wp);
+        if (max < fptr->fd2)
+          max = fptr->fd2;
+      }
+    }
+  } else {
+    wp = NULL;
+  }
+
+  if (!mrb_nil_p(except)) {
+    mrb_check_type(mrb, except, MRB_TT_ARRAY);
+    ep = &eset;
+    FD_ZERO(ep);
+    for (i = 0; i < RARRAY_LEN(except); i++) {
+      fptr = (struct mrb_io *)mrb_get_datatype(mrb, RARRAY_PTR(except)[i], &mrb_io_type);
+      FD_SET(fptr->fd, ep);
+      if (max < fptr->fd)
+        max = fptr->fd;
+      if (fptr->fd2 >= 0) {
+        FD_SET(fptr->fd2, ep);
+        if (max < fptr->fd2)
+          max = fptr->fd2;
+      }
+    }
+  } else {
+    ep = NULL;
+  }
+
+  max++;
+
+retry:
+  n = select(max, rp, wp, ep, tp);
+  if (n < 0) {
+    if (errno != EINTR)
+      mrb_sys_fail(mrb, "select failed");
+    if (tp == NULL)
+      goto retry;
+    interrupt_flag = 1;
+  }
+
+  if (!pending && n == 0)
+    return mrb_nil_value();
+
+  result = mrb_ary_new_capa(mrb, 3);
+  mrb_ary_push(mrb, result, rp? mrb_ary_new(mrb) : mrb_ary_new_capa(mrb, 0));
+  mrb_ary_push(mrb, result, wp? mrb_ary_new(mrb) : mrb_ary_new_capa(mrb, 0));
+  mrb_ary_push(mrb, result, ep? mrb_ary_new(mrb) : mrb_ary_new_capa(mrb, 0));
+
+  if (interrupt_flag == 0) {
+    if (rp) {
+      list = RARRAY_PTR(result)[0];
+      for (i = 0; i < RARRAY_LEN(read); i++) {
+        fptr = (struct mrb_io *)mrb_get_datatype(mrb, RARRAY_PTR(read)[i], &mrb_io_type);
+        if (FD_ISSET(fptr->fd, rp) ||
+            FD_ISSET(fptr->fd, &pset)) {
+          mrb_ary_push(mrb, list, RARRAY_PTR(read)[i]);
+        }
+      }
+    }
+
+    if (wp) {
+      list = RARRAY_PTR(result)[1];
+      for (i = 0; i < RARRAY_LEN(write); i++) {
+        fptr = (struct mrb_io *)mrb_get_datatype(mrb, RARRAY_PTR(write)[i], &mrb_io_type);
+        if (FD_ISSET(fptr->fd, wp)) {
+          mrb_ary_push(mrb, list, RARRAY_PTR(write)[i]);
+        } else if (fptr->fd2 >= 0 && FD_ISSET(fptr->fd2, wp)) {
+          mrb_ary_push(mrb, list, RARRAY_PTR(write)[i]);
+        }
+      }
+    }
+
+    if (ep) {
+      list = RARRAY_PTR(result)[2];
+      for (i = 0; i < RARRAY_LEN(except); i++) {
+        fptr = (struct mrb_io *)mrb_get_datatype(mrb, RARRAY_PTR(except)[i], &mrb_io_type);
+        if (FD_ISSET(fptr->fd, ep)) {
+          mrb_ary_push(mrb, list, RARRAY_PTR(except)[i]);
+        } else if (fptr->fd2 >= 0 && FD_ISSET(fptr->fd2, ep)) {
+          mrb_ary_push(mrb, list, RARRAY_PTR(except)[i]);
+        }
+      }
+    }
+  }
+
+  return result;
+}
+
+mrb_value
+mrb_io_fileno(mrb_state *mrb, mrb_value io)
+{
+  struct mrb_io *fptr;
+  fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type);
+  return mrb_fixnum_value(fptr->fd);
+}
+
+mrb_value
+mrb_io_close_on_exec_p(mrb_state *mrb, mrb_value self)
+{
+#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
+  struct mrb_io *fptr;
+  int ret;
+
+  fptr = io_get_open_fptr(mrb, self);
+
+  if (fptr->fd2 >= 0) {
+    if ((ret = fcntl(fptr->fd2, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed");
+    if (!(ret & FD_CLOEXEC)) return mrb_false_value();
+  }
+
+  if ((ret = fcntl(fptr->fd, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed");
+  if (!(ret & FD_CLOEXEC)) return mrb_false_value();
+  return mrb_true_value();
+
+#else
+  mrb_raise(mrb, E_NOTIMP_ERROR, "IO#close_on_exec? is not supported on the platform");
+  return mrb_false_value();
+#endif
+}
+
+mrb_value
+mrb_io_set_close_on_exec(mrb_state *mrb, mrb_value self)
+{
+#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
+  struct mrb_io *fptr;
+  int flag, ret;
+  mrb_bool b;
+
+  fptr = io_get_open_fptr(mrb, self);
+  mrb_get_args(mrb, "b", &b);
+  flag = b ? FD_CLOEXEC : 0;
+
+  if (fptr->fd2 >= 0) {
+    if ((ret = fcntl(fptr->fd2, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed");
+    if ((ret & FD_CLOEXEC) != flag) {
+      ret = (ret & ~FD_CLOEXEC) | flag;
+      ret = fcntl(fptr->fd2, F_SETFD, ret);
+
+      if (ret == -1) mrb_sys_fail(mrb, "F_SETFD failed");
+    }
+  }
+
+  if ((ret = fcntl(fptr->fd, F_GETFD)) == -1) mrb_sys_fail(mrb, "F_GETFD failed");
+  if ((ret & FD_CLOEXEC) != flag) {
+    ret = (ret & ~FD_CLOEXEC) | flag;
+    ret = fcntl(fptr->fd, F_SETFD, ret);
+    if (ret == -1) mrb_sys_fail(mrb, "F_SETFD failed");
+  }
+
+  return mrb_bool_value(b);
+#else
+  mrb_raise(mrb, E_NOTIMP_ERROR, "IO#close_on_exec= is not supported on the platform");
+  return mrb_nil_value();
+#endif
+}
+
+mrb_value
+mrb_io_set_sync(mrb_state *mrb, mrb_value self)
+{
+  struct mrb_io *fptr;
+  mrb_bool b;
+
+  fptr = io_get_open_fptr(mrb, self);
+  mrb_get_args(mrb, "b", &b);
+  fptr->sync = b;
+  return mrb_bool_value(b);
+}
+
+mrb_value
+mrb_io_sync(mrb_state *mrb, mrb_value self)
+{
+  struct mrb_io *fptr;
+  fptr = io_get_open_fptr(mrb, self);
+  return mrb_bool_value(fptr->sync);
+}
+
+void
+mrb_init_io(mrb_state *mrb)
+{
+  struct RClass *io;
+
+  io      = mrb_define_class(mrb, "IO", mrb->object_class);
+  MRB_SET_INSTANCE_TT(io, MRB_TT_DATA);
+
+  mrb_include_module(mrb, io, mrb_module_get(mrb, "Enumerable")); /* 15.2.20.3 */
+  mrb_define_class_method(mrb, io, "_popen",  mrb_io_s_popen,   MRB_ARGS_ANY());
+  mrb_define_class_method(mrb, io, "_sysclose",  mrb_io_s_sysclose, MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, io, "for_fd",  mrb_io_s_for_fd,   MRB_ARGS_ANY());
+  mrb_define_class_method(mrb, io, "select",  mrb_io_s_select,  MRB_ARGS_ANY());
+  mrb_define_class_method(mrb, io, "sysopen", mrb_io_s_sysopen, MRB_ARGS_ANY());
+#ifndef _WIN32
+  mrb_define_class_method(mrb, io, "_pipe", mrb_io_s_pipe, MRB_ARGS_NONE());
+#endif
+
+  mrb_define_method(mrb, io, "initialize", mrb_io_initialize, MRB_ARGS_ANY());    /* 15.2.20.5.21 (x)*/
+  mrb_define_method(mrb, io, "initialize_copy", mrb_io_initialize_copy, MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, io, "_check_readable", mrb_io_check_readable, MRB_ARGS_NONE());
+  mrb_define_method(mrb, io, "isatty",     mrb_io_isatty,     MRB_ARGS_NONE());
+  mrb_define_method(mrb, io, "sync",       mrb_io_sync,       MRB_ARGS_NONE());
+  mrb_define_method(mrb, io, "sync=",      mrb_io_set_sync,   MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, io, "sysread",    mrb_io_sysread,    MRB_ARGS_ANY());
+  mrb_define_method(mrb, io, "sysseek",    mrb_io_sysseek,    MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, io, "syswrite",   mrb_io_syswrite,   MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, io, "close",      mrb_io_close,      MRB_ARGS_NONE());   /* 15.2.20.5.1 */
+  mrb_define_method(mrb, io, "close_write",    mrb_io_close_write,       MRB_ARGS_NONE());
+  mrb_define_method(mrb, io, "close_on_exec=", mrb_io_set_close_on_exec, MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, io, "close_on_exec?", mrb_io_close_on_exec_p,   MRB_ARGS_NONE());
+  mrb_define_method(mrb, io, "closed?",    mrb_io_closed,     MRB_ARGS_NONE());   /* 15.2.20.5.2 */
+  mrb_define_method(mrb, io, "pid",        mrb_io_pid,        MRB_ARGS_NONE());   /* 15.2.20.5.2 */
+  mrb_define_method(mrb, io, "fileno",     mrb_io_fileno,     MRB_ARGS_NONE());
+
+
+  mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$/"), mrb_str_new_cstr(mrb, "\n"));
+}
diff --git a/third-party/mruby/mrbgems/mruby-io/src/mruby_io_gem.c b/third-party/mruby/mrbgems/mruby-io/src/mruby_io_gem.c
new file mode 100644 (file)
index 0000000..6880e66
--- /dev/null
@@ -0,0 +1,20 @@
+#include "mruby.h"
+
+void mrb_init_io(mrb_state *mrb);
+void mrb_init_file(mrb_state *mrb);
+void mrb_init_file_test(mrb_state *mrb);
+
+#define DONE mrb_gc_arena_restore(mrb, 0)
+
+void
+mrb_mruby_io_gem_init(mrb_state* mrb)
+{
+  mrb_init_io(mrb); DONE;
+  mrb_init_file(mrb); DONE;
+  mrb_init_file_test(mrb); DONE;
+}
+
+void
+mrb_mruby_io_gem_final(mrb_state* mrb)
+{
+}
diff --git a/third-party/mruby/mrbgems/mruby-io/test/file.rb b/third-party/mruby/mrbgems/mruby-io/test/file.rb
new file mode 100644 (file)
index 0000000..dc6fe36
--- /dev/null
@@ -0,0 +1,213 @@
+##
+# IO Test
+
+assert('File', '15.2.21') do
+  File.class == Class
+end
+
+assert('File', '15.2.21.2') do
+  File.superclass == IO
+end
+
+assert('File TEST SETUP') do
+  MRubyIOTestUtil.io_test_setup
+end
+
+assert('File#initialize', '15.2.21.4.1') do
+  io = File.open($mrbtest_io_rfname, "r")
+  assert_nil io.close
+  assert_raise IOError do
+    io.close
+  end
+end
+
+assert('File#path', '15.2.21.4.2') do
+  io = File.open($mrbtest_io_rfname, "r")
+  assert_equal $mrbtest_io_msg, io.read
+  assert_equal $mrbtest_io_rfname, io.path
+  io.close
+  assert_equal $mrbtest_io_rfname, io.path
+  io.closed?
+end
+
+assert('File.basename') do
+  assert_equal '/', File.basename('//')
+  assert_equal 'a', File.basename('/a/')
+  assert_equal 'b', File.basename('/a/b')
+  assert_equal 'b', File.basename('../a/b')
+end
+
+assert('File.dirname') do
+  assert_equal '.',    File.dirname('')
+  assert_equal '.',    File.dirname('a')
+  assert_equal '/',    File.dirname('/a')
+  assert_equal 'a',    File.dirname('a/b')
+  assert_equal '/a',   File.dirname('/a/b')
+end
+
+assert('File.extname') do
+  assert_equal '.txt', File.extname('foo/foo.txt')
+  assert_equal '.gz',  File.extname('foo/foo.tar.gz')
+  assert_equal '', File.extname('foo/bar')
+  assert_equal '', File.extname('foo/.bar')
+  assert_equal '', File.extname('foo.txt/bar')
+  assert_equal '', File.extname('.foo')
+end
+
+assert('File#flock') do
+  f = File.open $mrbtest_io_rfname
+  begin
+    assert_equal(f.flock(File::LOCK_SH), 0)
+    assert_equal(f.flock(File::LOCK_UN), 0)
+    assert_equal(f.flock(File::LOCK_EX | File::LOCK_NB), 0)
+    assert_equal(f.flock(File::LOCK_UN), 0)
+  rescue NotImplementedError => e
+    skip e.message
+  ensure
+    f.close
+  end
+end
+
+assert('File#mtime') do
+  unless Object.const_defined?(:Time)
+    skip "File#mtime require Time"
+  end
+  begin
+    now = Time.now.to_i
+    mt = 0
+    File.open('mtime-test', 'w') do |f|
+      mt = f.mtime.to_i
+    end
+    assert_equal true, mt >= now
+  ensure
+    File.delete('mtime-test')
+  end
+end
+
+assert('File.join') do
+  assert_equal "", File.join()
+  assert_equal "a", File.join("a")
+  assert_equal "/a", File.join("/a")
+  assert_equal "a/", File.join("a/")
+  assert_equal "a/b/c", File.join("a", "b", "c")
+  assert_equal "/a/b/c", File.join("/a", "b", "c")
+  assert_equal "a/b/c/", File.join("a", "b", "c/")
+  assert_equal "a/b/c", File.join("a/", "/b/", "/c")
+  assert_equal "a/b/c", File.join(["a", "b", "c"])
+  assert_equal "a/b/c", File.join("a", ["b", ["c"]])
+end
+
+assert('File.realpath') do
+  if File::ALT_SEPARATOR
+    readme_path = File._getwd + File::ALT_SEPARATOR + "README.md"
+    assert_equal readme_path, File.realpath("README.md")
+  else
+    dir = MRubyIOTestUtil.mkdtemp("mruby-io-test.XXXXXX")
+    begin
+      dir1 = File.realpath($mrbtest_io_rfname)
+      dir2 = File.realpath("./#{dir}//./../#{$mrbtest_io_symlinkname}")
+      assert_equal dir1, dir2
+    ensure
+      MRubyIOTestUtil.rmdir dir
+    end
+  end
+end
+
+assert("File.readlink") do
+  begin
+    assert_equal $mrbtest_io_rfname, File.readlink($mrbtest_io_symlinkname)
+  rescue NotImplementedError => e
+    skip e.message
+  end
+end
+
+assert("File.readlink fails with non-symlink") do
+  skip "readlink is not supported on this platform" if MRubyIOTestUtil.win?
+  begin
+    e2 = nil
+    assert_raise(RuntimeError) {
+      begin
+        File.readlink($mrbtest_io_rfname)
+      rescue => e
+        if Object.const_defined?(:SystemCallError) and e.kind_of?(SystemCallError)
+          raise RuntimeError, "SystemCallError converted to RuntimeError"
+        end
+        raise e
+      rescue NotImplementedError => e
+        e2 = e
+      end
+    }
+    raise e2 if e2
+  rescue NotImplementedError => e
+    skip e.message
+  end
+end
+
+assert('File.expand_path') do
+  assert_equal "/",    File.expand_path("..", "/tmp"),       "parent path with base_dir (1)"
+  assert_equal "/tmp", File.expand_path("..", "/tmp/mruby"), "parent path with base_dir (2)"
+
+  assert_equal "/home", File.expand_path("/home"),      "absolute"
+  assert_equal "/home", File.expand_path("/home", "."), "absolute with base_dir"
+
+  assert_equal "/hoge", File.expand_path("/tmp/..//hoge")
+  assert_equal "/hoge", File.expand_path("////tmp/..///////hoge")
+
+  assert_equal "/", File.expand_path("../../../..", "/")
+  if File._getwd[1] == ":"
+    drive_letter = File._getwd[0]
+    assert_equal drive_letter + ":\\", File.expand_path(([".."] * 100).join("/"))
+  else
+    assert_equal "/", File.expand_path(([".."] * 100).join("/"))
+  end
+end
+
+assert('File.expand_path (with ENV)') do
+  skip unless Object.const_defined?(:ENV) && ENV['HOME']
+
+  assert_equal ENV['HOME'], File.expand_path("~/"),      "home"
+  assert_equal ENV['HOME'], File.expand_path("~/", "/"), "home with base_dir"
+
+  assert_equal "#{ENV['HOME']}/user", File.expand_path("user", ENV['HOME']), "relative with base_dir"
+end
+
+assert('File.path') do
+  assert_equal "", File.path("")
+  assert_equal "a/b/c", File.path("a/b/c")
+  assert_equal "a/../b/./c", File.path("a/../b/./c")
+  assert_raise(TypeError) { File.path(nil) }
+  assert_raise(TypeError) { File.path(123) }
+
+end
+
+assert('File.symlink') do
+  target_name = "/usr/bin"
+  symlink_name = "test-bin-dummy"
+  if !File.exist?(target_name)
+    skip("target directory of File.symlink is not found")
+  else
+    begin
+      assert_equal 0, File.symlink(target_name, symlink_name)
+      begin
+        assert_equal true, File.symlink?(symlink_name)
+      ensure
+        File.delete symlink_name
+      end
+    rescue NotImplementedError => e
+      skip e.message
+    end
+  end
+end
+
+assert('File.chmod') do
+  File.open('chmod-test', 'w') {}
+  begin
+    assert_equal 1, File.chmod(0400, 'chmod-test')
+  ensure
+    File.delete('chmod-test')
+  end
+end
+
+assert('File TEST CLEANUP') do
+  assert_nil MRubyIOTestUtil.io_test_cleanup
+end
diff --git a/third-party/mruby/mrbgems/mruby-io/test/file_test.rb b/third-party/mruby/mrbgems/mruby-io/test/file_test.rb
new file mode 100644 (file)
index 0000000..2c831f0
--- /dev/null
@@ -0,0 +1,117 @@
+##
+# FileTest
+
+assert('FileTest TEST SETUP') do
+  MRubyIOTestUtil.io_test_setup
+end
+
+assert("FileTest.directory?") do
+  dir = MRubyIOTestUtil.mkdtemp("mruby-io-test.XXXXXX")
+  begin
+    assert_true  FileTest.directory?(dir)
+    assert_false FileTest.directory?($mrbtest_io_rfname)
+  ensure
+    MRubyIOTestUtil.rmdir dir
+  end
+end
+
+assert("FileTest.exist?") do
+  assert_equal true,  FileTest.exist?($mrbtest_io_rfname), "filename - exist"
+  assert_equal false, FileTest.exist?($mrbtest_io_rfname + "-"), "filename - not exist"
+  io = IO.new(IO.sysopen($mrbtest_io_rfname))
+  assert_equal true,  FileTest.exist?(io), "io obj - exist"
+  io.close
+  assert_equal true, io.closed?
+  assert_raise IOError do
+    FileTest.exist?(io)
+  end
+end
+
+assert("FileTest.file?") do
+  dir = MRubyIOTestUtil.mkdtemp("mruby-io-test.XXXXXX")
+  begin
+    assert_true  FileTest.file?($mrbtest_io_rfname)
+    assert_false FileTest.file?(dir)
+  ensure
+    MRubyIOTestUtil.rmdir dir
+  end
+end
+
+assert("FileTest.pipe?") do
+  begin
+    assert_equal false, FileTest.pipe?("/tmp")
+    io = IO.popen("ls")
+    assert_equal true,  FileTest.pipe?(io)
+  rescue NotImplementedError => e
+    skip e.message
+  end
+end
+
+assert('FileTest.size') do
+  assert_equal FileTest.size($mrbtest_io_rfname), $mrbtest_io_msg.size
+  assert_equal FileTest.size($mrbtest_io_wfname), 0
+end
+
+assert("FileTest.size?") do
+  assert_equal $mrbtest_io_msg.size, FileTest.size?($mrbtest_io_rfname)
+  assert_equal nil, FileTest.size?($mrbtest_io_wfname)
+  assert_equal nil, FileTest.size?("not-exist-test-target-file")
+
+  fp1 = File.open($mrbtest_io_rfname)
+  fp2 = File.open($mrbtest_io_wfname)
+  assert_equal $mrbtest_io_msg.size,  FileTest.size?(fp1)
+  assert_equal nil, FileTest.size?(fp2)
+  fp1.close
+  fp2.close
+
+  assert_raise IOError do
+    FileTest.size?(fp1)
+  end
+  assert_raise IOError do
+    FileTest.size?(fp2)
+  end
+
+  fp1.closed? && fp2.closed?
+end
+
+assert("FileTest.socket?") do
+  begin
+    assert_true FileTest.socket?($mrbtest_io_socketname)
+  rescue NotImplementedError => e
+    skip e.message
+  end
+end
+
+assert("FileTest.symlink?") do
+  begin
+    assert_true FileTest.symlink?($mrbtest_io_symlinkname)
+  rescue NotImplementedError => e
+    skip e.message
+  end
+end
+
+assert("FileTest.zero?") do
+  assert_equal false, FileTest.zero?($mrbtest_io_rfname)
+  assert_equal true,  FileTest.zero?($mrbtest_io_wfname)
+  assert_equal false, FileTest.zero?("not-exist-test-target-file")
+
+  fp1 = File.open($mrbtest_io_rfname)
+  fp2 = File.open($mrbtest_io_wfname)
+  assert_equal false, FileTest.zero?(fp1)
+  assert_equal true,  FileTest.zero?(fp2)
+  fp1.close
+  fp2.close
+
+  assert_raise IOError do
+    FileTest.zero?(fp1)
+  end
+  assert_raise IOError do
+    FileTest.zero?(fp2)
+  end
+
+  fp1.closed? && fp2.closed?
+end
+
+assert('FileTest TEST CLEANUP') do
+  assert_nil MRubyIOTestUtil.io_test_cleanup
+end
diff --git a/third-party/mruby/mrbgems/mruby-io/test/gc_filedes.sh b/third-party/mruby/mrbgems/mruby-io/test/gc_filedes.sh
new file mode 100644 (file)
index 0000000..6e5d1bb
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+ulimit -n 20
+mruby -e '100.times { File.open "'$0'" }'
diff --git a/third-party/mruby/mrbgems/mruby-io/test/io.rb b/third-party/mruby/mrbgems/mruby-io/test/io.rb
new file mode 100644 (file)
index 0000000..e06b149
--- /dev/null
@@ -0,0 +1,646 @@
+##
+# IO Test
+
+unless Object.respond_to? :assert_nothing_raised
+  def assert_nothing_raised(*exp)
+    ret = true
+    if $mrbtest_assert
+      $mrbtest_assert_idx += 1
+      msg = exp.last.class == String ? exp.pop : ""
+      begin
+        yield
+      rescue Exception => e
+        msg = "#{msg} exception raised."
+        diff = "      Class: <#{e.class}>\n" +
+          "    Message: #{e.message}"
+        $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff])
+        ret = false
+      end
+    end
+    ret
+  end
+end
+
+assert('IO TEST SETUP') do
+  MRubyIOTestUtil.io_test_setup
+  $cr = MRubyIOTestUtil.win? ? 1 : 0  # "\n" include CR or not
+end
+
+assert('IO', '15.2.20') do
+  assert_equal(Class, IO.class)
+end
+
+assert('IO', '15.2.20.2') do
+  assert_equal(Object, IO.superclass)
+end
+
+assert('IO', '15.2.20.3') do
+  assert_include(IO.included_modules, Enumerable)
+end
+
+assert('IO.open', '15.2.20.4.1') do
+  fd = IO.sysopen $mrbtest_io_rfname
+  assert_equal Fixnum, fd.class
+  io = IO.open fd
+  assert_equal IO, io.class
+  assert_equal $mrbtest_io_msg, io.read
+  io.close
+
+  fd = IO.sysopen $mrbtest_io_rfname
+  IO.open(fd) do |io|
+    assert_equal $mrbtest_io_msg, io.read
+  end
+
+  true
+end
+
+assert('IO#close', '15.2.20.5.1') do
+  io = IO.new(IO.sysopen($mrbtest_io_rfname))
+  assert_nil io.close
+end
+
+assert('IO#closed?', '15.2.20.5.2') do
+  io = IO.new(IO.sysopen($mrbtest_io_rfname))
+  assert_false io.closed?
+  io.close
+  assert_true io.closed?
+end
+
+#assert('IO#each', '15.2.20.5.3') do
+#assert('IO#each_byte', '15.2.20.5.4') do
+#assert('IO#each_line', '15.2.20.5.5') do
+
+assert('IO#eof?', '15.2.20.5.6') do
+  io = IO.new(IO.sysopen($mrbtest_io_wfname, 'w'), 'w')
+  assert_raise(IOError) do
+    io.eof?
+  end
+  io.close
+
+  # empty file
+  io = IO.open(IO.sysopen($mrbtest_io_wfname, 'w'), 'w')
+  io.close
+  io = IO.open(IO.sysopen($mrbtest_io_wfname, 'r'), 'r')
+  assert_true io.eof?
+  io.close
+
+  # nonempty file
+  io = IO.new(IO.sysopen($mrbtest_io_rfname))
+  assert_false io.eof?
+  io.readchar
+  assert_false io.eof?
+  io.read
+  assert_true io.eof?
+  io.close
+
+  true
+end
+
+assert('IO#flush', '15.2.20.5.7') do
+  # Note: mruby-io does not have any buffer to be flushed now.
+  io = IO.new(IO.sysopen($mrbtest_io_wfname))
+  assert_equal io, io.flush
+  io.close
+  assert_raise(IOError) do
+    io.flush
+  end
+end
+
+assert('IO#getc', '15.2.20.5.8') do
+  io = IO.new(IO.sysopen($mrbtest_io_rfname))
+  $mrbtest_io_msg.each_char { |ch|
+    assert_equal ch, io.getc
+  }
+  assert_equal nil, io.getc
+  io.close
+  true
+end
+
+#assert('IO#gets', '15.2.20.5.9') do
+#assert('IO#initialize_copy', '15.2.20.5.10') do
+#assert('IO#print', '15.2.20.5.11') do
+#assert('IO#putc', '15.2.20.5.12') do
+#assert('IO#puts', '15.2.20.5.13') do
+
+assert('IO#read', '15.2.20.5.14') do
+  IO.open(IO.sysopen($mrbtest_io_rfname)) do |io|
+    assert_raise(ArgumentError) { io.read(-5) }
+    assert_raise(TypeError) { io.read("str") }
+
+    len = $mrbtest_io_msg.length
+    assert_equal '', io.read(0)
+    assert_equal 'mruby', io.read(5)
+    assert_equal $mrbtest_io_msg[5,len], io.read(len)
+
+    assert_equal "", io.read
+    assert_nil io.read(1)
+  end
+
+  IO.open(IO.sysopen($mrbtest_io_rfname)) do |io|
+    assert_equal $mrbtest_io_msg, io.read
+  end
+end
+
+assert "IO#read(n) with n > IO::BUF_SIZE" do
+  skip "pipe is not supported on this platform" if MRubyIOTestUtil.win?
+  r,w = IO.pipe
+  n = IO::BUF_SIZE+1
+  w.write 'a'*n
+  assert_equal r.read(n), 'a'*n
+end
+
+assert('IO#readchar', '15.2.20.5.15') do
+  # almost same as IO#getc
+  IO.open(IO.sysopen($mrbtest_io_rfname)) do |io|
+    $mrbtest_io_msg.each_char { |ch|
+      assert_equal ch, io.readchar
+    }
+    assert_raise(EOFError) do
+      io.readchar
+    end
+  end
+end
+
+#assert('IO#readline', '15.2.20.5.16') do
+#assert('IO#readlines', '15.2.20.5.17') do
+
+assert('IO#sync', '15.2.20.5.18') do
+  io = IO.new(IO.sysopen($mrbtest_io_rfname))
+  s = io.sync
+  assert_true(s == true || s == false)
+  io.close
+  assert_raise(IOError) do
+    io.sync
+  end
+end
+
+assert('IO#sync=', '15.2.20.5.19') do
+  io = IO.new(IO.sysopen($mrbtest_io_rfname))
+  io.sync = true
+  assert_true io.sync
+  io.sync = false
+  assert_false io.sync
+  io.close
+  assert_raise(IOError) do
+    io.sync = true
+  end
+end
+
+assert('IO#write', '15.2.20.5.20') do
+  io = IO.open(IO.sysopen($mrbtest_io_wfname))
+  assert_equal 0, io.write("")
+  io.close
+
+  io = IO.open(IO.sysopen($mrbtest_io_wfname, "r+"), "r+")
+  assert_equal 7, io.write("abcdefg")
+  io.rewind
+  assert_equal "ab", io.read(2)
+  assert_equal 3, io.write("123")
+  io.rewind
+  assert_equal "ab123fg", io.read
+  io.close
+
+  true
+end
+
+assert('IO#<<') do
+  io = IO.open(IO.sysopen($mrbtest_io_wfname))
+  io << "" << ""
+  assert_equal 0, io.pos
+  io.close
+  true
+end
+
+assert('IO#dup for readable') do
+  io = IO.new(IO.sysopen($mrbtest_io_rfname))
+  dup = io.dup
+  assert_true io != dup
+  assert_true io.fileno != dup.fileno
+  begin
+    assert_true dup.close_on_exec?
+  rescue NotImplementedError
+  end
+  assert_equal 'm', dup.sysread(1)
+  assert_equal 'r', io.sysread(1)
+  assert_equal 'u', dup.sysread(1)
+  assert_equal 'b', io.sysread(1)
+  assert_equal 'y', dup.sysread(1)
+  dup.close
+  assert_false io.closed?
+  io.close
+  true
+end
+
+assert('IO#dup for writable') do
+  io = IO.open(IO.sysopen($mrbtest_io_wfname, 'w+'), 'w+')
+  dup = io.dup
+  io.syswrite "mruby"
+  assert_equal 5, dup.sysseek(0, IO::SEEK_CUR)
+  io.sysseek 0, IO::SEEK_SET
+  assert_equal 0, dup.sysseek(0, IO::SEEK_CUR)
+  assert_equal "mruby", dup.sysread(5)
+  dup.close
+  io.close
+  true
+end
+
+assert('IO.for_fd') do
+  fd = IO.sysopen($mrbtest_io_rfname)
+  io = IO.for_fd(fd)
+    assert_equal $mrbtest_io_msg, io.read
+  io.close
+  true
+end
+
+assert('IO.new') do
+  io = IO.new(0)
+  io.close
+  true
+end
+
+assert('IO gc check') do
+  100.times { IO.new(0) }
+end
+
+assert('IO.sysopen("./nonexistent")') do
+  if Object.const_defined? :Errno
+    eclass = Errno::ENOENT
+  else
+    eclass = RuntimeError
+  end
+  assert_raise eclass do
+    fd = IO.sysopen "./nonexistent"
+    IO._sysclose fd
+  end
+end
+
+assert('IO.sysopen, IO#sysread') do
+  fd = IO.sysopen $mrbtest_io_rfname
+  io = IO.new fd
+  str1 = "     "
+  str2 = io.sysread(5, str1)
+  assert_equal $mrbtest_io_msg[0,5], str1
+  assert_equal $mrbtest_io_msg[0,5], str2
+  assert_raise EOFError do
+    io.sysread(10000)
+    io.sysread(10000)
+  end
+
+  assert_raise RuntimeError do
+    io.sysread(5, "abcde".freeze)
+  end
+
+  io.close
+  assert_equal "", io.sysread(0)
+  assert_raise(IOError) { io.sysread(1) }
+  assert_raise(ArgumentError) { io.sysread(-1) }
+  io.closed?
+
+  fd = IO.sysopen $mrbtest_io_wfname, "w"
+  io = IO.new fd, "w"
+  assert_raise(IOError) { io.sysread(1) }
+  io.close
+  true
+end
+
+assert('IO.sysopen, IO#syswrite') do
+  fd = IO.sysopen $mrbtest_io_wfname, "w"
+  io = IO.new fd, "w"
+  str = "abcdefg"
+  len = io.syswrite(str)
+  assert_equal str.size, len
+  io.close
+
+  io = IO.new(IO.sysopen($mrbtest_io_rfname), "r")
+  assert_raise(IOError) { io.syswrite("a") }
+  io.close
+
+  true
+end
+
+assert('IO#_read_buf') do
+  fd = IO.sysopen $mrbtest_io_rfname
+  io = IO.new fd
+  def io._buf
+    @buf
+  end
+  msg_len = $mrbtest_io_msg.size
+  assert_equal '', io._buf
+  assert_equal $mrbtest_io_msg, io._read_buf
+  assert_equal $mrbtest_io_msg, io._buf
+  assert_equal 'mruby', io.read(5)
+  assert_equal 5, io.pos
+  assert_equal msg_len - 5, io._buf.size
+  assert_equal $mrbtest_io_msg[5,100], io.read
+  assert_equal 0, io._buf.size
+  assert_raise EOFError do
+    io._read_buf
+  end
+  assert_equal true, io.eof
+  assert_equal true, io.eof?
+  io.close
+  io.closed?
+end
+
+assert('IO#isatty') do
+  skip "isatty is not supported on this platform" if MRubyIOTestUtil.win?
+  f1 = File.open("/dev/tty")
+  f2 = File.open($mrbtest_io_rfname)
+
+  assert_true  f1.isatty
+  assert_false f2.isatty
+
+  f1.close
+  f2.close
+  true
+end
+
+assert('IO#pos=, IO#seek') do
+  fd = IO.sysopen $mrbtest_io_rfname
+  io = IO.new fd
+  def io._buf
+    @buf
+  end
+  assert_equal 'm', io.getc
+  assert_equal 1, io.pos
+  assert_equal 0, io.seek(0)
+  assert_equal 0, io.pos
+  io.close
+  io.closed?
+end
+
+assert('IO#rewind') do
+  fd = IO.sysopen $mrbtest_io_rfname
+  io = IO.new fd
+  assert_equal 'm', io.getc
+  assert_equal 1, io.pos
+  assert_equal 0, io.rewind
+  assert_equal 0, io.pos
+  io.close
+  io.closed?
+end
+
+assert('IO#gets') do
+  fd = IO.sysopen $mrbtest_io_rfname
+  io = IO.new fd
+
+  # gets without arguments
+  assert_equal $mrbtest_io_msg, io.gets, "gets without arguments"
+  assert_equal nil, io.gets, "gets returns nil, when EOF"
+
+  # gets with limit
+  io.pos = 0
+  assert_equal $mrbtest_io_msg[0, 5], io.gets(5), "gets with limit"
+
+  # gets with rs
+  io.pos = 0
+  assert_equal $mrbtest_io_msg[0, 6], io.gets(' '), "gets with rs"
+
+  # gets with rs, limit
+  io.pos = 0
+  assert_equal $mrbtest_io_msg[0, 5], io.gets(' ', 5), "gets with rs, limit"
+  io.close
+  assert_equal true, io.closed?, "close success"
+
+  # reading many-lines file.
+  fd = IO.sysopen $mrbtest_io_wfname, "w"
+  io = IO.new fd, "w"
+  io.write "0123456789" * 2 + "\na"
+  assert_equal 22 + $cr, io.pos
+  io.close
+  assert_equal true, io.closed?
+
+  fd = IO.sysopen $mrbtest_io_wfname
+  io = IO.new fd
+  line = io.gets
+
+  # gets first line
+  assert_equal "0123456789" * 2 + "\n", line, "gets first line"
+  assert_equal 21, line.size
+  assert_equal 21 + $cr, io.pos
+
+  # gets second line
+  assert_equal "a", io.gets, "gets second line"
+
+  # gets third line
+  assert_equal nil, io.gets, "gets third line; returns nil"
+
+  io.close
+  io.closed?
+end
+
+assert('IO#gets - paragraph mode') do
+  fd = IO.sysopen $mrbtest_io_wfname, "w"
+  io = IO.new fd, "w"
+  io.write "0" * 10 + "\n"
+  io.write "1" * 10 + "\n\n"
+  io.write "2" * 10 + "\n"
+  assert_equal 34 + $cr * 4, io.pos
+  io.close
+  assert_equal true, io.closed?
+
+  fd = IO.sysopen $mrbtest_io_wfname
+  io = IO.new fd
+  para1 = "#{'0' * 10}\n#{'1' * 10}\n\n"
+  text1 = io.gets("")
+  assert_equal para1, text1
+  para2 = "#{'2' * 10}\n"
+  text2 = io.gets("")
+  assert_equal para2, text2
+  io.close
+  io.closed?
+end
+
+assert('IO.popen') do
+  begin
+    $? = nil
+    io = IO.popen("echo mruby-io")
+    assert_true io.close_on_exec?
+    assert_equal Fixnum, io.pid.class
+
+    out = io.read
+    assert_equal out.class, String
+    assert_include out, 'mruby-io'
+
+    io.close
+    if Object.const_defined? :Process
+      assert_true $?.success?
+    else
+      assert_equal 0, $?
+    end
+
+    assert_true io.closed?
+  rescue NotImplementedError => e
+    skip e.message
+  end
+end
+
+assert('IO.popen with in option') do
+  begin
+    IO.pipe do |r, w|
+      w.write 'hello'
+      w.close
+      assert_equal "hello", IO.popen("cat", "r", in: r) { |i| i.read }
+      assert_equal "", r.read
+    end
+    assert_raise(ArgumentError) { IO.popen("hello", "r", in: Object.new) }
+  rescue NotImplementedError => e
+    skip e.message
+  end
+end
+
+assert('IO.popen with out option') do
+  begin
+    IO.pipe do |r, w|
+      IO.popen("echo 'hello'", "w", out: w) {}
+      w.close
+      assert_equal "hello\n", r.read
+    end
+  rescue NotImplementedError => e
+    skip e.message
+  end
+end
+
+assert('IO.popen with err option') do
+  begin
+    IO.pipe do |r, w|
+      assert_equal "", IO.popen("echo 'hello' 1>&2", "r", err: w) { |i| i.read }
+      w.close
+      assert_equal "hello\n", r.read
+    end
+  rescue NotImplementedError => e
+    skip e.message
+  end
+end
+
+assert('IO.read') do
+  # empty file
+  fd = IO.sysopen $mrbtest_io_wfname, "w"
+  io = IO.new fd, "w"
+  io.close
+  assert_equal "",  IO.read($mrbtest_io_wfname)
+  assert_equal nil, IO.read($mrbtest_io_wfname, 1)
+
+  # one byte file
+  fd = IO.sysopen $mrbtest_io_wfname, "w"
+  io = IO.new fd, "w"
+  io.write "123"
+  io.close
+  assert_equal "123", IO.read($mrbtest_io_wfname)
+  assert_equal "",    IO.read($mrbtest_io_wfname, 0)
+  assert_equal "1",   IO.read($mrbtest_io_wfname, 1)
+  assert_equal "",    IO.read($mrbtest_io_wfname, 0, 10)
+  assert_equal "23",  IO.read($mrbtest_io_wfname, 2, 1)
+  assert_equal "23",  IO.read($mrbtest_io_wfname, 10, 1)
+  assert_equal "",    IO.read($mrbtest_io_wfname, nil, 10)
+  assert_equal nil,   IO.read($mrbtest_io_wfname, 1, 10)
+end
+
+assert('IO#fileno') do
+  fd = IO.sysopen $mrbtest_io_rfname
+  io = IO.new fd
+  assert_equal io.fileno, fd
+  assert_equal io.to_i, fd
+  io.close
+  io.closed?
+end
+
+assert('IO#close_on_exec') do
+  fd = IO.sysopen $mrbtest_io_wfname, "w"
+  io = IO.new fd, "w"
+  begin
+    # IO.sysopen opens a file descripter with O_CLOEXEC flag.
+    assert_true io.close_on_exec?
+  rescue ScriptError
+    io.close
+    skip "IO\#close_on_exec is not implemented."
+  end
+
+  io.close_on_exec = false
+  assert_equal(false, io.close_on_exec?)
+  io.close_on_exec = true
+  assert_equal(true, io.close_on_exec?)
+  io.close_on_exec = false
+  assert_equal(false, io.close_on_exec?)
+
+  io.close
+  io.closed?
+
+  begin
+    r, w = IO.pipe
+    assert_equal(true, r.close_on_exec?)
+    r.close_on_exec = false
+    assert_equal(false, r.close_on_exec?)
+    r.close_on_exec = true
+    assert_equal(true, r.close_on_exec?)
+
+    assert_equal(true, w.close_on_exec?)
+    w.close_on_exec = false
+    assert_equal(false, w.close_on_exec?)
+    w.close_on_exec = true
+    assert_equal(true, w.close_on_exec?)
+  ensure
+    r.close unless r.closed?
+    w.close unless w.closed?
+  end
+end
+
+assert('IO#sysseek') do
+  IO.open(IO.sysopen($mrbtest_io_rfname)) do |io|
+    assert_equal 2, io.sysseek(2)
+    assert_equal 5, io.sysseek(3, IO::SEEK_CUR) # 2 + 3 => 5
+    assert_equal $mrbtest_io_msg.size - 4, io.sysseek(-4, IO::SEEK_END)
+  end
+end
+
+assert('IO.pipe') do
+  begin
+    called = false
+    IO.pipe do |r, w|
+      assert_true r.kind_of?(IO)
+      assert_true w.kind_of?(IO)
+      assert_false r.closed?
+      assert_false w.closed?
+      assert_true FileTest.pipe?(r)
+      assert_true FileTest.pipe?(w)
+      assert_nil r.pid
+      assert_nil w.pid
+      assert_true 2 < r.fileno
+      assert_true 2 < w.fileno
+      assert_true r.fileno != w.fileno
+      assert_false r.sync
+      assert_true w.sync
+      assert_equal 8, w.write('test for')
+      assert_equal 'test', r.read(4)
+      assert_equal ' for', r.read(4)
+      assert_equal 5, w.write(' pipe')
+      assert_equal nil, w.close
+      assert_equal ' pipe', r.read
+      called = true
+      assert_raise(IOError) { r.write 'test' }
+      # TODO:
+      # This assert expect raise IOError but got RuntimeError
+      # Because mruby-io not have flag for I/O readable
+      # assert_raise(IOError) { w.read }
+    end
+    assert_true called
+
+    assert_nothing_raised do
+      IO.pipe { |r, w| r.close; w.close }
+    end
+  rescue NotImplementedError => e
+    skip e.message
+  end
+end
+
+assert('`cmd`') do
+  begin
+    assert_equal `echo foo`, "foo\n"
+  rescue NotImplementedError => e
+    skip e.message
+  end
+end
+
+assert('IO TEST CLEANUP') do
+  assert_nil MRubyIOTestUtil.io_test_cleanup
+end
diff --git a/third-party/mruby/mrbgems/mruby-io/test/mruby_io_test.c b/third-party/mruby/mrbgems/mruby-io/test/mruby_io_test.c
new file mode 100644 (file)
index 0000000..71239a8
--- /dev/null
@@ -0,0 +1,256 @@
+#include <sys/types.h>
+#include <errno.h>
+
+#if defined(_WIN32) || defined(_WIN64)
+
+#include <winsock.h>
+#include <io.h>
+#include <fcntl.h>
+#include <direct.h>
+#include <string.h>
+#include <stdlib.h>
+#include <malloc.h>
+
+#if (!defined __MINGW64__) && (!defined __MINGW32__)
+typedef int mode_t;
+#endif
+
+#define open _open
+#define close _close
+
+#ifdef _MSC_VER
+#include <sys/stat.h>
+
+static int
+mkstemp(char *p)
+{
+  int fd;
+  char* fname = _mktemp(p);
+  if (fname == NULL)
+    return -1;
+  fd = open(fname, O_RDWR | O_CREAT | O_EXCL, _S_IREAD | _S_IWRITE);
+  if (fd >= 0)
+    return fd;
+  return -1;
+}
+#endif
+
+static char*
+mkdtemp(char *temp)
+{
+  char *path = _mktemp(temp);
+  if (path[0] == 0) return NULL;
+  if (_mkdir(path) < 0) return NULL;
+  return path;
+}
+
+#define umask(mode) _umask(mode)
+#define rmdir(path) _rmdir(path)
+#else
+  #include <sys/socket.h>
+  #include <unistd.h>
+  #include <sys/un.h>
+#endif
+
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "mruby.h"
+#include "mruby/array.h"
+#include "mruby/error.h"
+#include "mruby/string.h"
+#include "mruby/variable.h"
+
+static mrb_value
+mrb_io_test_io_setup(mrb_state *mrb, mrb_value self)
+{
+  char rfname[]      = "tmp.mruby-io-test-r.XXXXXXXX";
+  char wfname[]      = "tmp.mruby-io-test-w.XXXXXXXX";
+  char symlinkname[] = "tmp.mruby-io-test-l.XXXXXXXX";
+  char socketname[]  = "tmp.mruby-io-test-s.XXXXXXXX";
+  char msg[] = "mruby io test\n";
+  mode_t mask;
+  int fd0, fd1;
+  FILE *fp;
+
+#if !defined(_WIN32) && !defined(_WIN64)
+  int fd2, fd3;
+  struct sockaddr_un sun0;
+#endif
+
+  mask = umask(077);
+  fd0 = mkstemp(rfname);
+  fd1 = mkstemp(wfname);
+  if (fd0 == -1 || fd1 == -1) {
+    mrb_raise(mrb, E_RUNTIME_ERROR, "can't create temporary file");
+    return mrb_nil_value();
+  }
+  close(fd0);
+  close(fd1);
+
+#if !defined(_WIN32) && !defined(_WIN64)
+  fd2 = mkstemp(symlinkname);
+  fd3 = mkstemp(socketname);
+  if (fd2 == -1 || fd3 == -1) {
+    mrb_raise(mrb, E_RUNTIME_ERROR, "can't create temporary file");
+    return mrb_nil_value();
+  }
+#endif
+  umask(mask);
+
+  mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_rfname"), mrb_str_new_cstr(mrb, rfname));
+  mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_wfname"), mrb_str_new_cstr(mrb, wfname));
+  mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_symlinkname"), mrb_str_new_cstr(mrb, symlinkname));
+  mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_socketname"), mrb_str_new_cstr(mrb, socketname));
+  mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_msg"), mrb_str_new_cstr(mrb, msg));
+
+  fp = fopen(rfname, "wb");
+  if (fp == NULL) {
+    mrb_raise(mrb, E_RUNTIME_ERROR, "can't open temporary file");
+    return mrb_nil_value();
+  }
+  fputs(msg, fp);
+  fclose(fp);
+
+  fp = fopen(wfname, "wb");
+  if (fp == NULL) {
+    mrb_raise(mrb, E_RUNTIME_ERROR, "can't open temporary file");
+    return mrb_nil_value();
+  }
+  fclose(fp);
+
+#if !defined(_WIN32) && !defined(_WIN64)
+  unlink(symlinkname);
+  close(fd2);
+  if (symlink(rfname, symlinkname) == -1) {
+    mrb_raise(mrb, E_RUNTIME_ERROR, "can't make a symbolic link");
+  }
+
+  unlink(socketname);
+  close(fd3);
+  fd3 = socket(AF_UNIX, SOCK_STREAM, 0);
+  if (fd3 == -1) {
+    mrb_raise(mrb, E_RUNTIME_ERROR, "can't make a socket");
+  }
+  sun0.sun_family = AF_UNIX;
+  snprintf(sun0.sun_path, sizeof(sun0.sun_path), "%s", socketname);
+  if (bind(fd3, (struct sockaddr *)&sun0, sizeof(sun0)) == -1) {
+    mrb_raisef(mrb, E_RUNTIME_ERROR, "can't bind AF_UNIX socket to %S: %S",
+               mrb_str_new_cstr(mrb, sun0.sun_path),
+               mrb_fixnum_value(errno));
+  }
+  close(fd3);
+#endif
+
+  return mrb_true_value();
+}
+
+static mrb_value
+mrb_io_test_io_cleanup(mrb_state *mrb, mrb_value self)
+{
+  mrb_value rfname = mrb_gv_get(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_rfname"));
+  mrb_value wfname = mrb_gv_get(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_wfname"));
+  mrb_value symlinkname = mrb_gv_get(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_symlinkname"));
+  mrb_value socketname = mrb_gv_get(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_socketname"));
+
+  if (mrb_type(rfname) == MRB_TT_STRING) {
+    remove(RSTRING_PTR(rfname));
+  }
+  if (mrb_type(wfname) == MRB_TT_STRING) {
+    remove(RSTRING_PTR(wfname));
+  }
+  if (mrb_type(symlinkname) == MRB_TT_STRING) {
+    remove(RSTRING_PTR(symlinkname));
+  }
+  if (mrb_type(socketname) == MRB_TT_STRING) {
+    remove(RSTRING_PTR(socketname));
+  }
+
+  mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_rfname"), mrb_nil_value());
+  mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_wfname"), mrb_nil_value());
+  mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_symlinkname"), mrb_nil_value());
+  mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_socketname"), mrb_nil_value());
+  mrb_gv_set(mrb, mrb_intern_cstr(mrb, "$mrbtest_io_msg"), mrb_nil_value());
+
+  return mrb_nil_value();
+}
+
+static mrb_value
+mrb_io_test_file_setup(mrb_state *mrb, mrb_value self)
+{
+  mrb_value ary = mrb_io_test_io_setup(mrb, self);
+#if !defined(_WIN32) && !defined(_WIN64)
+  if (symlink("/usr/bin", "test-bin") == -1) {
+    mrb_raise(mrb, E_RUNTIME_ERROR, "can't make a symbolic link");
+  }
+#endif
+
+  return ary;
+}
+
+static mrb_value
+mrb_io_test_file_cleanup(mrb_state *mrb, mrb_value self)
+{
+  mrb_io_test_io_cleanup(mrb, self);
+  remove("test-bin");
+
+  return mrb_nil_value();
+}
+
+static mrb_value
+mrb_io_test_mkdtemp(mrb_state *mrb, mrb_value klass)
+{
+  mrb_value str;
+  char *cp;
+
+  mrb_get_args(mrb, "S", &str);
+  cp = mrb_str_to_cstr(mrb, str);
+  if (mkdtemp(cp) == NULL) {
+    mrb_sys_fail(mrb, "mkdtemp");
+  }
+  return mrb_str_new_cstr(mrb, cp);
+}
+
+static mrb_value
+mrb_io_test_rmdir(mrb_state *mrb, mrb_value klass)
+{
+  mrb_value str;
+  char *cp;
+
+  mrb_get_args(mrb, "S", &str);
+  cp = mrb_str_to_cstr(mrb, str);
+  if (rmdir(cp) == -1) {
+    mrb_sys_fail(mrb, "rmdir");
+  }
+  return mrb_true_value();
+}
+
+mrb_value
+mrb_io_win_p(mrb_state *mrb, mrb_value klass)
+{
+#if defined(_WIN32) || defined(_WIN64)
+# if defined(__CYGWIN__) || defined(__CYGWIN32__)
+  return mrb_false_value();
+# else
+  return mrb_true_value();
+# endif
+#else
+  return mrb_false_value();
+#endif
+}
+
+void
+mrb_mruby_io_gem_test(mrb_state* mrb)
+{
+  struct RClass *io_test = mrb_define_module(mrb, "MRubyIOTestUtil");
+  mrb_define_class_method(mrb, io_test, "io_test_setup", mrb_io_test_io_setup, MRB_ARGS_NONE());
+  mrb_define_class_method(mrb, io_test, "io_test_cleanup", mrb_io_test_io_cleanup, MRB_ARGS_NONE());
+
+  mrb_define_class_method(mrb, io_test, "file_test_setup", mrb_io_test_file_setup, MRB_ARGS_NONE());
+  mrb_define_class_method(mrb, io_test, "file_test_cleanup", mrb_io_test_file_cleanup, MRB_ARGS_NONE());
+
+  mrb_define_class_method(mrb, io_test, "mkdtemp", mrb_io_test_mkdtemp, MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, io_test, "rmdir", mrb_io_test_rmdir, MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, io_test, "win?", mrb_io_win_p, MRB_ARGS_NONE());
+}
diff --git a/third-party/mruby/mrbgems/mruby-kernel-ext/mrblib/kernel.rb b/third-party/mruby/mrbgems/mruby-kernel-ext/mrblib/kernel.rb
new file mode 100644 (file)
index 0000000..25a4d4e
--- /dev/null
@@ -0,0 +1,13 @@
+module Kernel
+  # call-seq:
+  #   obj.yield_self {|_obj|...} -> an_object
+  #
+  # Yields <i>obj</i> and returns the result.
+  #
+  #   'my string'.yield_self {|s|s.upcase} #=> "MY STRING"
+  #
+  def yield_self(&block)
+    return to_enum :yield_self unless block
+    block.call(self)
+  end
+end
index 7e6fa28..32d8637 100644 (file)
@@ -114,6 +114,7 @@ mrb_f_integer(mrb_state *mrb, mrb_value self)
   return mrb_convert_to_integer(mrb, arg, base);
 }
 
+#ifndef MRB_WITHOUT_FLOAT
 /*
  *  call-seq:
  *     Float(arg)    -> float
@@ -134,6 +135,7 @@ mrb_f_float(mrb_state *mrb, mrb_value self)
   mrb_get_args(mrb, "o", &arg);
   return mrb_Float(mrb, arg);
 }
+#endif
 
 /*
  *  call-seq:
@@ -222,6 +224,22 @@ mrb_f_hash(mrb_state *mrb, mrb_value self)
   return tmp;
 }
 
+/*
+ *  call-seq:
+ *     obj.itself -> an_object
+ *
+ *  Returns <i>obj</i>.
+ *
+ *      string = 'my string' #=> "my string"
+ *      string.itself.object_id == string.object_id #=> true
+ *
+ */
+static mrb_value
+mrb_f_itself(mrb_state *mrb, mrb_value self)
+{
+  return self;
+}
+
 void
 mrb_mruby_kernel_ext_gem_init(mrb_state *mrb)
 {
@@ -231,10 +249,13 @@ mrb_mruby_kernel_ext_gem_init(mrb_state *mrb)
   mrb_define_module_function(mrb, krn, "caller", mrb_f_caller, MRB_ARGS_OPT(2));
   mrb_define_method(mrb, krn, "__method__", mrb_f_method, MRB_ARGS_NONE());
   mrb_define_module_function(mrb, krn, "Integer", mrb_f_integer, MRB_ARGS_ANY());
+#ifndef MRB_WITHOUT_FLOAT
   mrb_define_module_function(mrb, krn, "Float", mrb_f_float, MRB_ARGS_REQ(1));
+#endif
   mrb_define_module_function(mrb, krn, "String", mrb_f_string, MRB_ARGS_REQ(1));
   mrb_define_module_function(mrb, krn, "Array", mrb_f_array, MRB_ARGS_REQ(1));
   mrb_define_module_function(mrb, krn, "Hash", mrb_f_hash, MRB_ARGS_REQ(1));
+  mrb_define_module_function(mrb, krn, "itself", mrb_f_itself, MRB_ARGS_NONE());
 }
 
 void
index 89cedaf..206b7ac 100644 (file)
@@ -49,7 +49,7 @@ assert('Kernel#__method__') do
 end
 
 assert('Kernel#Integer') do
-  assert_equal(123, Integer(123.999))
+  assert_equal(123, Integer(123.999)) if class_defined?("Float")
   assert_equal(26, Integer("0x1a"))
   assert_equal(930, Integer("0930", 10))
   assert_equal(7, Integer("111", 2))
@@ -63,7 +63,7 @@ assert('Kernel#Float') do
   assert_equal(123.456, Float(123.456))
   assert_equal(123.456, Float("123.456"))
   assert_raise(TypeError) { Float(nil) }
-end
+end if class_defined?("Float")
 
 assert('Kernel#String') do
   assert_equal("main", String(self))
index 7302b92..c182deb 100644 (file)
@@ -486,7 +486,7 @@ static mrb_value
 math_log(mrb_state *mrb, mrb_value obj)
 {
   mrb_float x, base;
-  int argc;
+  mrb_int argc;
 
   argc = mrb_get_args(mrb, "f|f", &x, &base);
   if (x < 0.0) {
@@ -657,7 +657,7 @@ math_ldexp(mrb_state *mrb, mrb_value obj)
   mrb_int   i;
 
   mrb_get_args(mrb, "fi", &x, &i);
-  x = ldexp(x, i);
+  x = ldexp(x, (int)i);
 
   return mrb_float_value(mrb, x);
 }
diff --git a/third-party/mruby/mrbgems/mruby-method/README.md b/third-party/mruby/mrbgems/mruby-method/README.md
new file mode 100644 (file)
index 0000000..5076bfb
--- /dev/null
@@ -0,0 +1,59 @@
+mruby-method
+===
+
+A implementetion of class **Method** and **UnboundMethod** for mruby
+
+```ruby
+p Enumerable.instance_method(:find_all).source_location
+#=> ["mruby/mruby/mrblib/enum.rb", 148]
+```
+
+# Note
+
+`source_location` method need this configuration in build_config.rb
+
+```ruby
+MRuby::Build.new do |conf|
+  enable_debug
+end
+```
+
+# Supported Methods
+
+## Kernel
+
+- `Kernel#method`
+- `Kernel#singleton_method`
+
+## Module
+
+- `Module#instance_method`
+
+## Method class
+
+- `Method#name`
+- `Method#call`
+- `Method#super_method`
+- `Method#arity`
+- `Method#unbind`
+- `Method#[]`
+- `Method#owner`
+- `Method#receiver`
+- `Method#parameters`
+- `Method#source_location`
+- `Method#to_proc`
+
+## UnboundMethod class
+
+- `UnboundMethod#name`
+- `UnboundMethod#bind`
+- `UnboundMethod#super_method`
+- `UnboundMethod#arity`
+- `UnboundMethod#owner`
+- `UnboundMethod#parameters`
+- `UnboundMethod#source_location`
+
+# See also
+
+- https://ruby-doc.org/core-2.3.3/Method.html
+- https://ruby-doc.org/core-2.3.3/UnboundMethod.html
diff --git a/third-party/mruby/mrbgems/mruby-method/mrbgem.rake b/third-party/mruby/mrbgems/mruby-method/mrbgem.rake
new file mode 100644 (file)
index 0000000..dca0c3a
--- /dev/null
@@ -0,0 +1,7 @@
+MRuby::Gem::Specification.new('mruby-method') do |spec|
+  spec.license = 'MIT'
+  spec.author  = 'mruby developers'
+  spec.summary = 'Method and UnboundMethod class'
+
+  spec.add_dependency('mruby-proc-ext', :core => 'mruby-proc-ext')
+end
diff --git a/third-party/mruby/mrbgems/mruby-method/mrblib/kernel.rb b/third-party/mruby/mrbgems/mruby-method/mrblib/kernel.rb
new file mode 100644 (file)
index 0000000..b2ebd45
--- /dev/null
@@ -0,0 +1,9 @@
+module Kernel
+  def singleton_method(name)
+    m = method(name)
+    if m.owner != singleton_class
+      raise NameError, "undefined method `#{name}' for class `#{singleton_class}'"
+    end
+    m
+  end
+end
diff --git a/third-party/mruby/mrbgems/mruby-method/mrblib/method.rb b/third-party/mruby/mrbgems/mruby-method/mrblib/method.rb
new file mode 100644 (file)
index 0000000..5de0afd
--- /dev/null
@@ -0,0 +1,20 @@
+class Method
+  def to_proc
+    m = self
+    lambda { |*args, &b|
+      m.call(*args, &b)
+    }
+  end
+
+  def owner
+    @owner
+  end
+
+  def receiver
+    @recv
+  end
+
+  def name
+    @name
+  end
+end
diff --git a/third-party/mruby/mrbgems/mruby-method/mrblib/unbound_method.rb b/third-party/mruby/mrbgems/mruby-method/mrblib/unbound_method.rb
new file mode 100644 (file)
index 0000000..1d3acf3
--- /dev/null
@@ -0,0 +1,9 @@
+class UnboundMethod
+  def owner
+    @owner
+  end
+
+  def name
+    @name
+  end
+end
diff --git a/third-party/mruby/mrbgems/mruby-method/src/method.c b/third-party/mruby/mrbgems/mruby-method/src/method.c
new file mode 100644 (file)
index 0000000..e445b34
--- /dev/null
@@ -0,0 +1,418 @@
+#include "mruby.h"
+#include "mruby/array.h"
+#include "mruby/class.h"
+#include "mruby/variable.h"
+#include "mruby/proc.h"
+#include "mruby/string.h"
+
+static struct RObject *
+method_object_alloc(mrb_state *mrb, struct RClass *mclass)
+{
+  return (struct RObject*)mrb_obj_alloc(mrb, MRB_TT_OBJECT, mclass);
+}
+
+static mrb_value
+unbound_method_bind(mrb_state *mrb, mrb_value self)
+{
+  struct RObject *me;
+  mrb_value owner = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@owner"));
+  mrb_value name = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@name"));
+  mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc"));
+  mrb_value klass = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@klass"));
+  mrb_value recv;
+
+  mrb_get_args(mrb, "o", &recv);
+
+  if (mrb_type(owner) != MRB_TT_MODULE &&
+      mrb_class_ptr(owner) != mrb_obj_class(mrb, recv) &&
+      !mrb_obj_is_kind_of(mrb, recv, mrb_class_ptr(owner))) {
+        if (mrb_type(owner) == MRB_TT_SCLASS) {
+          mrb_raise(mrb, E_TYPE_ERROR, "singleton method called for a different object");
+        } else {
+          const char *s = mrb_class_name(mrb, mrb_class_ptr(owner));
+          mrb_raisef(mrb, E_TYPE_ERROR, "bind argument must be an instance of %S", mrb_str_new_static(mrb, s, strlen(s)));
+        }
+  }
+  me = method_object_alloc(mrb, mrb_class_get(mrb, "Method"));
+  mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@owner"), owner);
+  mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@recv"), recv);
+  mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@name"), name);
+  mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "proc"), proc);
+  mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@klass"), klass);
+
+  return mrb_obj_value(me);
+}
+
+#define IV_GET(value, name) mrb_iv_get(mrb, value, mrb_intern_lit(mrb, name))
+static mrb_value
+method_eql(mrb_state *mrb, mrb_value self)
+{
+  mrb_value other, receiver, orig_proc, other_proc;
+  struct RClass *owner, *klass;
+  struct RProc *orig_rproc, *other_rproc;
+
+  mrb_get_args(mrb, "o", &other);
+  if (!mrb_obj_is_instance_of(mrb, other, mrb_class(mrb, self)))
+    return mrb_false_value();
+
+  if (mrb_class(mrb, self) != mrb_class(mrb, other))
+    return mrb_false_value();
+
+  klass = mrb_class_ptr(IV_GET(self, "@klass"));
+  if (klass != mrb_class_ptr(IV_GET(other, "@klass")))
+    return mrb_false_value();
+
+  owner = mrb_class_ptr(IV_GET(self, "@owner"));
+  if (owner != mrb_class_ptr(IV_GET(other, "@owner")))
+    return mrb_false_value();
+
+  receiver = IV_GET(self, "@recv");
+  if (!mrb_obj_equal(mrb, receiver, IV_GET(other, "@recv")))
+    return mrb_false_value();
+
+  orig_proc = IV_GET(self, "proc");
+  other_proc = IV_GET(other, "proc");
+  if (mrb_nil_p(orig_proc) && mrb_nil_p(other_proc)) {
+    if (mrb_symbol(IV_GET(self, "@name")) == mrb_symbol(IV_GET(other, "@name")))
+      return mrb_true_value();
+    else
+      return mrb_false_value();
+  }
+
+  if (mrb_nil_p(orig_proc))
+    return mrb_false_value();
+  if (mrb_nil_p(other_proc))
+    return mrb_false_value();
+
+  orig_rproc = mrb_proc_ptr(orig_proc);
+  other_rproc = mrb_proc_ptr(other_proc);
+  if (MRB_PROC_CFUNC_P(orig_rproc)) {
+    if (!MRB_PROC_CFUNC_P(other_rproc))
+      return mrb_false_value();
+    if (orig_rproc->body.func != other_rproc->body.func)
+      return mrb_false_value();
+  }
+  else {
+    if (MRB_PROC_CFUNC_P(other_rproc))
+      return mrb_false_value();
+    if (orig_rproc->body.irep != other_rproc->body.irep)
+      return mrb_false_value();
+  }
+
+  return mrb_true_value();
+}
+
+#undef IV_GET
+
+static mrb_value
+method_call(mrb_state *mrb, mrb_value self)
+{
+  mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc"));
+  mrb_value name = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@name"));
+  mrb_value recv = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@recv"));
+  struct RClass *owner = mrb_class_ptr(mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@owner")));
+  mrb_int argc;
+  mrb_value *argv, ret, block;
+  mrb_sym orig_mid;
+
+  mrb_get_args(mrb, "*&", &argv, &argc, &block);
+  orig_mid = mrb->c->ci->mid;
+  mrb->c->ci->mid = mrb_symbol(name);
+  if (mrb_nil_p(proc)) {
+    mrb_value missing_argv = mrb_ary_new_from_values(mrb, argc, argv);
+    mrb_ary_unshift(mrb, missing_argv, name);
+    ret = mrb_funcall_argv(mrb, recv, mrb_intern_lit(mrb, "method_missing"), argc + 1, RARRAY_PTR(missing_argv));
+  }
+  else if (!mrb_nil_p(block)) {
+    /*
+      workaround since `mrb_yield_with_class` does not support passing block as parameter
+      need new API that initializes `mrb->c->stack[argc+1]` with block passed by argument
+    */
+    ret = mrb_funcall_with_block(mrb, recv, mrb_symbol(name), argc, argv, block);
+  }
+  else {
+    ret = mrb_yield_with_class(mrb, proc, argc, argv, recv, owner);
+  }
+  mrb->c->ci->mid = orig_mid;
+  return ret;
+}
+
+static mrb_value
+method_unbind(mrb_state *mrb, mrb_value self)
+{
+  struct RObject *ume;
+  mrb_value owner = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@owner"));
+  mrb_value name = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@name"));
+  mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc"));
+  mrb_value klass = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@klass"));
+
+  ume = method_object_alloc(mrb, mrb_class_get(mrb, "UnboundMethod"));
+  mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@owner"), owner);
+  mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@recv"), mrb_nil_value());
+  mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@name"), name);
+  mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "proc"), proc);
+  mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@klass"), klass);
+
+  return mrb_obj_value(ume);
+}
+
+static struct RProc *
+method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid)
+{
+  mrb_method_t m = mrb_method_search_vm(mrb, cp, mid);
+  if (MRB_METHOD_UNDEF_P(m))
+    return NULL;
+  if (MRB_METHOD_PROC_P(m))
+    return MRB_METHOD_PROC(m);
+  return mrb_proc_new_cfunc(mrb, MRB_METHOD_FUNC(m));
+}
+
+static mrb_value
+method_super_method(mrb_state *mrb, mrb_value self)
+{
+  mrb_value recv = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@recv"));
+  mrb_value klass = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@klass"));
+  mrb_value owner = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@owner"));
+  mrb_value name = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@name"));
+  struct RClass *super, *rklass;
+  struct RProc *proc;
+  struct RObject *me;
+
+  switch (mrb_type(klass)) {
+    case MRB_TT_SCLASS:
+      super = mrb_class_ptr(klass)->super->super;
+      break;
+    case MRB_TT_ICLASS:
+      super = mrb_class_ptr(klass)->super;
+      break;
+    default:
+      super = mrb_class_ptr(owner)->super;
+      break;
+  }
+
+  proc = method_search_vm(mrb, &super, mrb_symbol(name));
+  if (!proc)
+    return mrb_nil_value();
+
+  rklass = super;
+  while (super->tt == MRB_TT_ICLASS)
+    super = super->c;
+
+  me = method_object_alloc(mrb, mrb_obj_class(mrb, self));
+  mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@owner"), mrb_obj_value(super));
+  mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@recv"), recv);
+  mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@name"), name);
+  mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "proc"), mrb_obj_value(proc));
+  mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@klass"), mrb_obj_value(rklass));
+
+  return mrb_obj_value(me);
+}
+
+static mrb_value
+method_arity(mrb_state *mrb, mrb_value self)
+{
+  mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc"));
+  struct RProc *rproc;
+  struct RClass *orig;
+  mrb_value ret;
+
+  if (mrb_nil_p(proc))
+    return mrb_fixnum_value(-1);
+
+  rproc = mrb_proc_ptr(proc);
+  orig = rproc->c;
+  rproc->c = mrb->proc_class;
+  ret = mrb_funcall(mrb, proc, "arity", 0);
+  rproc->c = orig;
+  return ret;
+}
+
+static mrb_value
+method_source_location(mrb_state *mrb, mrb_value self)
+{
+  mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc"));
+  struct RProc *rproc;
+  struct RClass *orig;
+  mrb_value ret;
+
+  if (mrb_nil_p(proc))
+    return mrb_nil_value();
+
+  rproc = mrb_proc_ptr(proc);
+  orig = rproc->c;
+  rproc->c = mrb->proc_class;
+  ret = mrb_funcall(mrb, proc, "source_location", 0);
+  rproc->c = orig;
+  return ret;
+}
+
+static mrb_value
+method_parameters(mrb_state *mrb, mrb_value self)
+{
+  mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc"));
+  struct RProc *rproc;
+  struct RClass *orig;
+  mrb_value ret;
+
+  if (mrb_nil_p(proc)) {
+    mrb_value rest = mrb_symbol_value(mrb_intern_lit(mrb, "rest"));
+    mrb_value arest = mrb_ary_new_from_values(mrb, 1, &rest);
+    return mrb_ary_new_from_values(mrb, 1, &arest);
+  }
+
+  rproc = mrb_proc_ptr(proc);
+  orig = rproc->c;
+  rproc->c = mrb->proc_class;
+  ret = mrb_funcall(mrb, proc, "parameters", 0);
+  rproc->c = orig;
+  return ret;
+}
+
+static mrb_value
+method_to_s(mrb_state *mrb, mrb_value self)
+{
+  mrb_value owner = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@owner"));
+  mrb_value klass = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@klass"));
+  mrb_value name = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@name"));
+  mrb_value str = mrb_str_new_lit(mrb, "#<");
+  struct RClass *rklass;
+
+  mrb_str_cat_cstr(mrb, str, mrb_obj_classname(mrb, self));
+  mrb_str_cat_lit(mrb, str, ": ");
+  rklass = mrb_class_ptr(klass);
+  if (mrb_class_ptr(owner) == rklass) {
+    mrb_str_cat_str(mrb, str, mrb_funcall(mrb, owner, "to_s", 0));
+    mrb_str_cat_lit(mrb, str, "#");
+    mrb_str_cat_str(mrb, str, mrb_funcall(mrb, name, "to_s", 0));
+  }
+  else {
+    mrb_str_cat_cstr(mrb, str, mrb_class_name(mrb, rklass));
+    mrb_str_cat_lit(mrb, str, "(");
+    mrb_str_cat_str(mrb, str, mrb_funcall(mrb, owner, "to_s", 0));
+    mrb_str_cat_lit(mrb, str, ")#");
+    mrb_str_cat_str(mrb, str, mrb_funcall(mrb, name, "to_s", 0));
+  }
+  mrb_str_cat_lit(mrb, str, ">");
+  return str;
+}
+
+static void
+mrb_search_method_owner(mrb_state *mrb, struct RClass *c, mrb_value obj, mrb_sym name, struct RClass **owner, struct RProc **proc, mrb_bool unbound)
+{
+  mrb_value ret;
+  const char *s;
+
+  *owner = c;
+  *proc = method_search_vm(mrb, owner, name);
+  if (!*proc) {
+    if (unbound) {
+      goto name_error;
+    }
+    if (!mrb_respond_to(mrb, obj, mrb_intern_lit(mrb, "respond_to_missing?"))) {
+      goto name_error;
+    }
+    ret = mrb_funcall(mrb, obj, "respond_to_missing?", 2, mrb_symbol_value(name), mrb_true_value());
+    if (!mrb_test(ret)) {
+      goto name_error;
+    }
+    *owner = c;
+  }
+
+  while ((*owner)->tt == MRB_TT_ICLASS)
+    *owner = (*owner)->c;
+
+  return;
+
+name_error:
+  s = mrb_class_name(mrb, c);
+  mrb_raisef(
+    mrb, E_NAME_ERROR,
+    "undefined method `%S' for class `%S'",
+    mrb_sym2str(mrb, name),
+    mrb_str_new_static(mrb, s, strlen(s))
+  );
+}
+
+static mrb_value
+mrb_kernel_method(mrb_state *mrb, mrb_value self)
+{
+  struct RClass *owner;
+  struct RProc *proc;
+  struct RObject *me;
+  mrb_sym name;
+
+  mrb_get_args(mrb, "n", &name);
+
+  mrb_search_method_owner(mrb, mrb_class(mrb, self), self, name, &owner, &proc, FALSE);
+
+  me = method_object_alloc(mrb, mrb_class_get(mrb, "Method"));
+  mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@owner"), mrb_obj_value(owner));
+  mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@recv"), self);
+  mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@name"), mrb_symbol_value(name));
+  mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "proc"), proc ? mrb_obj_value(proc) : mrb_nil_value());
+  mrb_obj_iv_set(mrb, me, mrb_intern_lit(mrb, "@klass"), mrb_obj_value(mrb_class(mrb, self)));
+
+  return mrb_obj_value(me);
+}
+
+static mrb_value
+mrb_module_instance_method(mrb_state *mrb, mrb_value self)
+{
+  struct RClass *owner;
+  struct RProc *proc;
+  struct RObject *ume;
+  mrb_sym name;
+
+  mrb_get_args(mrb, "n", &name);
+
+  mrb_search_method_owner(mrb, mrb_class_ptr(self), self, name, &owner, &proc, TRUE);
+
+  ume = method_object_alloc(mrb, mrb_class_get(mrb, "UnboundMethod"));
+  mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@owner"), mrb_obj_value(owner));
+  mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@recv"), mrb_nil_value());
+  mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@name"), mrb_symbol_value(name));
+  mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "proc"), proc ? mrb_obj_value(proc) : mrb_nil_value());
+  mrb_obj_iv_set(mrb, ume, mrb_intern_lit(mrb, "@klass"), self);
+
+  return mrb_obj_value(ume);
+}
+
+void
+mrb_mruby_method_gem_init(mrb_state* mrb)
+{
+  struct RClass *unbound_method = mrb_define_class(mrb, "UnboundMethod", mrb->object_class);
+  struct RClass *method = mrb_define_class(mrb, "Method", mrb->object_class);
+
+  mrb_undef_class_method(mrb, unbound_method, "new");
+  mrb_define_method(mrb, unbound_method, "bind", unbound_method_bind, MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, unbound_method, "super_method", method_super_method, MRB_ARGS_NONE());
+  mrb_define_method(mrb, unbound_method, "==", method_eql, MRB_ARGS_REQ(1));
+  mrb_alias_method(mrb, unbound_method, mrb_intern_lit(mrb, "eql?"), mrb_intern_lit(mrb, "=="));
+  mrb_define_method(mrb, unbound_method, "to_s", method_to_s, MRB_ARGS_NONE());
+  mrb_define_method(mrb, unbound_method, "inspect", method_to_s, MRB_ARGS_NONE());
+  mrb_define_method(mrb, unbound_method, "arity", method_arity, MRB_ARGS_NONE());
+  mrb_define_method(mrb, unbound_method, "source_location", method_source_location, MRB_ARGS_NONE());
+  mrb_define_method(mrb, unbound_method, "parameters", method_parameters, MRB_ARGS_NONE());
+
+  mrb_undef_class_method(mrb, method, "new");
+  mrb_define_method(mrb, method, "==", method_eql, MRB_ARGS_REQ(1));
+  mrb_alias_method(mrb, method, mrb_intern_lit(mrb, "eql?"), mrb_intern_lit(mrb, "=="));
+  mrb_define_method(mrb, method, "to_s", method_to_s, MRB_ARGS_NONE());
+  mrb_define_method(mrb, method, "inspect", method_to_s, MRB_ARGS_NONE());
+  mrb_define_method(mrb, method, "call", method_call, MRB_ARGS_ANY());
+  mrb_alias_method(mrb, method, mrb_intern_lit(mrb, "[]"), mrb_intern_lit(mrb, "call"));
+  mrb_define_method(mrb, method, "unbind", method_unbind, MRB_ARGS_NONE());
+  mrb_define_method(mrb, method, "super_method", method_super_method, MRB_ARGS_NONE());
+  mrb_define_method(mrb, method, "arity", method_arity, MRB_ARGS_NONE());
+  mrb_define_method(mrb, method, "source_location", method_source_location, MRB_ARGS_NONE());
+  mrb_define_method(mrb, method, "parameters", method_parameters, MRB_ARGS_NONE());
+
+  mrb_define_method(mrb, mrb->kernel_module, "method", mrb_kernel_method, MRB_ARGS_REQ(1));
+
+  mrb_define_method(mrb, mrb->module_class, "instance_method", mrb_module_instance_method, MRB_ARGS_REQ(1));
+}
+
+void
+mrb_mruby_method_gem_final(mrb_state* mrb)
+{
+}
diff --git a/third-party/mruby/mrbgems/mruby-method/test/method.rb b/third-party/mruby/mrbgems/mruby-method/test/method.rb
new file mode 100644 (file)
index 0000000..b229a45
--- /dev/null
@@ -0,0 +1,425 @@
+class Base
+  def foo() :base end
+end
+
+class Derived < Base
+  def foo() :derived end
+end
+
+class Interpreter
+  attr_accessor :ret
+
+  def do_a() @ret += "there, "; end
+  def do_d() @ret += "Hello ";  end
+  def do_e() @ret += "!\n";     end
+  def do_v() @ret += "Dave";    end
+  Dispatcher = {
+    "a" => instance_method(:do_a),
+    "d" => instance_method(:do_d),
+    "e" => instance_method(:do_e),
+    "v" => instance_method(:do_v)
+  }
+  def interpret(string)
+    @ret = ""
+    string.each_char {|b| Dispatcher[b].bind(self).call }
+  end
+end
+
+assert 'demo' do
+  interpreter = Interpreter.new
+  interpreter.interpret('dave')
+  assert_equal "Hello there, Dave!\n", interpreter.ret
+end
+
+assert 'Method#arity' do
+  Class.new {
+    attr_accessor :done
+    def initialize; @done = false; end
+    def m0() end
+    def m1(a) end
+    def m2(a, b) end
+    def mo1(a = nil, &b) end
+    def mo2(a, b = nil) end
+    def mo3(*a) end
+    def mo4(a, *b, &c) end
+    def mo5(a, *b, c) end
+    def mo6(a, *b, c, &d) end
+    def mo7(a, b = nil, *c, d, &e) end
+    def ma1((a), &b) nil && a end
+
+    def run
+      assert_equal(0, method(:m0).arity)
+      assert_equal(1, method(:m1).arity)
+      assert_equal(2, method(:m2).arity)
+      assert_equal(-1, method(:mo1).arity)
+      assert_equal(-2, method(:mo2).arity)
+      assert_equal(-1, method(:mo3).arity)
+      assert_equal(-2, method(:mo4).arity)
+      assert_equal(-3, method(:mo5).arity)
+      assert_equal(-3, method(:mo6).arity)
+      assert_equal(-3, method(:mo7).arity)
+      assert_equal(1, method(:ma1).arity)
+
+      assert_equal(-1, method(:__send__).arity)
+      assert_equal(-1, method(:nothing).arity)
+    end
+
+    def respond_to_missing?(m, b)
+      m == :nothing
+    end
+  }.new.run
+end
+
+assert 'Method and UnboundMethod should not be have a `new` method' do
+  assert_raise(NoMethodError){ Method.new }
+  assert_raise(NoMethodError){ UnboundMethod.new }
+end
+
+assert 'instance' do
+  assert_kind_of Method, 1.method(:+)
+  assert_kind_of UnboundMethod, Fixnum.instance_method(:+)
+end
+
+assert 'Method#call' do
+  assert_equal 3, 1.method(:+).call(2)
+  assert_equal "ab", "a".method(:+)["b"]
+  klass = Class.new {
+    def foo; 42; end
+  }
+  klass2 = Class.new(klass) {
+    def foo; super; end
+  }
+  assert_equal 42, klass2.new.method(:foo).call
+
+  i = Class.new {
+   def bar
+     yield 3
+   end
+  }.new
+  assert_raise(LocalJumpError) { i.method(:bar).call }
+  assert_equal 3, i.method(:bar).call { |i| i }
+end
+
+assert 'Method#call for regression' do
+  obj = BasicObject.new
+  def obj.foo
+    :ok
+  end
+  assert_equal :ok, Kernel.instance_method(:send).bind(obj).call(:foo), "https://github.com/ksss/mruby-method/issues/4"
+end
+
+assert 'Method#call with undefined method' do
+  c = Class.new {
+    attr_accessor :m, :argv
+    def respond_to_missing?(m, b)
+      m == :foo
+    end
+
+    def method_missing(m, *argv)
+      @m = m
+      @argv = argv
+      super
+    end
+  }
+  cc = c.new
+  assert_raise(NameError) { cc.method(:nothing) }
+  assert_kind_of Method, cc.method(:foo)
+  assert_raise(NoMethodError) { cc.method(:foo).call(:arg1, :arg2) }
+  assert_equal :foo, cc.m
+  assert_equal [:arg1, :arg2], cc.argv
+
+  cc = c.new
+  m = cc.method(:foo)
+  c.class_eval do
+    def foo
+      :ng
+    end
+  end
+  assert_raise(NoMethodError) { m.call(:arg1, :arg2) }
+end
+
+assert 'Method#source_location' do
+  skip if proc{}.source_location.nil?
+
+  filename = __FILE__
+  klass = Class.new
+
+  lineno = __LINE__ + 1
+  klass.define_method(:find_me_if_you_can) {}
+  assert_equal [filename, lineno], klass.new.method(:find_me_if_you_can).source_location
+
+  lineno = __LINE__ + 1
+  klass.define_singleton_method(:s_find_me_if_you_can) {}
+  assert_equal [filename, lineno], klass.method(:s_find_me_if_you_can).source_location
+
+  klass = Class.new { def respond_to_missing?(m, b); m == :nothing; end }
+  assert_nil klass.new.method(:nothing).source_location
+end
+
+assert 'UnboundMethod#source_location' do
+  skip if proc{}.source_location.nil?
+
+  filename = __FILE__
+  klass = Class.new {
+    def respond_to_missing?(m, b)
+      m == :nothing
+    end
+  }
+
+  lineno = __LINE__ + 1
+  klass.define_method(:find_me_if_you_can) {}
+  assert_equal [filename, lineno], klass.instance_method(:find_me_if_you_can).source_location
+  assert_nil klass.new.method(:nothing).unbind.source_location
+end
+
+assert 'Method#parameters' do
+  klass = Class.new {
+    def foo(a, b=nil, *c) end
+    def respond_to_missing?(m, b)
+      m == :missing
+    end
+  }
+  assert_equal [[:req, :a], [:opt, :b], [:rest, :c]], klass.new.method(:foo).parameters
+  assert_equal [[:rest]], klass.new.method(:missing).parameters
+end
+
+assert 'UnboundMethod#parameters' do
+  klass = Class.new {
+    def foo(a, b=nil, *c) end
+    def respond_to_missing?(m, b)
+      m == :nothing
+    end
+  }
+  assert_equal [[:req, :a], [:opt, :b], [:rest, :c]], klass.instance_method(:foo).parameters
+  assert_equal [[:rest]], klass.new.method(:nothing).unbind.parameters
+end
+
+assert 'Method#to_proc' do
+  m = 3.method(:+)
+  assert_kind_of Proc, m.to_proc
+  assert_equal 7, m.call(4)
+
+  o = Object.new
+  def o.foo(a, b=nil, *c)
+    [a, b, c]
+  end
+  assert_equal [:bar, nil, []], o.method(:foo).to_proc.call(:bar)
+#  We can fix this issue but leave until the problem
+#  assert_equal o.method(:foo).arity, o.method(:foo).to_proc.arity
+
+  def o.bar
+    yield 39
+  end
+  assert_equal 42, o.bar(&3.method(:+))
+end
+
+assert 'to_s' do
+  o = Object.new
+  def o.foo; end
+  m = o.method(:foo)
+  assert_equal("#<UnboundMethod: #{ class << o; self; end.inspect }#foo>", m.unbind.inspect)
+
+  c = Class.new
+  c.class_eval { def foo; end; }
+  m = c.new.method(:foo)
+  assert_equal("#<Method: #{ c.inspect }#foo>", m.inspect)
+  m = c.instance_method(:foo)
+  assert_equal("#<UnboundMethod: #{ c.inspect }#foo>", m.inspect)
+end
+
+assert 'owner' do
+  c = Class.new do
+    def foo; end
+    def self.bar; end
+  end
+  m = Module.new do
+    def baz; end
+  end
+  c.include(m)
+  c2 = Class.new(c)
+
+  assert_equal(c, c.instance_method(:foo).owner)
+  assert_equal(c, c2.instance_method(:foo).owner)
+
+  assert_equal(c, c.new.method(:foo).owner)
+  assert_equal(c, c2.new.method(:foo).owner)
+  assert_equal(c.singleton_class, c2.method(:bar).owner)
+end
+
+assert 'owner missing' do
+  c = Class.new do
+    def respond_to_missing?(name, bool)
+      name == :foo
+    end
+  end
+  c2 = Class.new(c)
+  assert_equal(c, c.new.method(:foo).owner)
+  assert_equal(c2, c2.new.method(:foo).owner)
+end
+
+assert 'receiver name owner' do
+  o = Object.new
+  def o.foo; end
+  m = o.method(:foo)
+  assert_equal(o, m.receiver)
+  assert_equal(:foo, m.name)
+  assert_equal(class << o; self; end, m.owner)
+  assert_equal(:foo, m.unbind.name)
+  assert_equal(class << o; self; end, m.unbind.owner)
+end
+
+assert 'Method#unbind' do
+  assert_equal(:derived, Derived.new.foo)
+  um = Derived.new.method(:foo).unbind
+  assert_kind_of(UnboundMethod, um)
+  Derived.class_eval do
+    def foo() :changed end
+  end
+  assert_equal(:changed, Derived.new.foo)
+  assert_equal(:changed, Derived.new.foo{})
+  assert_equal(:derived, um.bind(Derived.new).call)
+  assert_raise(TypeError) do
+    um.bind(Base.new)
+  end
+
+  # TODO:
+  #  Block passed method not handled correctly with workaround.
+  #  See comment near `mrb_funcall_with_block` for detail.
+  # assert_equal(:derived, um.bind(Derived.new).call{})
+end
+
+assert 'Kernel#method' do
+  c1 = Class.new {
+    def foo; :foo; end
+  }
+  o = c1.new
+  assert_kind_of Method, o.method(:foo)
+  assert_kind_of Method, o.method('foo')
+  assert_raise(TypeError) { o.method(nil) }
+  assert_raise(NameError) { o.method('bar') }
+  assert_raise(NameError) { o.method(:bar) }
+end
+
+assert "Module#instance_method" do
+  assert_kind_of UnboundMethod, Object.instance_method(:object_id)
+  assert_raise(NameError) { Object.instance_method(:nothing) }
+  c = Class.new {
+    def respond_to_missing?(m, b)
+      false
+    end
+  }
+  assert_raise(NameError) { c.instance_method(:nothing) }
+end
+
+assert 'Kernel#singleton_method' do
+  c1 = Class.new {
+    def foo; :foo; end
+  }
+  o = c1.new
+  def o.bar; :bar; end
+  assert_kind_of Method, o.method(:foo)
+  assert_raise(NameError) { o.singleton_method(:foo) }
+  assert_kind_of Method, o.singleton_method(:bar)
+  assert_raise(TypeError) { o.singleton_method(nil) }
+  m = assert_nothing_raised(NameError) { break o.singleton_method(:bar) }
+  assert_equal(:bar, m.call)
+end
+
+assert 'Method#super_method' do
+  o = Derived.new
+  m = o.method(:foo).super_method
+  assert_equal(Base, m.owner)
+  assert_true(o.equal? m.receiver)
+  assert_equal(:foo, m.name)
+  assert_nil(m.super_method)
+
+  c = Class.new {
+    def foo; end
+  }
+  o = c.new
+  o.extend Module.new {
+    def foo; end
+  }
+  assert_equal c, o.method(:foo).super_method.owner
+  assert_equal :foo, o.method(:foo).super_method.name
+  assert_equal o, o.method(:foo).super_method.receiver
+end
+
+assert 'Method#==' do
+  o = Object.new
+  class << o
+    def foo; end
+  end
+  assert_not_equal(o.method(:foo), nil)
+  m = o.method(:foo)
+  def m.foo; end
+  # TODO: assert_not_equal(o.method(:foo), m)
+  assert_equal(o.method(:foo), o.method(:foo))
+  # TODO: assert_false(o.method(:foo).eql? m)
+  assert_true(o.method(:foo).eql? o.method(:foo))
+
+  assert_false(0.method(:+) == 1.method(:+))
+  assert_false(0.method(:+) == 0.method(:-))
+  a = 0.method(:+)
+  assert_true(a.method(:==) == a.method(:eql?))
+end
+
+assert "Method#initialize_copy" do
+  c = Class.new {
+    def foo
+    end
+  }.new
+  m1 = c.method(:foo)
+  m2 = m1.clone
+  assert_equal(m1, m2)
+end
+
+assert 'UnboundMethod#arity' do
+  c = Class.new {
+    def foo(a, b)
+    end
+
+    def respond_to_missing?(m, b)
+      m == :nothing
+    end
+  }
+  assert_equal 2, c.instance_method(:foo).arity
+  assert_equal(-1, c.new.method(:nothing).unbind.arity)
+end
+
+assert 'UnboundMethod#==' do
+  assert_false(Fixnum.instance_method(:+) == Fixnum.instance_method(:-))
+  assert_true(Fixnum.instance_method(:+) == Fixnum.instance_method(:+))
+  assert_false(Fixnum.instance_method(:+) == Float.instance_method(:+))
+  assert_true(UnboundMethod.instance_method(:==) == UnboundMethod.instance_method(:eql?))
+end
+
+assert 'UnboundMethod#super_method' do
+  m = Derived.instance_method(:foo)
+  m = m.super_method
+  assert_equal(Base.instance_method(:foo), m)
+  assert_nil(m.super_method)
+
+  m = Object.instance_method(:object_id)
+  assert_nil(m.super_method)
+end
+
+assert 'UnboundMethod#bind' do
+  m = Module.new{ def meth() :meth end }.instance_method(:meth)
+  assert_raise(ArgumentError) { m.bind }
+  assert_kind_of Method, m.bind(1)
+  assert_kind_of Method, m.bind(:sym)
+  assert_kind_of Method, m.bind(Object.new)
+  assert_equal(:meth, m.bind(1).call)
+  assert_equal(:meth, m.bind(:sym).call)
+  assert_equal(:meth, m.bind(Object.new).call)
+  sc = Class.new {
+    class << self
+      def foo
+      end
+    end
+  }.singleton_class
+  assert_raise(TypeError) { sc.instance_method(:foo).bind([]) }
+  assert_raise(TypeError) { Array.instance_method(:each).bind(1) }
+  assert_kind_of Method, Object.instance_method(:object_id).bind(Object.new)
+end
index 0bf3c6a..f250538 100644 (file)
@@ -14,4 +14,12 @@ module Integral
       self
     end
   end
+
+  def positive?
+    self > 0
+  end
+
+  def negative?
+    self < 0
+  end
 end
index f71236a..1d6a077 100644 (file)
@@ -1,13 +1,34 @@
 #include <limits.h>
 #include <mruby.h>
 
+static inline mrb_int
+to_int(mrb_value x)
+{
+  double f;
+
+  if (mrb_fixnum_p(x)) return mrb_fixnum(x);
+  f = mrb_float(x);
+  return (mrb_int)f;
+}
+
+/*
+ *  Document-method: Integer#chr
+ *  call-seq:
+ *     int.chr  ->  string
+ *
+ *  Returns a string containing the character represented by the +int+'s value
+ *  according to +encoding+.
+ *
+ *     65.chr    #=> "A"
+ *     230.chr   #=> "\xE6"
+ */
 static mrb_value
 mrb_int_chr(mrb_state *mrb, mrb_value x)
 {
   mrb_int chr;
   char c;
 
-  chr = mrb_fixnum(x);
+  chr = to_int(x);
   if (chr >= (1 << CHAR_BIT)) {
     mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", x);
   }
@@ -16,12 +37,63 @@ mrb_int_chr(mrb_state *mrb, mrb_value x)
   return mrb_str_new(mrb, &c, 1);
 }
 
+/*
+ *  call-seq:
+ *     int.allbits?(mask)  ->  true or false
+ *
+ *  Returns +true+ if all bits of <code>+int+ & +mask+</code> are 1.
+ */
+static mrb_value
+mrb_int_allbits(mrb_state *mrb, mrb_value self)
+{
+  mrb_int n, m;
+
+  n = to_int(self);
+  mrb_get_args(mrb, "i", &m);
+  return mrb_bool_value((n & m) == m);
+}
+
+/*
+ *  call-seq:
+ *     int.anybits?(mask)  ->  true or false
+ *
+ *  Returns +true+ if any bits of <code>+int+ & +mask+</code> are 1.
+ */
+static mrb_value
+mrb_int_anybits(mrb_state *mrb, mrb_value self)
+{
+  mrb_int n, m;
+
+  n = to_int(self);
+  mrb_get_args(mrb, "i", &m);
+  return mrb_bool_value((n & m) != 0);
+}
+
+/*
+ *  call-seq:
+ *     int.nobits?(mask)  ->  true or false
+ *
+ *  Returns +true+ if no bits of <code>+int+ & +mask+</code> are 1.
+ */
+static mrb_value
+mrb_int_nobits(mrb_state *mrb, mrb_value self)
+{
+  mrb_int n, m;
+
+  n = to_int(self);
+  mrb_get_args(mrb, "i", &m);
+  return mrb_bool_value((n & m) == 0);
+}
+
 void
 mrb_mruby_numeric_ext_gem_init(mrb_state* mrb)
 {
-  struct RClass *i = mrb_class_get(mrb, "Integer");
+  struct RClass *i = mrb_module_get(mrb, "Integral");
 
   mrb_define_method(mrb, i, "chr", mrb_int_chr, MRB_ARGS_NONE());
+  mrb_define_method(mrb, i, "allbits?", mrb_int_allbits, MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, i, "anybits?", mrb_int_anybits, MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, i, "nobits?", mrb_int_nobits, MRB_ARGS_REQ(1));
 }
 
 void
index 8bead24..6ea0c14 100644 (file)
@@ -15,7 +15,7 @@ end
 
 assert('Float#div') do
   assert_float 52, 365.2425.div(7)
-end
+end if class_defined?("Float")
 
 assert('Integer#zero?') do
   assert_equal true, 0.zero?
index 35a07b5..b076b3e 100644 (file)
@@ -16,6 +16,7 @@ nil_to_a(mrb_state *mrb, mrb_value obj)
   return mrb_ary_new(mrb);
 }
 
+#ifndef MRB_WITHOUT_FLOAT
 /*
  *  call-seq:
  *     nil.to_f    -> 0.0
@@ -28,6 +29,7 @@ nil_to_f(mrb_state *mrb, mrb_value obj)
 {
   return mrb_float_value(mrb, 0.0);
 }
+#endif
 
 /*
  *  call-seq:
@@ -77,7 +79,9 @@ mrb_obj_instance_exec(mrb_state *mrb, mrb_value self)
   switch (mrb_type(self)) {
   case MRB_TT_SYMBOL:
   case MRB_TT_FIXNUM:
+#ifndef MRB_WITHOUT_FLOAT
   case MRB_TT_FLOAT:
+#endif
     c = NULL;
     break;
   default:
@@ -94,7 +98,9 @@ mrb_mruby_object_ext_gem_init(mrb_state* mrb)
   struct RClass * n = mrb->nil_class;
 
   mrb_define_method(mrb, n, "to_a", nil_to_a,       MRB_ARGS_NONE());
+#ifndef MRB_WITHOUT_FLOAT
   mrb_define_method(mrb, n, "to_f", nil_to_f,       MRB_ARGS_NONE());
+#endif
   mrb_define_method(mrb, n, "to_i", nil_to_i,       MRB_ARGS_NONE());
 
   mrb_define_method(mrb, mrb->kernel_module, "instance_exec", mrb_obj_instance_exec, MRB_ARGS_ANY() | MRB_ARGS_BLOCK());
index 5cd1cf4..7f77363 100644 (file)
@@ -4,7 +4,7 @@ end
 
 assert('NilClass#to_f') do
   assert_equal 0.0, nil.to_f
-end
+end if class_defined?("Float")
 
 assert('NilClass#to_i') do
   assert_equal 0, nil.to_i
index 4731d53..0553b97 100644 (file)
@@ -31,8 +31,8 @@ assert('ObjectSpace.count_objects') do
   1000.times do
     objs << {}
   end
-  objs = nil
   ObjectSpace.count_objects(h)
+  objs = nil
   GC.start
   ObjectSpace.count_objects(h_after)
 
diff --git a/third-party/mruby/mrbgems/mruby-pack/.gitignore b/third-party/mruby/mrbgems/mruby-pack/.gitignore
new file mode 100644 (file)
index 0000000..55ef316
--- /dev/null
@@ -0,0 +1,5 @@
+gem_*
+gem-*
+mrb-*.a
+src/*.o
+/tmp
diff --git a/third-party/mruby/mrbgems/mruby-pack/.travis.yml b/third-party/mruby/mrbgems/mruby-pack/.travis.yml
new file mode 100644 (file)
index 0000000..ffe2272
--- /dev/null
@@ -0,0 +1,2 @@
+script:
+  - "ruby run_test.rb all test"
diff --git a/third-party/mruby/mrbgems/mruby-pack/README.md b/third-party/mruby/mrbgems/mruby-pack/README.md
new file mode 100644 (file)
index 0000000..95733e2
--- /dev/null
@@ -0,0 +1,70 @@
+mruby-pack (pack / unpack)
+=========
+
+mruby-pack provides `Array#pack` and `String#unpack` for mruby.
+
+
+## Installation
+Add the line below into your `build_config.rb`:
+
+```
+  conf.gem :github => 'iij/mruby-pack'
+```
+
+There is no dependency on other mrbgems.
+
+
+## Supported template string
+ - A : arbitrary binary string (space padded, count is width)
+ - a : arbitrary binary string (null padded, count is width)
+ - C : 8-bit unsigned (unsigned char)
+ - c : 8-bit signed (signed char)
+ - D, d: 64-bit float, native format
+ - E : 64-bit float, little endian byte order
+ - e : 32-bit float, little endian byte order
+ - F, f: 32-bit float, native format
+ - G : 64-bit float, network (big-endian) byte order
+ - g : 32-bit float, network (big-endian) byte order
+ - H : hex string (high nibble first)
+ - h : hex string (low nibble first)
+ - I : unsigned integer, native endian (`unsigned int` in C)
+ - i : signed integer, native endian (`int` in C)
+ - L : 32-bit unsigned, native endian (`uint32_t`)
+ - l : 32-bit signed, native endian (`int32_t`)
+ - m : base64 encoded string (see RFC 2045, count is width)
+ - N : 32-bit unsigned, network (big-endian) byte order
+ - n : 16-bit unsigned, network (big-endian) byte order
+ - Q : 64-bit unsigned, native endian (`uint64_t`)
+ - q : 64-bit signed, native endian (`int64_t`)
+ - S : 16-bit unsigned, native endian (`uint16_t`)
+ - s : 16-bit signed, native endian (`int16_t`)
+ - U : UTF-8 character
+ - V : 32-bit unsigned, VAX (little-endian) byte order
+ - v : 16-bit unsigned, VAX (little-endian) byte order
+ - x : null byte
+ - Z : same as "a", except that null is added with *
+
+
+
+## License
+
+Copyright (c) 2012 Internet Initiative Japan Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a 
+copy of this software and associated documentation files (the "Software"), 
+to deal in the Software without restriction, including without limitation 
+the rights to use, copy, modify, merge, publish, distribute, sublicense, 
+and/or sell copies of the Software, and to permit persons to whom the 
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in 
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+DEALINGS IN THE SOFTWARE.
+
diff --git a/third-party/mruby/mrbgems/mruby-pack/mrbgem.rake b/third-party/mruby/mrbgems/mruby-pack/mrbgem.rake
new file mode 100644 (file)
index 0000000..6e8375d
--- /dev/null
@@ -0,0 +1,7 @@
+MRuby::Gem::Specification.new('mruby-pack') do |spec|
+  spec.license = 'MIT'
+  spec.authors = 'Internet Initiative Japan Inc.'
+  spec.summary = 'Array#pack and String#unpack method'
+
+  spec.cc.include_paths << "#{build.root}/src"
+end
diff --git a/third-party/mruby/mrbgems/mruby-pack/packtest.rb b/third-party/mruby/mrbgems/mruby-pack/packtest.rb
new file mode 100644 (file)
index 0000000..459447a
--- /dev/null
@@ -0,0 +1,157 @@
+# encoding: ascii
+
+# a = Array, s = String, t = Template
+
+def packtest(a, s, t)
+  begin
+    r = a.pack(t)
+    return if r == s
+    puts "#{a.inspect}.pack(#{t.inspect}) -> #{r.inspect} should be #{s.inspect}"
+  rescue => r
+    unless r.is_a? s
+      puts "#{a.inspect}.pack(#{t.inspect}) -> #{r.inspect} should be #{s.inspect}"
+    end
+  end
+end
+
+def unpacktest(a, s, t)
+  r = s.unpack(t)
+  return if r == a
+  puts "#{s.inspect}.unpack(#{t.inspect}) -> #{r.inspect} should be #{a.inspect}"
+end
+
+def pptest(a, s, t)
+  packtest(a, s, t)
+  unpacktest(a, s, t)
+end
+
+pptest [1], "\x01", "C"
+
+packtest [1.1], "\x01", "C"
+packtest [-1], "\xff", "C"
+packtest [1,2], "\x01\x02", "C2"
+#packtest [1], "X", ArgumentError
+
+unpacktest [48, nil], "0", "CC"
+unpacktest [160, -96], "\xa0\xa0", "Cc"
+unpacktest [49, 50, 51], "123", "C*"
+
+pptest [12849], "12", "S"
+unpacktest [nil], "0", "S"
+unpacktest [12849, nil], "123", "SS"
+unpacktest [12849], "123", "S*"
+
+pptest [10000], "\x27\x10", "s>"
+pptest [-10000], "\xd8\xf0", "s>"
+pptest [50000], "\xc3\x50", "S>"
+
+pptest [10000], "\x10\x27", "s<"
+pptest [-10000], "\xf0\xd8", "s<"
+pptest [50000], "\x50\xc3", "S<"
+
+pptest [1000000000], "\x3b\x9a\xca\x00", "l>"
+pptest [-1000000000], "\xc4\x65\x36\x00", "l>"
+
+pptest [1], "\x01\x00\x00\x00", "L<"
+pptest [258], "\x02\x01\x00\x00", "L<"
+pptest [66051], "\x03\x02\x01\x00", "L<"
+pptest [16909060], "\x04\x03\x02\x01", "L<"
+pptest [16909060], "\x01\x02\x03\x04", "L>"
+
+packtest [-1], "\xff\xff\xff\xff", "L<"
+
+pptest [1000000000], "\x00\x00\x00\x00\x3b\x9a\xca\x00", "q>"
+pptest [-1000000000], "\xff\xff\xff\xff\xc4\x65\x36\x00", "q>"
+
+if (2**33).is_a? Fixnum
+  pptest [81985529216486895],    "\x01\x23\x45\x67\x89\xab\xcd\xef", "q>"
+  pptest [-1167088121787636991], "\x01\x23\x45\x67\x89\xab\xcd\xef", "q<"
+end
+
+pptest [16909060], "\x01\x02\x03\x04", "N"
+pptest [258], "\x01\x02", "n"
+pptest [32769], "\x80\x01", "n"
+
+pptest [16909060], "\x04\x03\x02\x01", "V"
+pptest [258], "\x02\x01", "v"
+
+packtest [""], "", "m"
+packtest ["a"], "YQ==\n", "m"
+packtest ["ab"], "YWI=\n", "m"
+packtest ["abc"], "YWJj\n", "m"
+packtest ["abcd"], "YWJjZA==\n", "m"
+
+unpacktest [""], "", "m"
+unpacktest ["a"], "YQ==\n", "m"
+unpacktest ["ab"], "YWI=\n", "m"
+unpacktest ["abc"], "YWJj\n", "m"
+unpacktest ["abcd"], "YWJjZA==\n", "m"
+
+packtest [""], "\0", "H"
+packtest ["3"], "0", "H"
+packtest ["34"], "", "H0"
+packtest ["34"], "0", "H"
+packtest ["34"], "4", "H2"
+packtest ["34"], "4\0", "H3"
+packtest ["3456"], "4P", "H3"
+packtest ["34563"], "4V0", "H*"
+packtest ["5a"], "Z", "H*"
+packtest ["5A"], "Z", "H*"
+
+unpacktest [""], "", "H"
+unpacktest [""], "0", "H0"
+unpacktest ["3"], "0", "H"
+unpacktest ["30"], "0", "H2"
+unpacktest ["30"], "0", "H3"
+unpacktest ["303"], "01", "H3"
+unpacktest ["303132"], "012", "H*"
+unpacktest ["3031", 50], "012", "H4C"
+unpacktest ["5a"], "Z", "H*"
+
+packtest [""], "\0", "h"
+packtest ["3"], "\03", "h"
+packtest ["34"], "", "h0"
+packtest ["34"], "\03", "h"
+packtest ["34"], "C", "h2"
+packtest ["34"], "C\0", "h3"
+packtest ["3456"], "C\05", "h3"
+packtest ["34563"], "Ce\03", "h*"
+
+packtest   [""],    " ",   "A"
+unpacktest [""],    "",    "A"
+pptest     ["1"],   "1",   "A"
+pptest     ["1"],   "1 ",  "A2"
+unpacktest ["1"],   "1",   "A2"
+unpacktest ["1"],   "1 ",  "A2"
+unpacktest ["1"],   "1\0", "A2"
+packtest   ["12"],  "1",   "A"
+unpacktest ["1"],   "12",  "A"
+pptest     ["123"], "123", "A*"
+packtest   ["1","2"], "2", "A0A"
+unpacktest ["","2"],  "2", "A0A"
+
+packtest   [""],    "\0",  "a"
+unpacktest [""],    "",    "a"
+pptest     ["1"],   "1",   "a"
+pptest     ["1 "],  "1 ",  "a2"
+pptest     ["1\0"], "1\0", "a2"
+packtest   ["1"],   "1\0", "a2"
+pptest     ["123"], "123", "a*"
+
+packtest   [""],    "\0",    "Z"
+unpacktest [""],    "",      "Z"
+pptest     ["1"],   "1",     "Z"
+pptest     ["1"],   "1\0",   "Z2"
+pptest     ["1 "],  "1 ",    "Z2"
+pptest     ["123"], "123\0", "Z*"
+pptest     ["1","2"], "12",      "ZZ"
+pptest     ["1","2"], "1\0002",  "Z*Z"
+unpacktest ["1","3"], "1\00023", "Z3Z"
+
+packtest   [1, 2], "\x01\x02", "CyC"
+
+packtest   [65], "A", 'U'
+packtest   [59411], "\xEE\xA0\x93", 'U'
+
+pptest     [1], "\x00\x01", "xC"
+unpacktest [2], "\xcc\x02", "xC"
diff --git a/third-party/mruby/mrbgems/mruby-pack/run_test.rb b/third-party/mruby/mrbgems/mruby-pack/run_test.rb
new file mode 100644 (file)
index 0000000..d9566a2
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/env ruby
+#
+# mrbgems test runner
+#
+
+gemname = File.basename(File.dirname(File.expand_path __FILE__))
+
+if __FILE__ == $0
+  repository, dir = 'https://github.com/mruby/mruby.git', 'tmp/mruby'
+  build_args = ARGV
+  build_args = ['all', 'test']  if build_args.nil? or build_args.empty?
+
+  Dir.mkdir 'tmp'  unless File.exist?('tmp')
+  unless File.exist?(dir)
+    system "git clone #{repository} #{dir}"
+  end
+
+  exit system(%Q[cd #{dir}; MRUBY_CONFIG=#{File.expand_path __FILE__} ruby minirake #{build_args.join(' ')}])
+end
+
+MRuby::Build.new do |conf|
+  toolchain :gcc
+  conf.gembox 'default'
+
+  conf.gem File.expand_path(File.dirname(__FILE__))
+end
diff --git a/third-party/mruby/mrbgems/mruby-pack/src/pack.c b/third-party/mruby/mrbgems/mruby-pack/src/pack.c
new file mode 100644 (file)
index 0000000..d96ef10
--- /dev/null
@@ -0,0 +1,1291 @@
+/*
+ ** pack.c - Array#pack, String#unpack
+ */
+
+#include "mruby.h"
+#include "error.h"
+#include "mruby/array.h"
+#include "mruby/class.h"
+#include "mruby/numeric.h"
+#include "mruby/string.h"
+#include "mruby/variable.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+struct tmpl {
+  mrb_value str;
+  int idx;
+};
+
+enum {
+  PACK_DIR_CHAR,      /* C */
+  PACK_DIR_SHORT,     /* S */
+  PACK_DIR_LONG,      /* L */
+  PACK_DIR_QUAD,      /* Q */
+  //PACK_DIR_INT,     /* i */
+  //PACK_DIR_VAX,
+  PACK_DIR_UTF8,      /* U */
+  //PACK_DIR_BER,
+  PACK_DIR_DOUBLE,    /* E */
+  PACK_DIR_FLOAT,     /* f */
+  PACK_DIR_STR,       /* A */
+  PACK_DIR_HEX,       /* h */
+  PACK_DIR_BASE64,    /* m */
+  PACK_DIR_NUL,       /* x */
+  PACK_DIR_INVALID
+};
+
+enum {
+  PACK_TYPE_INTEGER,
+  PACK_TYPE_FLOAT,
+  PACK_TYPE_STRING,
+  PACK_TYPE_NONE
+};
+
+#define PACK_FLAG_s             0x00000001     /* native size ("_" "!") */
+#define PACK_FLAG_a             0x00000002     /* null padding ("a") */
+#define PACK_FLAG_Z             0x00000004     /* append nul char ("z") */
+#define PACK_FLAG_SIGNED        0x00000008     /* native size ("_" "!") */
+#define PACK_FLAG_GT            0x00000010     /* big endian (">") */
+#define PACK_FLAG_LT            0x00000020     /* little endian ("<") */
+#define PACK_FLAG_WIDTH         0x00000040     /* "count" is "width" */
+#define PACK_FLAG_LSB           0x00000080     /* LSB / low nibble first */
+#define PACK_FLAG_COUNT2        0x00000100     /* "count" is special... */
+#define PACK_FLAG_LITTLEENDIAN  0x00000200     /* little endian actually */
+
+#define PACK_BASE64_IGNORE     0xff
+#define PACK_BASE64_PADDING    0xfe
+
+static int littleendian = 0;
+
+const static unsigned char base64chars[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static signed char base64_dec_tab[128];
+
+
+static int
+check_little_endian(void)
+{
+  unsigned int n = 1;
+  return (*(unsigned char *)&n == 1);
+}
+
+static unsigned int
+hex2int(unsigned char ch)
+{
+  if (ch >= '0' && ch <= '9')
+    return ch - '0';
+  else if (ch >= 'A' && ch <= 'F')
+    return 10 + (ch - 'A');
+  else if (ch >= 'a' && ch <= 'f')
+    return 10 + (ch - 'a');
+  else
+    return 0;
+}
+
+static void
+make_base64_dec_tab(void)
+{
+  int i;
+  memset(base64_dec_tab, PACK_BASE64_IGNORE, sizeof(base64_dec_tab));
+  for (i = 0; i < 26; i++)
+    base64_dec_tab['A' + i] = i;
+  for (i = 0; i < 26; i++)
+    base64_dec_tab['a' + i] = i + 26;
+  for (i = 0; i < 10; i++)
+    base64_dec_tab['0' + i] = i + 52;
+  base64_dec_tab['+'] = 62;
+  base64_dec_tab['/'] = 63;
+  base64_dec_tab['='] = PACK_BASE64_PADDING;
+}
+
+static mrb_value
+str_len_ensure(mrb_state *mrb, mrb_value str, mrb_int len)
+{
+  mrb_int n = RSTRING_LEN(str);
+  if (len < 0) {
+    mrb_raise(mrb, E_RANGE_ERROR, "negative (or overflowed) integer");
+  }
+  if (len > n) {
+    do {
+      n *= 2;
+    } while (len > n);
+    str = mrb_str_resize(mrb, str, n);
+  }
+  return str;
+}
+
+
+static int
+pack_c(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
+{
+  str = str_len_ensure(mrb, str, sidx + 1);
+  RSTRING_PTR(str)[sidx] = (char)mrb_fixnum(o);
+  return 1;
+}
+
+static int
+unpack_c(mrb_state *mrb, const void *src, int srclen, mrb_value ary, unsigned int flags)
+{
+  if (flags & PACK_FLAG_SIGNED)
+    mrb_ary_push(mrb, ary, mrb_fixnum_value(*(signed char *)src));
+  else
+    mrb_ary_push(mrb, ary, mrb_fixnum_value(*(unsigned char *)src));
+  return 1;
+}
+
+static int
+pack_s(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
+{
+  uint16_t n;
+
+  str = str_len_ensure(mrb, str, sidx + 2);
+  n = (uint16_t)mrb_fixnum(o);
+  if (flags & PACK_FLAG_LITTLEENDIAN) {
+    RSTRING_PTR(str)[sidx+0] = n % 256;
+    RSTRING_PTR(str)[sidx+1] = n / 256;
+  } else {
+    RSTRING_PTR(str)[sidx+0] = n / 256;
+    RSTRING_PTR(str)[sidx+1] = n % 256;
+  }
+  return 2;
+}
+
+static int
+unpack_s(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, unsigned int flags)
+{
+  int n;
+
+  if (flags & PACK_FLAG_LITTLEENDIAN) {
+    n = src[1] * 256 + src[0];
+  } else {
+    n = src[0] * 256 + src[1];
+  }
+  if ((flags & PACK_FLAG_SIGNED) && (n >= 0x8000)) {
+    n -= 0x10000;
+  }
+  mrb_ary_push(mrb, ary, mrb_fixnum_value(n));
+  return 2;
+}
+
+static int
+pack_l(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
+{
+  uint32_t n;
+
+  str = str_len_ensure(mrb, str, sidx + 4);
+  n = (uint32_t)mrb_fixnum(o);
+  if (flags & PACK_FLAG_LITTLEENDIAN) {
+    RSTRING_PTR(str)[sidx+0] = (char)(n & 0xff);
+    RSTRING_PTR(str)[sidx+1] = (char)(n >> 8);
+    RSTRING_PTR(str)[sidx+2] = (char)(n >> 16);
+    RSTRING_PTR(str)[sidx+3] = (char)(n >> 24);
+  } else {
+    RSTRING_PTR(str)[sidx+0] = (char)(n >> 24);
+    RSTRING_PTR(str)[sidx+1] = (char)(n >> 16);
+    RSTRING_PTR(str)[sidx+2] = (char)(n >> 8);
+    RSTRING_PTR(str)[sidx+3] = (char)(n & 0xff);
+  }
+  return 4;
+}
+
+static int
+unpack_l(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, unsigned int flags)
+{
+#ifndef MRB_INT64
+  char msg[60];
+#endif
+  uint32_t ul;
+  mrb_int n;
+
+  if (flags & PACK_FLAG_LITTLEENDIAN) {
+    ul = (uint32_t)src[3] * 256*256*256;
+    ul += (uint32_t)src[2] *256*256;
+    ul += (uint32_t)src[1] *256;
+    ul += (uint32_t)src[0];
+  } else {
+    ul = (uint32_t)src[0] * 256*256*256;
+    ul += (uint32_t)src[1] *256*256;
+    ul += (uint32_t)src[2] *256;
+    ul += (uint32_t)src[3];
+  }
+  if (flags & PACK_FLAG_SIGNED) {
+    int32_t sl = ul;
+#ifndef MRB_INT64
+    if (!FIXABLE(sl)) {
+      snprintf(msg, sizeof(msg), "cannot unpack to Fixnum: %ld", (long)sl);
+      mrb_raise(mrb, E_RANGE_ERROR, msg);
+    }
+#endif
+    n = sl;
+  } else {
+#ifndef MRB_INT64
+    if (!POSFIXABLE(ul)) {
+      snprintf(msg, sizeof(msg), "cannot unpack to Fixnum: %lu", (unsigned long)ul);
+      mrb_raise(mrb, E_RANGE_ERROR, msg);
+    }
+#endif
+    n = ul;
+  }
+  mrb_ary_push(mrb, ary, mrb_fixnum_value(n));
+  return 4;
+}
+
+static int
+pack_q(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
+{
+  uint64_t n;
+
+  str = str_len_ensure(mrb, str, sidx + 8);
+  n = (uint64_t)mrb_fixnum(o);
+  if (flags & PACK_FLAG_LITTLEENDIAN) {
+    RSTRING_PTR(str)[sidx+0] = (char)(n & 0xff);
+    RSTRING_PTR(str)[sidx+1] = (char)(n >> 8);
+    RSTRING_PTR(str)[sidx+2] = (char)(n >> 16);
+    RSTRING_PTR(str)[sidx+3] = (char)(n >> 24);
+    RSTRING_PTR(str)[sidx+4] = (char)(n >> 32);
+    RSTRING_PTR(str)[sidx+5] = (char)(n >> 40);
+    RSTRING_PTR(str)[sidx+6] = (char)(n >> 48);
+    RSTRING_PTR(str)[sidx+7] = (char)(n >> 56);
+  } else {
+    RSTRING_PTR(str)[sidx+0] = (char)(n >> 56);
+    RSTRING_PTR(str)[sidx+1] = (char)(n >> 48);
+    RSTRING_PTR(str)[sidx+2] = (char)(n >> 40);
+    RSTRING_PTR(str)[sidx+3] = (char)(n >> 32);
+    RSTRING_PTR(str)[sidx+4] = (char)(n >> 24);
+    RSTRING_PTR(str)[sidx+5] = (char)(n >> 16);
+    RSTRING_PTR(str)[sidx+6] = (char)(n >> 8);
+    RSTRING_PTR(str)[sidx+7] = (char)(n & 0xff);
+  }
+  return 8;
+}
+
+static int
+unpack_q(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, unsigned int flags)
+{
+  char msg[60];
+  uint64_t ull;
+  int i, pos, step;
+  mrb_int n;
+
+  if (flags & PACK_FLAG_LITTLEENDIAN) {
+    pos  = 7;
+    step = -1;
+  } else {
+    pos  = 0;
+    step = 1;
+  }
+  ull = 0;
+  for (i = 0; i < 8; i++) {
+    ull = ull * 256 + (uint64_t)src[pos];
+    pos += step;
+  }
+  if (flags & PACK_FLAG_SIGNED) {
+    int64_t sll = ull;
+    if (!FIXABLE(sll)) {
+      snprintf(msg, sizeof(msg), "cannot unpack to Fixnum: %lld", (long long)sll);
+      mrb_raise(mrb, E_RANGE_ERROR, msg);
+    }
+    n = sll;
+  } else {
+    if (!POSFIXABLE(ull)) {
+      snprintf(msg, sizeof(msg), "cannot unpack to Fixnum: %llu", (unsigned long long)ull);
+      mrb_raise(mrb, E_RANGE_ERROR, msg);
+    }
+    n = ull;
+  }
+  mrb_ary_push(mrb, ary, mrb_fixnum_value(n));
+  return 8;
+}
+
+#ifndef MRB_WITHOUT_FLOAT
+static int
+pack_double(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
+{
+  int i;
+  double d;
+  uint8_t *buffer = (uint8_t *)&d;
+  str = str_len_ensure(mrb, str, sidx + 8);
+  d = mrb_float(o);
+
+  if (flags & PACK_FLAG_LITTLEENDIAN) {
+#ifdef MRB_ENDIAN_BIG
+    for (i = 0; i < 8; ++i) {
+      RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1];
+    }
+#else
+    memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
+#endif
+  } else {
+#ifdef MRB_ENDIAN_BIG
+    memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
+#else
+    for (i = 0; i < 8; ++i) {
+      RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1];
+    }
+#endif
+  }
+
+  return 8;
+}
+
+static int
+unpack_double(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ary, unsigned int flags)
+{
+  int i;
+  double d;
+  uint8_t *buffer = (uint8_t *)&d;
+
+  if (flags & PACK_FLAG_LITTLEENDIAN) {
+#ifdef MRB_ENDIAN_BIG
+    for (i = 0; i < 8; ++i) {
+      buffer[8 - i - 1] = src[i];
+    }
+#else
+    memcpy(buffer, src, 8);
+#endif
+  } else {
+#ifdef MRB_ENDIAN_BIG
+    memcpy(buffer, src, 8);
+#else
+    for (i = 0; i < 8; ++i) {
+      buffer[8 - i - 1] = src[i];
+    }
+#endif
+  }
+  mrb_ary_push(mrb, ary, mrb_float_value(mrb, d));
+
+  return 8;
+}
+
+static int
+pack_float(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
+{
+  int i;
+  float f;
+  uint8_t *buffer = (uint8_t *)&f;
+  str = str_len_ensure(mrb, str, sidx + 4);
+  f = (float)mrb_float(o);
+
+  if (flags & PACK_FLAG_LITTLEENDIAN) {
+#ifdef MRB_ENDIAN_BIG
+    for (i = 0; i < 4; ++i) {
+      RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1];
+    }
+#else
+    memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
+#endif
+  } else {
+#ifdef MRB_ENDIAN_BIG
+    memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
+#else
+    for (i = 0; i < 4; ++i) {
+      RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1];
+    }
+#endif
+  }
+
+  return 4;
+}
+
+static int
+unpack_float(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ary, unsigned int flags)
+{
+  int i;
+  float f;
+  uint8_t *buffer = (uint8_t *)&f;
+
+  if (flags & PACK_FLAG_LITTLEENDIAN) {
+#ifdef MRB_ENDIAN_BIG
+    for (i = 0; i < 4; ++i) {
+      buffer[4 - i - 1] = src[i];
+    }
+#else
+    memcpy(buffer, src, 4);
+#endif
+  } else {
+#ifdef MRB_ENDIAN_BIG
+    memcpy(buffer, src, 4);
+#else
+    for (i = 0; i < 4; ++i) {
+      buffer[4 - i - 1] = src[i];
+    }
+#endif
+  }
+  mrb_ary_push(mrb, ary, mrb_float_value(mrb, f));
+
+  return 4;
+}
+#endif
+
+static int
+pack_utf8(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, long count, unsigned int flags)
+{
+  char utf8[4];
+  int len = 0;
+  uint32_t c = 0;
+
+#ifndef MRB_WITHOUT_FLOAT
+  if (mrb_float_p(o)) {
+    goto range_error;
+  }
+#endif
+  c = (uint32_t)mrb_fixnum(o);
+
+  /* Unicode character */
+  /* from mruby-compiler gem */
+  if (c < 0x80) {
+    utf8[0] = (char)c;
+    len = 1;
+  }
+  else if (c < 0x800) {
+    utf8[0] = (char)(0xC0 | (c >> 6));
+    utf8[1] = (char)(0x80 | (c & 0x3F));
+    len = 2;
+  }
+  else if (c < 0x10000) {
+    utf8[0] = (char)(0xE0 |  (c >> 12)        );
+    utf8[1] = (char)(0x80 | ((c >>  6) & 0x3F));
+    utf8[2] = (char)(0x80 | ( c        & 0x3F));
+    len = 3;
+  }
+  else if (c < 0x200000) {
+    utf8[0] = (char)(0xF0 |  (c >> 18)        );
+    utf8[1] = (char)(0x80 | ((c >> 12) & 0x3F));
+    utf8[2] = (char)(0x80 | ((c >>  6) & 0x3F));
+    utf8[3] = (char)(0x80 | ( c        & 0x3F));
+    len = 4;
+  }
+  else {
+#ifndef MRB_WITHOUT_FLOAT
+range_error:
+#endif
+    mrb_raise(mrb, E_RANGE_ERROR, "pack(U): value out of range");
+  }
+
+  str = str_len_ensure(mrb, str, sidx + len);
+  memcpy(RSTRING_PTR(str) + sidx, utf8, len);
+
+  return len;
+}
+
+static const unsigned long utf8_limits[] = {
+  0x0,        /* 1 */
+  0x80,       /* 2 */
+  0x800,      /* 3 */
+  0x10000,    /* 4 */
+  0x200000,   /* 5 */
+  0x4000000,  /* 6 */
+  0x80000000, /* 7 */
+};
+
+static unsigned long
+utf8_to_uv(mrb_state *mrb, const char *p, long *lenp)
+{
+  int c = *p++ & 0xff;
+  unsigned long uv = c;
+  long n = 1;
+
+  if (!(uv & 0x80)) {
+    *lenp = 1;
+    return uv;
+  }
+  if (!(uv & 0x40)) {
+    *lenp = 1;
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character");
+  }
+
+  if      (!(uv & 0x20)) { n = 2; uv &= 0x1f; }
+  else if (!(uv & 0x10)) { n = 3; uv &= 0x0f; }
+  else if (!(uv & 0x08)) { n = 4; uv &= 0x07; }
+  else if (!(uv & 0x04)) { n = 5; uv &= 0x03; }
+  else if (!(uv & 0x02)) { n = 6; uv &= 0x01; }
+  else {
+    *lenp = 1;
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character");
+  }
+  if (n > *lenp) {
+    mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character (expected %S bytes, given %S bytes)",
+    mrb_fixnum_value(n), mrb_fixnum_value(*lenp));
+  }
+  *lenp = n--;
+  if (n != 0) {
+    while (n--) {
+      c = *p++ & 0xff;
+      if ((c & 0xc0) != 0x80) {
+        *lenp -= n + 1;
+        mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character");
+      }
+      else {
+        c &= 0x3f;
+        uv = uv << 6 | c;
+      }
+    }
+  }
+  n = *lenp - 1;
+  if (uv < utf8_limits[n]) {
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "redundant UTF-8 sequence");
+  }
+  return uv;
+}
+
+static int
+unpack_utf8(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ary, unsigned int flags)
+{
+  unsigned long uv;
+  long lenp = srclen;
+
+  if (srclen == 0) {
+    return 1;
+  }
+  uv = utf8_to_uv(mrb, (const char *)src, &lenp);
+  mrb_ary_push(mrb, ary, mrb_fixnum_value((mrb_int)uv));
+  return (int)lenp;
+}
+
+static int
+pack_a(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags)
+{
+  mrb_int copylen, slen, padlen;
+  char *dptr, *dptr0, pad, *sptr;
+
+  sptr = RSTRING_PTR(src);
+  slen = RSTRING_LEN(src);
+
+  if ((flags & PACK_FLAG_a) || (flags & PACK_FLAG_Z))
+    pad = '\0';
+  else
+    pad = ' ';
+
+  if (count == 0) {
+    return 0;
+  } else if (count == -1) {
+    copylen = slen;
+    padlen = (flags & PACK_FLAG_Z) ? 1 : 0;
+  } else if (count < slen) {
+    copylen = count;
+    padlen = 0;
+  } else {
+    copylen = slen;
+    padlen = count - slen;
+  }
+
+  dst = str_len_ensure(mrb, dst, didx + copylen + padlen);
+  dptr0 = dptr = RSTRING_PTR(dst) + didx;
+  memcpy(dptr, sptr, copylen);
+  dptr += copylen;
+  while (padlen-- > 0) {
+    *dptr++ = pad;
+  }
+
+  return (int)(dptr - dptr0);
+}
+
+static int
+unpack_a(mrb_state *mrb, const void *src, int slen, mrb_value ary, long count, unsigned int flags)
+{
+  mrb_value dst;
+  const char *cp, *sptr;
+  int copylen;
+
+  sptr = (const char *)src;
+  if (count != -1 && count < slen)  {
+    slen = count;
+  }
+  copylen = slen;
+
+  if (flags & PACK_FLAG_Z) {  /* "Z" */
+    if ((cp = (const char *)memchr(sptr, '\0', slen)) != NULL) {
+      copylen = (int)(cp - sptr);
+      if (count == -1) {
+        slen = copylen + 1;
+      }
+    }
+  } else if (!(flags & PACK_FLAG_a)) {  /* "A" */
+    while (copylen > 0 && (sptr[copylen - 1] == '\0' || isspace(sptr[copylen - 1]))) {
+      copylen--;
+    }
+  }
+
+  dst = mrb_str_new(mrb, sptr, (mrb_int)copylen);
+  mrb_ary_push(mrb, ary, dst);
+  return slen;
+}
+
+
+static int
+pack_h(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags)
+{
+  unsigned int a, ashift, b, bshift;
+  long slen;
+  char *dptr, *dptr0, *sptr;
+
+  sptr = RSTRING_PTR(src);
+  slen = (long)RSTRING_LEN(src);
+
+  if (flags & PACK_FLAG_LSB) {
+    ashift = 0;
+    bshift = 4;
+  } else {
+    ashift = 4;
+    bshift = 0;
+  }
+
+  if (count == -1) {
+    count = slen;
+  } else if (slen > count) {
+    slen = count;
+  }
+
+  dst = str_len_ensure(mrb, dst, didx + count);
+  dptr = RSTRING_PTR(dst) + didx;
+
+  dptr0 = dptr;
+  for (; count > 0; count -= 2) {
+    a = b = 0;
+    if (slen > 0) {
+      a = hex2int(*sptr++);
+      slen--;
+    }
+    if (slen > 0) {
+      b = hex2int(*sptr++);
+      slen--;
+    }
+    *dptr++ = (a << ashift) + (b << bshift);
+  }
+
+  return (int)(dptr - dptr0);
+}
+
+static int
+unpack_h(mrb_state *mrb, const void *src, int slen, mrb_value ary, int count, unsigned int flags)
+{
+  mrb_value dst;
+  int a, ashift, b, bshift;
+  const char *sptr, *sptr0;
+  char *dptr, *dptr0;
+  const char hexadecimal[] = "0123456789abcdef";
+
+  if (flags & PACK_FLAG_LSB) {
+    ashift = 0;
+    bshift = 4;
+  } else {
+    ashift = 4;
+    bshift = 0;
+  }
+
+  sptr = (const char *)src;
+
+  if (count == -1)
+    count = slen * 2;
+
+  dst = mrb_str_new(mrb, NULL, count);
+  dptr = RSTRING_PTR(dst);
+
+  sptr0 = sptr;
+  dptr0 = dptr;
+  while (slen > 0 && count > 0) {
+    a = (*sptr >> ashift) & 0x0f;
+    b = (*sptr >> bshift) & 0x0f;
+    sptr++;
+    slen--;
+
+    *dptr++ = hexadecimal[a];
+    count--;
+
+    if (count > 0) {
+      *dptr++ = hexadecimal[b];
+      count--;
+    }
+  }
+
+  dst = mrb_str_resize(mrb, dst, dptr - dptr0);
+  mrb_ary_push(mrb, ary, dst);
+  return (int)(sptr - sptr0);
+}
+
+
+static int
+pack_m(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags)
+{
+  mrb_int dstlen;
+  unsigned long l;
+  mrb_int column, srclen;
+  char *srcptr, *dstptr, *dstptr0;
+
+  srcptr = RSTRING_PTR(src);
+  srclen = RSTRING_LEN(src);
+
+  if (srclen == 0)  /* easy case */
+    return 0;
+
+  if (count != 0 && count < 3) {  /* -1, 1 or 2 */
+    count = 45;
+  } else if (count >= 3) {
+    count -= count % 3;
+  }
+
+  dstlen = (srclen+2) / 3 * 4;
+  if (count > 0) {
+    dstlen += (srclen / count) + ((srclen % count) == 0 ? 0 : 1);
+  }
+  dst = str_len_ensure(mrb, dst, didx + dstlen);
+  dstptr = RSTRING_PTR(dst) + didx;
+
+  dstptr0 = dstptr;
+  for (column = 3; srclen >= 3; srclen -= 3, column += 3) {
+    l = (unsigned char)*srcptr++ << 16;
+    l += (unsigned char)*srcptr++ << 8;
+    l += (unsigned char)*srcptr++;
+
+    *dstptr++ = base64chars[(l >> 18) & 0x3f];
+    *dstptr++ = base64chars[(l >> 12) & 0x3f];
+    *dstptr++ = base64chars[(l >>  6) & 0x3f];
+    *dstptr++ = base64chars[ l        & 0x3f];
+
+    if (column == count) {
+      *dstptr++ = '\n';
+      column = 0;
+    }
+  }
+  if (srclen == 1) {
+    l = (unsigned char)*srcptr++ << 16;
+    *dstptr++ = base64chars[(l >> 18) & 0x3f];
+    *dstptr++ = base64chars[(l >> 12) & 0x3f];
+    *dstptr++ = '=';
+    *dstptr++ = '=';
+    column += 3;
+  } else if (srclen == 2) {
+    l = (unsigned char)*srcptr++ << 16;
+    l += (unsigned char)*srcptr++ << 8;
+    *dstptr++ = base64chars[(l >> 18) & 0x3f];
+    *dstptr++ = base64chars[(l >> 12) & 0x3f];
+    *dstptr++ = base64chars[(l >>  6) & 0x3f];
+    *dstptr++ = '=';
+    column += 3;
+  }
+  if (column > 0 && count > 0) {
+    *dstptr++ = '\n';
+  }
+
+  return (int)(dstptr - dstptr0);
+}
+
+static int
+unpack_m(mrb_state *mrb, const void *src, int slen, mrb_value ary, unsigned int flags)
+{
+  mrb_value dst;
+  int dlen;
+  unsigned long l;
+  int i, padding;
+  unsigned char c, ch[4];
+  const char *sptr, *sptr0;
+  char *dptr, *dptr0;
+
+  sptr0 = sptr = (const char *)src;
+
+  dlen = slen / 4 * 3;  /* an estimated value - may be shorter */
+  dst = mrb_str_new(mrb, NULL, dlen);
+  dptr0 = dptr = RSTRING_PTR(dst);
+
+  padding = 0;
+  while (slen >= 4) {
+    for (i = 0; i < 4; i++) {
+      do {
+        if (slen-- == 0)
+          goto done;
+        c = *sptr++;
+       if (c >= sizeof(base64_dec_tab))
+         continue;
+       ch[i] = base64_dec_tab[c];
+       if (ch[i] == PACK_BASE64_PADDING) {
+         ch[i] = 0;
+         padding++;
+       }
+      } while (c >= sizeof(base64_dec_tab) || ch[i] == PACK_BASE64_IGNORE);
+    }
+
+    l = (ch[0] << 18) + (ch[1] << 12) + (ch[2] << 6) + ch[3];
+
+    if (padding == 0) {
+      *dptr++ = (l >> 16) & 0xff;
+      *dptr++ = (l >> 8) & 0xff;
+      *dptr++ = l & 0xff;
+    } else if (padding == 1) {
+      *dptr++ = (l >> 16) & 0xff;
+      *dptr++ = (l >> 8) & 0xff;
+      break;
+    } else {
+      *dptr++ = (l >> 16) & 0xff;
+      break;
+    }
+  }
+
+done:
+  dst = mrb_str_resize(mrb, dst, dptr - dptr0);
+  mrb_ary_push(mrb, ary, dst);
+  return (int)(sptr - sptr0);
+}
+
+static int
+pack_x(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags)
+{
+  long i;
+
+  if (count < 0) return 0;
+  dst = str_len_ensure(mrb, dst, didx + count);
+  for (i = 0; i < count; i++) {
+    RSTRING_PTR(dst)[didx + i] = '\0';
+  }
+  return count;
+}
+static int
+unpack_x(mrb_state *mrb, const void *src, int slen, mrb_value ary, int count, unsigned int flags)
+{
+  if (count < 0) return slen;
+  if (slen < count) {
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "x outside of string");
+  }
+  return count;
+}
+
+static void
+prepare_tmpl(mrb_state *mrb, struct tmpl *tmpl)
+{
+  mrb_get_args(mrb, "S", &tmpl->str);
+  tmpl->idx = 0;
+}
+
+static int
+has_tmpl(const struct tmpl *tmpl)
+{
+  return (tmpl->idx < RSTRING_LEN(tmpl->str));
+}
+
+static void
+read_tmpl(mrb_state *mrb, struct tmpl *tmpl, int *dirp, int *typep, int *sizep, int *countp, unsigned int *flagsp)
+{
+  mrb_int t, tlen;
+  int ch, dir, type, size = 0;
+  int count = 1;
+  unsigned int flags = 0;
+  const char *tptr;
+
+  tptr = RSTRING_PTR(tmpl->str);
+  tlen = RSTRING_LEN(tmpl->str);
+
+  t = tptr[tmpl->idx++];
+alias:
+  switch (t) {
+  case 'A':
+    dir = PACK_DIR_STR;
+    type = PACK_TYPE_STRING;
+    flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2;
+    break;
+  case 'a':
+    dir = PACK_DIR_STR;
+    type = PACK_TYPE_STRING;
+    flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2 | PACK_FLAG_a;
+    break;
+  case 'C':
+    dir = PACK_DIR_CHAR;
+    type = PACK_TYPE_INTEGER;
+    size = 1;
+    break;
+  case 'c':
+    dir = PACK_DIR_CHAR;
+    type = PACK_TYPE_INTEGER;
+    size = 1;
+    flags |= PACK_FLAG_SIGNED;
+    break;
+  case 'D': case 'd':
+    dir = PACK_DIR_DOUBLE;
+    type = PACK_TYPE_FLOAT;
+    size = 8;
+    flags |= PACK_FLAG_SIGNED;
+    break;
+  case 'F': case 'f':
+    dir = PACK_DIR_FLOAT;
+    type = PACK_TYPE_FLOAT;
+    size = 4;
+    flags |= PACK_FLAG_SIGNED;
+    break;
+  case 'E':
+    dir = PACK_DIR_DOUBLE;
+    type = PACK_TYPE_FLOAT;
+    size = 8;
+    flags |= PACK_FLAG_SIGNED | PACK_FLAG_LT;
+    break;
+  case 'e':
+    dir = PACK_DIR_FLOAT;
+    type = PACK_TYPE_FLOAT;
+    size = 4;
+    flags |= PACK_FLAG_SIGNED | PACK_FLAG_LT;
+    break;
+  case 'G':
+    dir = PACK_DIR_DOUBLE;
+    type = PACK_TYPE_FLOAT;
+    size = 8;
+    flags |= PACK_FLAG_SIGNED | PACK_FLAG_GT;
+    break;
+  case 'g':
+    dir = PACK_DIR_FLOAT;
+    type = PACK_TYPE_FLOAT;
+    size = 4;
+    flags |= PACK_FLAG_SIGNED | PACK_FLAG_GT;
+    break;
+  case 'H':
+    dir = PACK_DIR_HEX;
+    type = PACK_TYPE_STRING;
+    flags |= PACK_FLAG_COUNT2;
+    break;
+  case 'h':
+    dir = PACK_DIR_HEX;
+    type = PACK_TYPE_STRING;
+    flags |= PACK_FLAG_COUNT2 | PACK_FLAG_LSB;
+    break;
+  case 'I':
+    switch (sizeof(int)) {
+      case 2: t = 'S'; goto alias;
+      case 4: t = 'L'; goto alias;
+      case 8: t = 'Q'; goto alias;
+      default:
+        mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(int) == %S", mrb_fixnum_value(sizeof(int)));
+    }
+    break;
+  case 'i':
+    switch (sizeof(int)) {
+      case 2: t = 's'; goto alias;
+      case 4: t = 'l'; goto alias;
+      case 8: t = 'q'; goto alias;
+      default:
+        mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(int) == %S", mrb_fixnum_value(sizeof(int)));
+    }
+    break;
+  case 'L':
+    dir = PACK_DIR_LONG;
+    type = PACK_TYPE_INTEGER;
+    size = 4;
+    break;
+  case 'l':
+    dir = PACK_DIR_LONG;
+    type = PACK_TYPE_INTEGER;
+    size = 4;
+    flags |= PACK_FLAG_SIGNED;
+    break;
+  case 'm':
+    dir = PACK_DIR_BASE64;
+    type = PACK_TYPE_STRING;
+    flags |= PACK_FLAG_WIDTH;
+    break;
+  case 'N':  /* = "L>" */
+    dir = PACK_DIR_LONG;
+    type = PACK_TYPE_INTEGER;
+    size = 4;
+    flags |= PACK_FLAG_GT;
+    break;
+  case 'n':  /* = "S>" */
+    dir = PACK_DIR_SHORT;
+    type = PACK_TYPE_INTEGER;
+    size = 2;
+    flags |= PACK_FLAG_GT;
+    break;
+  case 'Q':
+    dir = PACK_DIR_QUAD;
+    type = PACK_TYPE_INTEGER;
+    size = 8;
+    break;
+  case 'q':
+    dir = PACK_DIR_QUAD;
+    type = PACK_TYPE_INTEGER;
+    size = 8;
+    flags |= PACK_FLAG_SIGNED;
+    break;
+  case 'S':
+    dir = PACK_DIR_SHORT;
+    type = PACK_TYPE_INTEGER;
+    size = 2;
+    break;
+  case 's':
+    dir = PACK_DIR_SHORT;
+    type = PACK_TYPE_INTEGER;
+    size = 2;
+    flags |= PACK_FLAG_SIGNED;
+    break;
+  case 'U':
+    dir = PACK_DIR_UTF8;
+    type = PACK_TYPE_INTEGER;
+    break;
+  case 'V':  /* = "L<" */
+    dir = PACK_DIR_LONG;
+    type = PACK_TYPE_INTEGER;
+    size = 4;
+    flags |= PACK_FLAG_LT;
+    break;
+  case 'v':  /* = "S<" */
+    dir = PACK_DIR_SHORT;
+    type = PACK_TYPE_INTEGER;
+    size = 2;
+    flags |= PACK_FLAG_LT;
+    break;
+  case 'x':
+    dir = PACK_DIR_NUL;
+    type = PACK_TYPE_NONE;
+    break;
+  case 'Z':
+    dir = PACK_DIR_STR;
+    type = PACK_TYPE_STRING;
+    flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2 | PACK_FLAG_Z;
+    break;
+  default:
+    dir = PACK_DIR_INVALID;
+    type = PACK_TYPE_NONE;
+    break;
+  }
+
+  /* read suffix [0-9*_!<>] */
+  while (tmpl->idx < tlen) {
+    ch = tptr[tmpl->idx++];
+    if (isdigit(ch)) {
+      count = ch - '0';
+      while (tmpl->idx < tlen && isdigit(tptr[tmpl->idx])) {
+        count = count * 10 + (tptr[tmpl->idx++] - '0');
+        if (count < 0) {
+          mrb_raisef(mrb, E_RUNTIME_ERROR, "too big template length");
+        }
+      }
+      continue;  /* special case */
+    } else if (ch == '*')  {
+      count = -1;
+    } else if (ch == '_' || ch == '!' || ch == '<' || ch == '>') {
+      if (strchr("sSiIlLqQ", (int)t) == NULL) {
+        char ch_str = (char)ch;
+        mrb_raisef(mrb, E_ARGUMENT_ERROR, "'%S' allowed only after types sSiIlLqQ", mrb_str_new(mrb, &ch_str, 1));
+      }
+      if (ch == '_' || ch == '!') {
+        flags |= PACK_FLAG_s;
+      } else if (ch == '<') {
+        flags |= PACK_FLAG_LT;
+      } else if (ch == '>') {
+        flags |= PACK_FLAG_GT;
+      }
+    } else {
+      tmpl->idx--;
+      break;
+    }
+  }
+
+  if ((flags & PACK_FLAG_LT) || (!(flags & PACK_FLAG_GT) && littleendian)) {
+    flags |= PACK_FLAG_LITTLEENDIAN;
+  }
+
+  *dirp = dir;
+  *typep = type;
+  *sizep = size;
+  *countp = count;
+  *flagsp = flags;
+}
+
+static mrb_value
+mrb_pack_pack(mrb_state *mrb, mrb_value ary)
+{
+  mrb_value o, result;
+  mrb_int aidx;
+  struct tmpl tmpl;
+  int count;
+  unsigned int flags;
+  int dir, ridx, size, type;
+
+  prepare_tmpl(mrb, &tmpl);
+
+  result = mrb_str_new(mrb, NULL, 128);  /* allocate initial buffer */
+  aidx = 0;
+  ridx = 0;
+  while (has_tmpl(&tmpl)) {
+    read_tmpl(mrb, &tmpl, &dir, &type, &size, &count, &flags);
+
+    if (dir == PACK_DIR_INVALID)
+      continue;
+    else if (dir == PACK_DIR_NUL) {
+        ridx += pack_x(mrb, mrb_nil_value(), result, ridx, count, flags);
+        continue;
+    }
+
+    for (; aidx < RARRAY_LEN(ary); aidx++) {
+      if (count == 0 && !(flags & PACK_FLAG_WIDTH))
+        break;
+
+      o = mrb_ary_ref(mrb, ary, aidx);
+      if (type == PACK_TYPE_INTEGER) {
+        o = mrb_to_int(mrb, o);
+#ifndef MRB_WITHOUT_FLOAT
+      } else if (type == PACK_TYPE_FLOAT) {
+        if (!mrb_float_p(o)) {
+          o = mrb_funcall(mrb, o, "to_f", 0);
+        }
+#endif
+      } else if (type == PACK_TYPE_STRING) {
+        if (!mrb_string_p(o)) {
+          mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into String", mrb_class_path(mrb, mrb_obj_class(mrb, o)));
+        }
+      }
+
+      switch (dir) {
+      case PACK_DIR_CHAR:
+        ridx += pack_c(mrb, o, result, ridx, flags);
+        break;
+      case PACK_DIR_SHORT:
+        ridx += pack_s(mrb, o, result, ridx, flags);
+        break;
+      case PACK_DIR_LONG:
+        ridx += pack_l(mrb, o, result, ridx, flags);
+        break;
+      case PACK_DIR_QUAD:
+        ridx += pack_q(mrb, o, result, ridx, flags);
+        break;
+      case PACK_DIR_BASE64:
+        ridx += pack_m(mrb, o, result, ridx, count, flags);
+        break;
+      case PACK_DIR_HEX:
+        ridx += pack_h(mrb, o, result, ridx, count, flags);
+        break;
+      case PACK_DIR_STR:
+        ridx += pack_a(mrb, o, result, ridx, count, flags);
+        break;
+#ifndef MRB_WITHOUT_FLOAT
+      case PACK_DIR_DOUBLE:
+        ridx += pack_double(mrb, o, result, ridx, flags);
+        break;
+      case PACK_DIR_FLOAT:
+        ridx += pack_float(mrb, o, result, ridx, flags);
+        break;
+#endif
+      case PACK_DIR_UTF8:
+        ridx += pack_utf8(mrb, o, result, ridx, count, flags);
+        break;
+      default:
+        break;
+      }
+      if (dir == PACK_DIR_STR) { /* always consumes 1 entry */
+        aidx++;
+        break;
+      }
+      if (count > 0) {
+        count--;
+      }
+    }
+    if (ridx < 0) {
+      mrb_raise(mrb, E_RANGE_ERROR, "negative (or overflowed) template size");
+    }
+  }
+
+  mrb_str_resize(mrb, result, ridx);
+  return result;
+}
+
+static mrb_value
+mrb_pack_unpack(mrb_state *mrb, mrb_value str)
+{
+  mrb_value result;
+  struct tmpl tmpl;
+  int count;
+  unsigned int flags;
+  int dir, size, type;
+  int srcidx, srclen;
+  const unsigned char *sptr;
+
+  prepare_tmpl(mrb, &tmpl);
+
+  srcidx = 0;
+  srclen = (int)RSTRING_LEN(str);
+
+  result = mrb_ary_new(mrb);
+  while (has_tmpl(&tmpl)) {
+    read_tmpl(mrb, &tmpl, &dir, &type, &size, &count, &flags);
+
+    if (dir == PACK_DIR_INVALID)
+      continue;
+    else if (dir == PACK_DIR_NUL) {
+      srcidx += unpack_x(mrb, sptr, srclen - srcidx, result, count, flags);
+      continue;
+    }
+
+    if (flags & PACK_FLAG_COUNT2) {
+      sptr = (const unsigned char *)RSTRING_PTR(str) + srcidx;
+      switch (dir) {
+      case PACK_DIR_HEX:
+        srcidx += unpack_h(mrb, sptr, srclen - srcidx, result, count, flags);
+        break;
+      case PACK_DIR_STR:
+        srcidx += unpack_a(mrb, sptr, srclen - srcidx, result, count, flags);
+        break;
+      }
+      continue;
+    }
+
+    while (count != 0) {
+      if (srclen - srcidx < size) {
+        while (count-- > 0) {
+          mrb_ary_push(mrb, result, mrb_nil_value());
+        }
+        break;
+      }
+
+      sptr = (const unsigned char*)RSTRING_PTR(str) + srcidx;
+      switch (dir) {
+      case PACK_DIR_CHAR:
+        srcidx += unpack_c(mrb, sptr, srclen - srcidx, result, flags);
+        break;
+      case PACK_DIR_SHORT:
+        srcidx += unpack_s(mrb, sptr, srclen - srcidx, result, flags);
+        break;
+      case PACK_DIR_LONG:
+        srcidx += unpack_l(mrb, sptr, srclen - srcidx, result, flags);
+        break;
+      case PACK_DIR_QUAD:
+        srcidx += unpack_q(mrb, sptr, srclen - srcidx, result, flags);
+        break;
+      case PACK_DIR_BASE64:
+        srcidx += unpack_m(mrb, sptr, srclen - srcidx, result, flags);
+        break;
+#ifndef MRB_WITHOUT_FLOAT
+      case PACK_DIR_FLOAT:
+        srcidx += unpack_float(mrb, sptr, srclen - srcidx, result, flags);
+        break;
+      case PACK_DIR_DOUBLE:
+        srcidx += unpack_double(mrb, sptr, srclen - srcidx, result, flags);
+        break;
+#endif
+      case PACK_DIR_UTF8:
+        srcidx += unpack_utf8(mrb, sptr, srclen - srcidx, result, flags);
+        break;
+      default:
+        mrb_raise(mrb, E_RUNTIME_ERROR, "mruby-pack's bug");
+      }
+      if (count > 0) {
+        count--;
+      }
+    }
+  }
+
+  return result;
+}
+
+void
+mrb_mruby_pack_gem_init(mrb_state *mrb)
+{
+  littleendian = check_little_endian();
+  make_base64_dec_tab();
+
+  mrb_define_method(mrb, mrb->array_class, "pack", mrb_pack_pack, MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, mrb->string_class, "unpack", mrb_pack_unpack, MRB_ARGS_REQ(1));
+}
+
+void
+mrb_mruby_pack_gem_final(mrb_state *mrb)
+{
+}
diff --git a/third-party/mruby/mrbgems/mruby-pack/test/pack.rb b/third-party/mruby/mrbgems/mruby-pack/test/pack.rb
new file mode 100644 (file)
index 0000000..f518ca4
--- /dev/null
@@ -0,0 +1,165 @@
+# pack & unpack 'm' (base64)
+assert('[""].pack("m")') do
+  ary = ""
+  str = ""
+  [ary].pack("m") == str and
+  str.unpack("m") == [ary]
+end
+
+assert('["\0"].pack("m")') do
+  ary = "\0"
+  str = "AA==\n"
+  [ary].pack("m") == str and
+  str.unpack("m") == [ary]
+end
+
+assert('["\0\0"].pack("m")') do
+  ary = "\0\0"
+  str = "AAA=\n"
+  [ary].pack("m") == str and
+  str.unpack("m") == [ary]
+end
+
+assert('["\0\0\0"].pack("m")') do
+  ary = "\0\0\0"
+  str = "AAAA\n"
+  [ary].pack("m") == str and
+  str.unpack("m") == [ary]
+end
+
+assert('["abc..xyzABC..XYZ"].pack("m")') do
+  ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"].pack("m") == "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n"
+end
+
+assert('"YWJ...".unpack("m") should "abc..xyzABC..XYZ"') do
+  str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+  "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n".unpack("m") == [str] and
+  "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==\n".unpack("m") == [str]
+end
+
+# pack & unpack 'H'
+assert('["3031"].pack("H*")') do
+  ary = "3031"
+  str = "01"
+  [ary].pack("H*") == str and
+  str.unpack("H*") == [ary]
+end
+
+assert('["10"].pack("H*")') do
+  ary = "10"
+  str = "\020"
+  [ary].pack("H*") == str and
+  str.unpack("H*") == [ary]
+end
+
+assert('[0,1,127,128,255].pack("C*")') do
+ ary = [ 0, 1, 127, 128, 255 ]
+ str = "\x00\x01\x7F\x80\xFF"
+ ary.pack("C*") == str and str.unpack("C*") == ary
+end
+
+# pack "a"
+assert('["abc"].pack("a")') do
+  ["abc"].pack("a") == "a" and
+  ["abc"].pack("a*") == "abc" and
+  ["abc"].pack("a4") == "abc\0"
+end
+
+# upack "a"
+assert('["abc"].pack("a")') do
+  "abc\0".unpack("a4") == ["abc\0"] and
+  "abc ".unpack("a4") == ["abc "]
+end
+
+# pack "A"
+assert('["abc"].pack("A")') do
+  ["abc"].pack("A") == "a" and
+  ["abc"].pack("A*") == "abc" and
+  ["abc"].pack("A4") == "abc "
+end
+
+# upack "A"
+assert('["abc"].pack("A")') do
+  "abc\0".unpack("A4") == ["abc"] and
+  "abc ".unpack("A4") == ["abc"]
+end
+
+# regression tests
+assert('issue #1') do
+  [1, 2].pack("nn") == "\000\001\000\002"
+end
+
+def assert_pack tmpl, packed, unpacked
+  assert_equal packed, unpacked.pack(tmpl)
+  assert_equal unpacked, packed.unpack(tmpl)
+end
+
+PACK_IS_LITTLE_ENDIAN = "\x01\00".unpack('S')[0] == 0x01
+
+assert 'pack float' do
+  assert_pack 'e', "\x00\x00@@", [3.0]
+  assert_pack 'g', "@@\x00\x00", [3.0]
+
+  if PACK_IS_LITTLE_ENDIAN
+    assert_pack 'f', "\x00\x00@@", [3.0]
+    assert_pack 'F', "\x00\x00@@", [3.0]
+  else
+    assert_pack 'f', "@@\x00\x00", [3.0]
+    assert_pack 'F', "@@\x00\x00", [3.0]
+  end
+end
+
+assert 'pack double' do
+  assert_pack 'E', "\x00\x00\x00\x00\x00\x00\b@", [3.0]
+  assert_pack 'G', "@\b\x00\x00\x00\x00\x00\x00", [3.0]
+
+  if PACK_IS_LITTLE_ENDIAN
+    assert_pack 'd', "\x00\x00\x00\x00\x00\x00\b@", [3.0]
+    assert_pack 'D', "\x00\x00\x00\x00\x00\x00\b@", [3.0]
+  else
+    assert_pack 'd', "@\b\x00\x00\x00\x00\x00\x00", [3.0]
+    assert_pack 'D', "@\b\x00\x00\x00\x00\x00\x00", [3.0]
+  end
+end
+
+assert 'pack/unpack "i"' do
+  int_size = [0].pack('i').size
+  raise "pack('i').size is too small (#{int_size})" if int_size < 2
+
+  if PACK_IS_LITTLE_ENDIAN
+    str = "\xC7\xCF" + "\xFF" * (int_size-2)
+  else
+    str = "\xFF" * (int_size-2) + "\xC7\xCF"
+  end
+  assert_pack 'i', str, [-12345]
+end
+
+assert 'pack/unpack "I"' do
+  uint_size = [0].pack('I').size
+  raise "pack('I').size is too small (#{uint_size})" if uint_size < 2
+
+  if PACK_IS_LITTLE_ENDIAN
+    str = "\x39\x30" + "\0" * (uint_size-2)
+  else
+    str = "\0" * (uint_size-2) + "\x39\x30"
+  end
+  assert_pack 'I', str, [12345]
+end
+
+assert 'pack/unpack "U"' do
+  assert_equal [], "".unpack("U")
+  assert_equal [], "".unpack("U*")
+  assert_equal [65, 66], "ABC".unpack("U2")
+  assert_equal [12371, 12435, 12395, 12385, 12399, 19990, 30028], "こんにちは世界".unpack("U*")
+
+  assert_equal "", [].pack("U")
+  assert_equal "", [].pack("U*")
+  assert_equal "AB", [65, 66, 67].pack("U2")
+  assert_equal "こんにちは世界", [12371, 12435, 12395, 12385, 12399, 19990, 30028].pack("U*")
+
+  assert_equal "\000", [0].pack("U")
+
+  assert_raise(RangeError) { [-0x40000000].pack("U") }
+  assert_raise(RangeError) { [-1].pack("U") }
+  assert_raise(RangeError) { [0x40000000].pack("U") }
+end
index fd8d152..e181b06 100644 (file)
@@ -19,7 +19,7 @@ printstr(mrb_state *mrb, mrb_value obj)
 #if defined(_WIN32)
     if (isatty(fileno(stdout))) {
       DWORD written;
-      int mlen = RSTRING_LEN(obj);
+      int mlen = (int)RSTRING_LEN(obj);
       char* utf8 = RSTRING_PTR(obj);
       int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, NULL, 0);
       wchar_t* utf16 = (wchar_t*)mrb_malloc(mrb, (wlen+1) * sizeof(wchar_t));
@@ -33,6 +33,7 @@ printstr(mrb_state *mrb, mrb_value obj)
     } else
 #endif
       fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stdout);
+    fflush(stdout);
   }
 }
 
index a77b68e..7072fe2 100644 (file)
@@ -13,10 +13,13 @@ proc_new_cfunc_with_env(mrb_state *mrb, mrb_value self)
 {
   mrb_sym n;
   mrb_value n_val;
+  mrb_method_t m;
+  struct RProc *p;
   mrb_get_args(mrb, "n", &n);
   n_val = mrb_symbol_value(n);
-  mrb_define_method_raw(mrb, mrb_class_ptr(self), n,
-                        mrb_proc_new_cfunc_with_env(mrb, return_func_name, 1, &n_val));
+  p = mrb_proc_new_cfunc_with_env(mrb, return_func_name, 1, &n_val);
+  MRB_METHOD_FROM_PROC(m, p);
+  mrb_define_method_raw(mrb, mrb_class_ptr(self), n, m);
   return self;
 }
 
@@ -33,9 +36,12 @@ cfunc_env_get(mrb_state *mrb, mrb_value self)
 {
   mrb_sym n;
   mrb_value *argv; mrb_int argc;
+  mrb_method_t m;
+  struct RProc *p;
   mrb_get_args(mrb, "na", &n, &argv, &argc);
-  mrb_define_method_raw(mrb, mrb_class_ptr(self), n,
-                        mrb_proc_new_cfunc_with_env(mrb, return_env, argc, argv));
+  p = mrb_proc_new_cfunc_with_env(mrb, return_env, argc, argv);
+  MRB_METHOD_FROM_PROC(m, p);
+  mrb_define_method_raw(mrb, mrb_class_ptr(self), n, m);
   return self;
 }
 
index 424c0bc..037d8d1 100644 (file)
@@ -66,6 +66,10 @@ end
 
 assert('Kernel#proc') do
   assert_true !proc{|a|}.lambda?
+
+  assert_raise LocalJumpError do
+    proc{ break }.call
+  end
 end
 
 assert('mrb_proc_new_cfunc_with_env') do
index bee06b5..405bd5c 100644 (file)
@@ -52,8 +52,7 @@ void mrb_random_init_genrand(mt_state *t, unsigned long s)
 {
     t->mt[0]= s & 0xffffffffUL;
     for (t->mti=1; t->mti<N; t->mti++) {
-        t->mt[t->mti] =
-           (1812433253UL * (t->mt[t->mti-1] ^ (t->mt[t->mti-1] >> 30)) + t->mti);
+        t->mt[t->mti] = (1812433253UL * (t->mt[t->mti-1] ^ (t->mt[t->mti-1] >> 30)) + t->mti);
         t->mt[t->mti] &= 0xffffffffUL;
     }
 }
@@ -111,8 +110,7 @@ void init_genrand(unsigned long s)
 {
     mt[0]= s & 0xffffffffUL;
     for (mti=1; mti<N; mti++) {
-        mt[mti] =
-           (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
+        mt[mti] = (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
         /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
         /* In the previous versions, MSBs of the seed affect   */
         /* only MSBs of the array mt[].                        */
index 3131192..aca71cc 100644 (file)
@@ -1,7 +1,6 @@
 #include <mruby.h>
 #include <mruby/range.h>
 #include <math.h>
-#include <float.h>
 
 static mrb_bool
 r_le(mrb_state *mrb, mrb_value a, mrb_value b)
diff --git a/third-party/mruby/mrbgems/mruby-socket/.travis.yml b/third-party/mruby/mrbgems/mruby-socket/.travis.yml
new file mode 100644 (file)
index 0000000..6476289
--- /dev/null
@@ -0,0 +1,4 @@
+language: c
+sudo: false
+script:
+  - "ruby run_test.rb all test"
diff --git a/third-party/mruby/mrbgems/mruby-socket/README.md b/third-party/mruby/mrbgems/mruby-socket/README.md
new file mode 100644 (file)
index 0000000..7428cfa
--- /dev/null
@@ -0,0 +1,55 @@
+mruby-socket
+============
+
+"mruby-socket" mrbgem provides BSD socket interface for mruby.
+API is compatible with CRuby's "socket" library.
+
+
+## Example
+```sh
+% vi kame.rb
+s = TCPSocket.open("www.kame.net", 80)
+s.write("GET / HTTP/1.0\r\n\r\n")
+puts s.read
+s.close
+
+% mruby kame.rb
+HTTP/1.1 200 OK
+Date: Tue, 21 May 2013 04:31:30 GMT
+...
+```
+
+## Requirement
+- [iij/mruby-io](https://github.com/iij/mruby-io) mrbgem
+- [iij/mruby-mtest](https://github.com/iij/mruby-mtest) mrgbem to run tests
+- system must have RFC3493 basic socket interface
+- and some POSIX API...
+
+## TODO
+- add missing methods
+- write more tests
+- fix possible descriptor leakage (see XXX comments)
+- `UNIXSocket#recv_io` `UNIXSocket#send_io`
+
+
+## License
+
+Copyright (c) 2013 Internet Initiative Japan Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/third-party/mruby/mrbgems/mruby-socket/mrbgem.rake b/third-party/mruby/mrbgems/mruby-socket/mrbgem.rake
new file mode 100644 (file)
index 0000000..8096815
--- /dev/null
@@ -0,0 +1,17 @@
+MRuby::Gem::Specification.new('mruby-socket') do |spec|
+  spec.license = 'MIT'
+  spec.authors = 'Internet Initiative Japan'
+  spec.summary = 'standard socket class'
+
+  spec.cc.include_paths << "#{build.root}/src"
+
+  # If Windows, use winsock
+  if ( /mswin|mingw|win32/ =~ RUBY_PLATFORM ) then
+    spec.linker.libraries << "wsock32"
+    spec.linker.libraries << "ws2_32"
+  end
+
+  spec.add_dependency('mruby-io', :core => 'mruby-io')
+  spec.add_dependency('mruby-pack', :core => 'mruby-pack')
+  # spec.add_dependency('mruby-mtest')
+end
diff --git a/third-party/mruby/mrbgems/mruby-socket/mrblib/socket.rb b/third-party/mruby/mrbgems/mruby-socket/mrblib/socket.rb
new file mode 100644 (file)
index 0000000..0e7bbdc
--- /dev/null
@@ -0,0 +1,621 @@
+class Addrinfo
+  def initialize(sockaddr, family=Socket::PF_UNSPEC, socktype=0, protocol=0)
+    @hostname = nil
+    if sockaddr.is_a? Array
+      sary = sockaddr
+      if sary[0] == 'AF_INET' || sary[0] == 'AF_INET6'
+        @sockaddr = Socket.sockaddr_in(sary[1], sary[3])
+        @hostname = sary[2]
+      elsif sary[0] == 'AF_UNIX'
+        @sockaddr = Socket.sockaddr_un(sary[1])
+      end
+    else
+      @sockaddr = sockaddr.dup
+    end
+    if family == Socket::PF_UNSPEC or family == nil
+      @family = Socket._sockaddr_family(@sockaddr)
+    else
+      @family = family
+    end
+    @socktype = socktype
+    @protocol = protocol
+    @canonname = nil
+  end
+
+  def self.foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=0, &block)
+    a = self.getaddrinfo(nodename, service, family, socktype, protocol, flags)
+    a.each { |ai| block.call(ai) }
+    a
+  end
+
+  def self.ip(host)
+    Addrinfo.new(Socket.sockaddr_in(0, host))
+  end
+
+  def self.tcp(host, port)
+    Addrinfo.getaddrinfo(host, port, nil, Socket::SOCK_STREAM, Socket::IPPROTO_TCP)[0]
+  end
+
+  def self.udp(host, port)
+    Addrinfo.getaddrinfo(host, port, nil, Socket::SOCK_DGRAM, Socket::IPPROTO_UDP)[0]
+  end
+
+  def self.unix(path, socktype=Socket::SOCK_STREAM)
+    Addrinfo.new(Socket.sockaddr_un(path), Socket::AF_UNIX, socktype)
+  end
+
+  def afamily
+    @family
+  end
+
+  #def bind
+
+  attr_reader :canonname
+
+  #def connect
+  #def connect_from
+  #def connect_to
+
+  #def family_addrinfo(host, port=nil)
+  #def getnameinfo(flags=0)
+  #  Socket.getnameinfo
+  #end
+
+  def inspect
+    if ipv4? or ipv6?
+      if @protocol == Socket::IPPROTO_TCP or (@socktype == Socket::SOCK_STREAM and @protocol == 0)
+        proto = 'TCP'
+      elsif @protocol == Socket::IPPROTO_UDP or (@socktype == Socket::SOCK_DGRAM and @protocol == 0)
+        proto = 'UDP'
+      else
+        proto = '???'
+      end
+      "#<Addrinfo: #{inspect_sockaddr} #{proto}>"
+    else
+      "#<Addrinfo: #{self.unix_path} SOCK_STREAM>"
+    end
+  end
+
+  def inspect_sockaddr
+    if ipv4?
+      a, p = ip_unpack
+      "#{a}:#{p}"
+    elsif ipv6?
+      a, p = ip_unpack
+      "[#{a}]:#{p}"
+    elsif unix?
+      unix_path
+    else
+      '???'
+    end
+  end
+
+  def ip?
+    ipv4? or ipv6?
+  end
+
+  def ip_address
+    ip_unpack[0]
+  end
+
+  def ip_port
+    ip_unpack[1]
+  end
+
+  def ip_unpack
+    h, p = getnameinfo(Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV)
+    [ h, p.to_i ]
+  end
+
+  def ipv4?
+    @family == Socket::AF_INET
+  end
+
+  #def ipv4_loopback?
+  #def ipv4_multicast?
+  #def ipv4_private?
+
+  def ipv6?
+    @family == Socket::AF_INET6
+  end
+
+  #def ipv6_loopback?
+  #def ipv6_mc_global?
+  #def ipv6_mc_linklocal?
+  #def ipv6_mc_nodelocal?
+  #def ipv6_mc_orilocal?
+  #def ipv6_mc_sitelocal?
+  #def ipv6_multicast?
+  #def ipv6_to_ipv4
+  #def ipv6_unspecified
+  #def ipv6_v4compat?
+  #def ipv6_v4mapped?
+  #def listen(backlog=5)
+
+  def pfamily
+    @family
+  end
+
+  attr_reader :protocol
+  attr_reader :socktype
+
+  def _to_array
+    case @family
+    when Socket::AF_INET
+      s = "AF_INET"
+    when Socket::AF_INET6
+      s = "AF_INET6"
+    when Socket::AF_UNIX
+      s = "AF_UNIX"
+    else
+      s = "(unknown AF)"
+    end
+    addr, port = self.getnameinfo(Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV)
+    [ s, port.to_i, addr, addr ]
+  end
+
+  def to_sockaddr
+    @sockaddr
+  end
+
+  alias to_s to_sockaddr
+
+  def unix?
+    @family == Socket::AF_UNIX
+  end
+end
+
+class BasicSocket < IO
+  @@do_not_reverse_lookup = true
+
+  def self.do_not_reverse_lookup
+    @@do_not_reverse_lookup
+  end
+
+  def self.do_not_reverse_lookup=(val)
+    @@do_not_reverse_lookup = val ? true : false
+  end
+
+  def initialize(*args)
+    super(*args)
+    self._is_socket = true
+    @do_not_reverse_lookup = @@do_not_reverse_lookup
+  end
+
+  def self.for_fd(fd)
+    super(fd, "r+")
+  end
+
+  #def connect_address
+
+  def local_address
+    Addrinfo.new self.getsockname
+  end
+
+  def recv_nonblock(maxlen, flags=0)
+    begin
+      _setnonblock(true)
+      recv(maxlen, flags)
+    ensure
+      _setnonblock(false)
+    end
+  end
+
+  def remote_address
+    Addrinfo.new self.getpeername
+  end
+
+  attr_accessor :do_not_reverse_lookup
+end
+
+class IPSocket < BasicSocket
+  def self.getaddress(host)
+    Addrinfo.ip(host).ip_address
+  end
+
+  def addr
+    Addrinfo.new(self.getsockname)._to_array
+  end
+
+  def peeraddr
+    Addrinfo.new(self.getpeername)._to_array
+  end
+
+  def recvfrom(maxlen, flags=0)
+    msg, sa = _recvfrom(maxlen, flags)
+    [ msg, Addrinfo.new(sa)._to_array ]
+  end
+end
+
+class TCPSocket < IPSocket
+  def initialize(host, service, local_host=nil, local_service=nil)
+    if @init_with_fd
+      super(host, service)
+    else
+      s = nil
+      e = SocketError
+      Addrinfo.foreach(host, service) { |ai|
+        begin
+          s = Socket._socket(ai.afamily, Socket::SOCK_STREAM, 0)
+          if local_host or local_service
+            local_host ||= (ai.afamily == Socket::AF_INET) ? "0.0.0.0" : "::"
+            local_service ||= "0"
+            bi = Addrinfo.getaddrinfo(local_host, local_service, ai.afamily, ai.socktype)[0]
+            Socket._bind(s, bi.to_sockaddr)
+          end
+          Socket._connect(s, ai.to_sockaddr)
+          super(s, "r+")
+          return
+        rescue => e0
+          e = e0
+        end
+      }
+      raise e
+    end
+  end
+
+  def self.new_with_prelude pre, *args
+    o = self._allocate
+    o.instance_eval(&pre)
+    o.initialize(*args)
+    o
+  end
+
+  #def self.gethostbyname(host)
+end
+
+class TCPServer < TCPSocket
+  def initialize(host=nil, service)
+    ai = Addrinfo.getaddrinfo(host, service, nil, nil, nil, Socket::AI_PASSIVE)[0]
+    @init_with_fd = true
+    super(Socket._socket(ai.afamily, Socket::SOCK_STREAM, 0), "r+")
+    if Socket.const_defined?(:SO_REUSEADDR)
+      self.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
+    end
+    Socket._bind(self.fileno, ai.to_sockaddr)
+    listen(5)
+    self
+  end
+
+  def accept
+    fd = self.sysaccept
+    begin
+      TCPSocket.new_with_prelude(proc { @init_with_fd = true }, fd, "r+")
+    rescue
+      IO._sysclose(fd) rescue nil
+      raise
+    end
+  end
+
+  def accept_nonblock
+    begin
+      self._setnonblock(true)
+      self.accept
+    ensure
+      self._setnonblock(false)
+    end
+  end
+
+  def listen(backlog)
+    Socket._listen(self.fileno, backlog)
+    0
+  end
+
+  def sysaccept
+    Socket._accept(self.fileno)
+  end
+end
+
+class UDPSocket < IPSocket
+  def initialize(af=Socket::AF_INET)
+    super(Socket._socket(af, Socket::SOCK_DGRAM, 0), "r+")
+    @af = af
+    self
+  end
+
+  def bind(host, port)
+    Socket._bind(self.fileno, _sockaddr_in(port, host))
+    0
+  end
+
+  def connect(host, port)
+    Socket._connect(self.fileno, _sockaddr_in(port, host))
+    0
+  end
+
+  def recvfrom_nonblock(*args)
+    s = self
+    begin
+      self._setnonblock(true)
+      self.recvfrom(*args)
+    ensure
+      # XXX: self is a SystemcallException here! (should be bug)
+      s._setnonblock(false)
+    end
+  end
+
+  def send(mesg, flags, host=nil, port=nil)
+    if port
+      super(mesg, flags, _sockaddr_in(port, host))
+    elsif host
+      super(mesg, flags, host)
+    else
+      super(mesg, flags)
+    end
+  end
+
+  def _sockaddr_in(port, host)
+    ai = Addrinfo.getaddrinfo(host, port, @af, Socket::SOCK_DGRAM)[0]
+    ai.to_sockaddr
+  end
+end
+
+class Socket < BasicSocket
+  def initialize(domain, type, protocol=0)
+    super(Socket._socket(domain, type, protocol), "r+")
+  end
+
+  #def self.accept_loop
+
+  def self.getaddrinfo(nodename, servname, family=nil, socktype=nil, protocol=nil, flags=0)
+    Addrinfo.getaddrinfo(nodename, servname, family, socktype, protocol, flags).map { |ai|
+      ary = ai._to_array
+      ary[2] = nodename
+      ary[4] = ai.afamily
+      ary[5] = ai.socktype
+      ary[6] = ai.protocol
+      ary
+    }
+  end
+
+  #def self.getnameinfo
+  #def self.ip_address_list
+
+  def self.open(*args)
+    new(args)
+  end
+
+  def self.sockaddr_in(port, host)
+    ai = Addrinfo.getaddrinfo(host, port, nil, Socket::SOCK_DGRAM)[0]
+    ai.to_sockaddr
+  end
+
+  #def self.tcp
+  #def self.tcp_server_loop
+  #def self.tcp_server_sockets
+  #def self.udp_server_loop
+  #def self.udp_server_loop_on
+  #def self.udp_server_recv
+  #def self.udp_server_sockets
+  #def self.unix(path)
+  #def self.unix_server_loop
+  #def self.unix_server_socket
+
+  def self.unpack_sockaddr_in(sa)
+    Addrinfo.new(sa).ip_unpack.reverse
+  end
+
+  def self.unpack_sockaddr_un(sa)
+    Addrinfo.new(sa).unix_path
+  end
+
+  class << self
+    alias pack_sockaddr_in sockaddr_in
+    alias pack_sockaddr_un sockaddr_un
+    alias pair socketpair
+  end
+
+  def accept
+    fd, addr = self.sysaccept
+    [ Socket.for_fd(fd), addr ]
+  end
+
+  def accept_nonblock
+    begin
+      self._setnonblock(true)
+      self.accept
+    ensure
+      self._setnonblock(false)
+    end
+  end
+
+  def bind(sockaddr)
+    sockaddr = sockaddr.to_sockaddr if sockaddr.is_a? Addrinfo
+    Socket._bind(self.fileno, sockaddr)
+    0
+  end
+
+  def connect(sockaddr)
+    sockaddr = sockaddr.to_sockaddr if sockaddr.is_a? Addrinfo
+    Socket._connect(self.fileno, sockaddr)
+    0
+  end
+
+  def connect_nonblock(sockaddr)
+    begin
+      self._setnonblock(true)
+      self.connect(sockaddr)
+    ensure
+      self._setnonblock(false)
+    end
+  end
+
+  #def ipv6only!
+
+  def listen(backlog)
+    Socket._listen(self.fileno, backlog)
+    0
+  end
+
+  def recvfrom(maxlen, flags=0)
+    msg, sa = _recvfrom(maxlen, flags)
+    socktype = self.getsockopt(Socket::SOL_SOCKET, Socket::SO_TYPE).int
+    [ msg, Addrinfo.new(sa, Socket::PF_UNSPEC, socktype) ]
+  end
+
+  def recvfrom_nonblock(*args)
+    begin
+      self._setnonblock(true)
+      self._recvfrom(*args)
+    ensure
+      self._setnonblock(false)
+    end
+  end
+
+  def sysaccept
+    Socket._accept2(self.fileno)
+  end
+end
+
+class UNIXSocket < BasicSocket
+  def initialize(path, &block)
+    if self.is_a? UNIXServer
+      super(path, "r")
+    else
+      super(Socket._socket(Socket::AF_UNIX, Socket::SOCK_STREAM, 0), "r+")
+      Socket._connect(self.fileno, Socket.sockaddr_un(path))
+
+      if block_given?
+        begin
+          yield self
+        ensure
+          begin
+            self.close unless self.closed?
+          rescue StandardError
+          end
+        end
+      end
+    end
+  end
+
+  def self.socketpair(type=Socket::SOCK_STREAM, protocol=0)
+    a = Socket.socketpair(Socket::AF_UNIX, type, protocol)
+    [ UNIXSocket.for_fd(a[0]), UNIXSocket.for_fd(a[1]) ]
+  end
+
+  class << self
+    alias pair socketpair
+  end
+
+  def addr
+    [ "AF_UNIX", path ]
+  end
+
+  def path
+    Addrinfo.new(self.getsockname).unix_path
+  end
+
+  def peeraddr
+    [ "AF_UNIX", Addrinfo.new(self.getpeername).unix_path ]
+  end
+
+  #def recv_io
+
+  def recvfrom(maxlen, flags=0)
+    msg, sa = _recvfrom(maxlen, flags)
+    path = (sa.size > 0) ? Addrinfo.new(sa).unix_path : ""
+    [ msg, [ "AF_UNIX", path ] ]
+  end
+
+  #def send_io
+end
+
+class UNIXServer < UNIXSocket
+  def initialize(path)
+    fd = Socket._socket(Socket::AF_UNIX, Socket::SOCK_STREAM, 0)
+    begin
+      super(fd)
+      Socket._bind(fd, Socket.pack_sockaddr_un(path))
+      self.listen(5)
+    rescue => e
+      IO._sysclose(fd) rescue nil
+      raise e
+    end
+
+    if block_given?
+      begin
+        yield self
+      ensure
+        self.close rescue nil unless self.closed?
+      end
+    end
+  end
+
+  def accept
+    fd = self.sysaccept
+    begin
+      sock = UNIXSocket.for_fd(fd)
+    rescue
+      IO._sysclose(fd) rescue nil
+    end
+    sock
+  end
+
+  def accept_nonblock
+    begin
+      self._setnonblock(true)
+      self.accept
+    ensure
+      self._setnonblock(false)
+    end
+  end
+
+  def listen(backlog)
+    Socket._listen(self.fileno, backlog)
+    0
+  end
+
+  def sysaccept
+    Socket._accept(self.fileno)
+  end
+end
+
+class Socket
+  include Constants
+end
+
+class Socket
+  class Option
+    def initialize(family, level, optname, data)
+      @family  = family
+      @level   = level
+      @optname = optname
+      @data    = data
+    end
+
+    def self.bool(family, level, optname, bool)
+      self.new(family, level, optname, [(bool ? 1 : 0)].pack('i'))
+    end
+
+    def self.int(family, level, optname, integer)
+      self.new(family, level, optname, [integer].pack('i'))
+    end
+
+    #def self.linger(family, level, optname, integer)
+    #end
+
+    attr_reader :data, :family, :level, :optname
+
+    def bool
+      @data.unpack('i')[0] != 0
+    end
+
+    def inspect
+      "#<Socket::Option: family:#{@family} level:#{@level} optname:#{@optname} #{@data.inspect}>"
+    end
+
+    def int
+      @data.unpack('i')[0]
+    end
+
+    def linger
+      raise NotImplementedError.new
+    end
+
+    def unpack(template)
+      raise NotImplementedError.new
+    end
+  end
+end
+
+class SocketError < StandardError; end
diff --git a/third-party/mruby/mrbgems/mruby-socket/run_test.rb b/third-party/mruby/mrbgems/mruby-socket/run_test.rb
new file mode 100644 (file)
index 0000000..87a222f
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/env ruby
+#
+# mrbgems test runner
+#
+
+if __FILE__ == $0
+  repository, dir = 'https://github.com/mruby/mruby.git', 'tmp/mruby'
+  build_args = ARGV
+
+  Dir.mkdir 'tmp'  unless File.exist?('tmp')
+  unless File.exist?(dir)
+    system "git clone #{repository} #{dir}"
+  end
+
+  exit system(%Q[cd #{dir}; MRUBY_CONFIG=#{File.expand_path __FILE__} ruby minirake #{build_args.join(' ')}])
+end
+
+MRuby::Build.new do |conf|
+  toolchain :gcc
+  conf.gembox 'default'
+
+  conf.gem :git => 'https://github.com/iij/mruby-mtest.git'
+  conf.gem :git => 'https://github.com/iij/mruby-io.git'
+  conf.gem :git => 'https://github.com/iij/mruby-pack.git'
+
+  conf.gem File.expand_path(File.dirname(__FILE__))
+  conf.enable_test
+end
diff --git a/third-party/mruby/mrbgems/mruby-socket/src/const.cstub b/third-party/mruby/mrbgems/mruby-socket/src/const.cstub
new file mode 100644 (file)
index 0000000..f176513
--- /dev/null
@@ -0,0 +1,459 @@
+#if defined(AF_INET)
+  define_const(AF_INET);
+#endif
+#if defined(PF_INET)
+  define_const(PF_INET);
+#endif
+#if defined(AF_INET6)
+  define_const(AF_INET6);
+#endif
+#if defined(PF_INET6)
+  define_const(PF_INET6);
+#endif
+#if defined(AF_LINK)
+  define_const(AF_LINK);
+#endif
+#if defined(PF_LINK)
+  define_const(PF_LINK);
+#endif
+#if defined(AF_LOCAL)
+  define_const(AF_LOCAL);
+#endif
+#if defined(PF_LOCAL)
+  define_const(PF_LOCAL);
+#endif
+#if defined(AF_UNIX)
+  define_const(AF_UNIX);
+#endif
+#if defined(PF_UNIX)
+  define_const(PF_UNIX);
+#endif
+#if defined(AF_MAX)
+  define_const(AF_MAX);
+#endif
+#if defined(AF_UNSPEC)
+  define_const(AF_UNSPEC);
+#endif
+#if defined(PF_UNSPEC)
+  define_const(PF_UNSPEC);
+#endif
+#if defined(AF_ROUTE)
+  define_const(AF_ROUTE);
+#endif
+#if defined(PF_ROUTE)
+  define_const(PF_ROUTE);
+#endif
+#if defined(AI_CANONNAME)
+  define_const(AI_CANONNAME);
+#endif
+#if defined(AI_FQDN)
+  define_const(AI_FQDN);
+#endif
+#if defined(AI_NUMERICHOST)
+  define_const(AI_NUMERICHOST);
+#endif
+#if defined(AI_NUMERICSERV)
+  define_const(AI_NUMERICSERV);
+#endif
+#if defined(AI_PASSIVE)
+  define_const(AI_PASSIVE);
+#endif
+#if defined(IP_ADD_MEMBERSHIP)
+  define_const(IP_ADD_MEMBERSHIP);
+#endif
+#if defined(IP_ADD_SOURCE_MEMBERSHIP)
+  define_const(IP_ADD_SOURCE_MEMBERSHIP);
+#endif
+#if defined(IP_BLOCK_SOURCE)
+  define_const(IP_BLOCK_SOURCE);
+#endif
+#if defined(IP_DROP_MEMBERSHIP)
+  define_const(IP_DROP_MEMBERSHIP);
+#endif
+#if defined(IP_DROP_SOURCE_MEMBERSHIP)
+  define_const(IP_DROP_SOURCE_MEMBERSHIP);
+#endif
+#if defined(IP_FREEBIND)
+  define_const(IP_FREEBIND);
+#endif
+#if defined(IP_HDRINCL)
+  define_const(IP_HDRINCL);
+#endif
+#if defined(IP_IPSEC_POLICY)
+  define_const(IP_IPSEC_POLICY);
+#endif
+#if defined(IP_MINTTL)
+  define_const(IP_MINTTL);
+#endif
+#if defined(IP_MSFILTER)
+  define_const(IP_MSFILTER);
+#endif
+#if defined(IP_MTU)
+  define_const(IP_MTU);
+#endif
+#if defined(IP_MTU_DISCOVER)
+  define_const(IP_MTU_DISCOVER);
+#endif
+#if defined(IP_MULTICAST_ALL)
+  define_const(IP_MULTICAST_ALL);
+#endif
+#if defined(IP_MULTICAST_IF)
+  define_const(IP_MULTICAST_IF);
+#endif
+#if defined(IP_MULTICAST_LOOP)
+  define_const(IP_MULTICAST_LOOP);
+#endif
+#if defined(IP_MULTICAST_TTL)
+  define_const(IP_MULTICAST_TTL);
+#endif
+#if defined(IP_OPTIONS)
+  define_const(IP_OPTIONS);
+#endif
+#if defined(IP_ORIGDSTADDR)
+  define_const(IP_ORIGDSTADDR);
+#endif
+#if defined(IP_PASSSEC)
+  define_const(IP_PASSSEC);
+#endif
+#if defined(IP_PKTINFO)
+  define_const(IP_PKTINFO);
+#endif
+#if defined(IP_PKTOPTIONS)
+  define_const(IP_PKTOPTIONS);
+#endif
+#if defined(IP_PMTUDISC_DO)
+  define_const(IP_PMTUDISC_DO);
+#endif
+#if defined(IP_PMTUDISC_DONT)
+  define_const(IP_PMTUDISC_DONT);
+#endif
+#if defined(IP_PMTUDISC_PROBE)
+  define_const(IP_PMTUDISC_PROBE);
+#endif
+#if defined(IP_PMTUDISC_WANT)
+  define_const(IP_PMTUDISC_WANT);
+#endif
+#if defined(IP_RECVDSTADDR)
+  define_const(IP_RECVDSTADDR);
+#endif
+#if defined(IP_RECVERR)
+  define_const(IP_RECVERR);
+#endif
+#if defined(IP_RECVOPTS)
+  define_const(IP_RECVOPTS);
+#endif
+#if defined(IP_RECVORIGDSTADDR)
+  define_const(IP_RECVORIGDSTADDR);
+#endif
+#if defined(IP_RECVRETOPTS)
+  define_const(IP_RECVRETOPTS);
+#endif
+#if defined(IP_RECVTOS)
+  define_const(IP_RECVTOS);
+#endif
+#if defined(IP_RECVTTL)
+  define_const(IP_RECVTTL);
+#endif
+#if defined(IP_RETOPTS)
+  define_const(IP_RETOPTS);
+#endif
+#if defined(IP_ROUTER_ALERT)
+  define_const(IP_ROUTER_ALERT);
+#endif
+#if defined(IP_TOS)
+  define_const(IP_TOS);
+#endif
+#if defined(IP_TRANSPARENT)
+  define_const(IP_TRANSPARENT);
+#endif
+#if defined(IP_TTL)
+  define_const(IP_TTL);
+#endif
+#if defined(IP_UNBLOCK_SOURCE)
+  define_const(IP_UNBLOCK_SOURCE);
+#endif
+#if defined(IP_XFRM_POLICY)
+  define_const(IP_XFRM_POLICY);
+#endif
+#if defined(IPV6_JOIN_GROUP)
+  define_const(IPV6_JOIN_GROUP);
+#endif
+#if defined(IPV6_LEAVE_GROUP)
+  define_const(IPV6_LEAVE_GROUP);
+#endif
+#if defined(IPV6_MULTICAST_HOPS)
+  define_const(IPV6_MULTICAST_HOPS);
+#endif
+#if defined(IPV6_MULTICAST_IF)
+  define_const(IPV6_MULTICAST_IF);
+#endif
+#if defined(IPV6_MULTICAST_LOOP)
+  define_const(IPV6_MULTICAST_LOOP);
+#endif
+#if defined(IPV6_UNICAST_HOPS)
+  define_const(IPV6_UNICAST_HOPS);
+#endif
+#if defined(IPV6_V6ONLY)
+  define_const(IPV6_V6ONLY);
+#endif
+#if defined(IPPROTO_AH) || defined(_WINSOCKAPI_)
+  define_const(IPPROTO_AH);
+#endif
+#if defined(IPPROTO_DSTOPTS) || defined(_WINSOCKAPI_)
+  define_const(IPPROTO_DSTOPTS);
+#endif
+#if defined(IPPROTO_ESP) || defined(_WINSOCKAPI_)
+  define_const(IPPROTO_ESP);
+#endif
+#if defined(IPPROTO_FRAGMENT) || defined(_WINSOCKAPI_)
+  define_const(IPPROTO_FRAGMENT);
+#endif
+#if defined(IPPROTO_ICMP) || defined(_WINSOCKAPI_)
+  define_const(IPPROTO_ICMP);
+#endif
+#if defined(IPPROTO_ICMPV6) || defined(_WINSOCKAPI_)
+  define_const(IPPROTO_ICMPV6);
+#endif
+#if defined(IPPROTO_IP) || defined(_WINSOCKAPI_)
+  define_const(IPPROTO_IP);
+#endif
+#if defined(IPPROTO_IPV6) || defined(_WINSOCKAPI_)
+  define_const(IPPROTO_IPV6);
+#endif
+#if defined(IPPROTO_NONE) || defined(_WINSOCKAPI_)
+  define_const(IPPROTO_NONE);
+#endif
+#if defined(IPPROTO_RAW) || defined(_WINSOCKAPI_)
+  define_const(IPPROTO_RAW);
+#endif
+#if defined(IPPROTO_ROUTING) || defined(_WINSOCKAPI_)
+  define_const(IPPROTO_ROUTING);
+#endif
+#if defined(IPPROTO_TCP) || defined(_WINSOCKAPI_)
+  define_const(IPPROTO_TCP);
+#endif
+#if defined(IPPROTO_UDP) || defined(_WINSOCKAPI_)
+  define_const(IPPROTO_UDP);
+#endif
+#if defined(MCAST_BLOCK_SOURCE)
+  define_const(MCAST_BLOCK_SOURCE);
+#endif
+#if defined(MCAST_JOIN_GROUP)
+  define_const(MCAST_JOIN_GROUP);
+#endif
+#if defined(MCAST_JOIN_SOURCE_GROUP)
+  define_const(MCAST_JOIN_SOURCE_GROUP);
+#endif
+#if defined(MCAST_LEAVE_GROUP)
+  define_const(MCAST_LEAVE_GROUP);
+#endif
+#if defined(MCAST_LEAVE_SOURCE_GROUP)
+  define_const(MCAST_LEAVE_SOURCE_GROUP);
+#endif
+#if defined(MCAST_MSFILTER)
+  define_const(MCAST_MSFILTER);
+#endif
+#if defined(MCAST_UNBLOCK_SOURCE)
+  define_const(MCAST_UNBLOCK_SOURCE);
+#endif
+#if defined(MSG_BCAST)
+  define_const(MSG_BCAST);
+#endif
+#if defined(MSG_CTRUNC)
+  define_const(MSG_CTRUNC);
+#endif
+#if defined(MSG_DONTROUTE)
+  define_const(MSG_DONTROUTE);
+#endif
+#if defined(MSG_DONTWAIT)
+  define_const(MSG_DONTWAIT);
+#endif
+#if defined(MSG_EOR)
+  define_const(MSG_EOR);
+#endif
+#if defined(MSG_MCAST)
+  define_const(MSG_MCAST);
+#endif
+#if defined(MSG_NOSIGNAL)
+  define_const(MSG_NOSIGNAL);
+#endif
+#if defined(MSG_OOB)
+  define_const(MSG_OOB);
+#endif
+#if defined(MSG_PEEK)
+  define_const(MSG_PEEK);
+#endif
+#if defined(MSG_TRUNC)
+  define_const(MSG_TRUNC);
+#endif
+#if defined(MSG_WAITALL)
+  define_const(MSG_WAITALL);
+#endif
+#if defined(NI_DGRAM)
+  define_const(NI_DGRAM);
+#endif
+#if defined(NI_MAXHOST)
+  define_const(NI_MAXHOST);
+#endif
+#if defined(NI_MAXSERV)
+  define_const(NI_MAXSERV);
+#endif
+#if defined(NI_NAMEREQD)
+  define_const(NI_NAMEREQD);
+#endif
+#if defined(NI_NOFQDN)
+  define_const(NI_NOFQDN);
+#endif
+#if defined(NI_NUMERICHOST)
+  define_const(NI_NUMERICHOST);
+#endif
+#if defined(NI_NUMERICSERV)
+  define_const(NI_NUMERICSERV);
+#endif
+#if defined(SHUT_RD)
+  define_const(SHUT_RD);
+#endif
+#if defined(SHUT_WR)
+  define_const(SHUT_WR);
+#endif
+#if defined(SHUT_RDWR)
+  define_const(SHUT_RDWR);
+#endif
+#if defined(SO_BINDANY)
+  define_const(SO_BINDANY);
+#endif
+#if defined(SO_BROADCAST)
+  define_const(SO_BROADCAST);
+#endif
+#if defined(SO_DEBUG)
+  define_const(SO_DEBUG);
+#endif
+#if defined(SO_DONTROUTE)
+  define_const(SO_DONTROUTE);
+#endif
+#if defined(SO_ERROR)
+  define_const(SO_ERROR);
+#endif
+#if defined(SO_KEEPALIVE)
+  define_const(SO_KEEPALIVE);
+#endif
+#if defined(SO_LINGER)
+  define_const(SO_LINGER);
+#endif
+#if defined(SO_NOSIGPIPE)
+  define_const(SO_NOSIGPIPE);
+#endif
+#if defined(SO_OOBINLINE)
+  define_const(SO_OOBINLINE);
+#endif
+#if defined(SO_PEERCRED)
+  define_const(SO_PEERCRED);
+#endif
+#if defined(SO_RCVBUF)
+  define_const(SO_RCVBUF);
+#endif
+#if defined(SO_RCVLOWAT)
+  define_const(SO_RCVLOWAT);
+#endif
+#if defined(SO_RCVTIMEO)
+  define_const(SO_RCVTIMEO);
+#endif
+#if defined(SO_REUSEADDR)
+  define_const(SO_REUSEADDR);
+#endif
+#if defined(SO_REUSEPORT)
+  define_const(SO_REUSEPORT);
+#endif
+#if defined(SO_RTABLE)
+  define_const(SO_RTABLE);
+#endif
+#if defined(SO_SNDBUF)
+  define_const(SO_SNDBUF);
+#endif
+#if defined(SO_SNDLOWAT)
+  define_const(SO_SNDLOWAT);
+#endif
+#if defined(SO_SNDTIMEO)
+  define_const(SO_SNDTIMEO);
+#endif
+#if defined(SO_SPLICE)
+  define_const(SO_SPLICE);
+#endif
+#if defined(SO_TIMESTAMP)
+  define_const(SO_TIMESTAMP);
+#endif
+#if defined(SO_TYPE)
+  define_const(SO_TYPE);
+#endif
+#if defined(SOCK_DGRAM)
+  define_const(SOCK_DGRAM);
+#endif
+#if defined(SOCK_RAW)
+  define_const(SOCK_RAW);
+#endif
+#if defined(SOCK_SEQPACKET)
+  define_const(SOCK_SEQPACKET);
+#endif
+#if defined(SOCK_STREAM)
+  define_const(SOCK_STREAM);
+#endif
+#if defined(SOL_SOCKET)
+  define_const(SOL_SOCKET);
+#endif
+#if defined(SOL_IP)
+  define_const(SOL_IP);
+#endif
+#if defined(SOL_TCP)
+  define_const(SOL_TCP);
+#endif
+#if defined(TCP_CONGCTL)
+  define_const(TCP_CONGCTL);
+#endif
+#if defined(TCP_CONGESTION)
+  define_const(TCP_CONGESTION);
+#endif
+#if defined(TCP_CORK)
+  define_const(TCP_CORK);
+#endif
+#if defined(TCP_DEFER_ACCEPT)
+  define_const(TCP_DEFER_ACCEPT);
+#endif
+#if defined(TCP_INFO)
+  define_const(TCP_INFO);
+#endif
+#if defined(TCP_KEEPCNT)
+  define_const(TCP_KEEPCNT);
+#endif
+#if defined(TCP_KEEPIDLE)
+  define_const(TCP_KEEPIDLE);
+#endif
+#if defined(TCP_KEEPINIT)
+  define_const(TCP_KEEPINIT);
+#endif
+#if defined(TCP_KEEPINTVL)
+  define_const(TCP_KEEPINTVL);
+#endif
+#if defined(TCP_LINGER2)
+  define_const(TCP_LINGER2);
+#endif
+#if defined(TCP_MAXSEG)
+  define_const(TCP_MAXSEG);
+#endif
+#if defined(TCP_MD5SIG)
+  define_const(TCP_MD5SIG);
+#endif
+#if defined(TCP_NODELAY)
+  define_const(TCP_NODELAY);
+#endif
+#if defined(TCP_QUICKACK)
+  define_const(TCP_QUICKACK);
+#endif
+#if defined(TCP_SACK_ENABLE)
+  define_const(TCP_SACK_ENABLE);
+#endif
+#if defined(TCP_SYNCNT)
+  define_const(TCP_SYNCNT);
+#endif
+#if defined(TCP_WINDOW_CLAMP)
+  define_const(TCP_WINDOW_CLAMP);
+#endif
diff --git a/third-party/mruby/mrbgems/mruby-socket/src/const.def b/third-party/mruby/mrbgems/mruby-socket/src/const.def
new file mode 100644 (file)
index 0000000..b54bb84
--- /dev/null
@@ -0,0 +1,165 @@
+AF_INET
+PF_INET
+AF_INET6
+PF_INET6
+AF_LINK
+PF_LINK
+AF_LOCAL
+PF_LOCAL
+AF_UNIX
+PF_UNIX
+AF_MAX
+AF_UNSPEC
+PF_UNSPEC
+AF_ROUTE
+PF_ROUTE
+
+AI_CANONNAME
+AI_FQDN
+AI_NUMERICHOST
+AI_NUMERICSERV
+AI_PASSIVE
+
+IP_ADD_MEMBERSHIP
+IP_ADD_SOURCE_MEMBERSHIP
+IP_BLOCK_SOURCE
+IP_DROP_MEMBERSHIP
+IP_DROP_SOURCE_MEMBERSHIP
+IP_FREEBIND
+IP_HDRINCL
+IP_IPSEC_POLICY
+IP_MINTTL
+IP_MSFILTER
+IP_MTU
+IP_MTU_DISCOVER
+IP_MULTICAST_ALL
+IP_MULTICAST_IF
+IP_MULTICAST_LOOP
+IP_MULTICAST_TTL
+IP_OPTIONS
+IP_ORIGDSTADDR
+IP_PASSSEC
+IP_PKTINFO
+IP_PKTOPTIONS
+IP_PMTUDISC_DO
+IP_PMTUDISC_DONT
+IP_PMTUDISC_PROBE
+IP_PMTUDISC_WANT
+IP_RECVDSTADDR
+IP_RECVERR
+IP_RECVOPTS
+IP_RECVORIGDSTADDR
+IP_RECVRETOPTS
+IP_RECVTOS
+IP_RECVTTL
+IP_RETOPTS
+IP_ROUTER_ALERT
+IP_TOS
+IP_TRANSPARENT
+IP_TTL
+IP_UNBLOCK_SOURCE
+IP_XFRM_POLICY
+
+IPV6_JOIN_GROUP
+IPV6_LEAVE_GROUP
+IPV6_MULTICAST_HOPS
+IPV6_MULTICAST_IF
+IPV6_MULTICAST_LOOP
+IPV6_UNICAST_HOPS
+IPV6_V6ONLY
+
+IPPROTO_AH
+IPPROTO_DSTOPTS
+IPPROTO_ESP
+IPPROTO_FRAGMENT
+IPPROTO_ICMP
+IPPROTO_ICMPV6
+IPPROTO_IP
+IPPROTO_IPV6
+IPPROTO_NONE
+IPPROTO_RAW
+IPPROTO_ROUTING
+IPPROTO_TCP
+IPPROTO_UDP
+
+MCAST_BLOCK_SOURCE
+MCAST_JOIN_GROUP
+MCAST_JOIN_SOURCE_GROUP
+MCAST_LEAVE_GROUP
+MCAST_LEAVE_SOURCE_GROUP
+MCAST_MSFILTER
+MCAST_UNBLOCK_SOURCE
+
+MSG_BCAST
+MSG_CTRUNC
+MSG_DONTROUTE
+MSG_DONTWAIT
+MSG_EOR
+MSG_MCAST
+MSG_NOSIGNAL
+MSG_OOB
+MSG_PEEK
+MSG_TRUNC
+MSG_WAITALL
+
+NI_DGRAM
+NI_MAXHOST
+NI_MAXSERV
+NI_NAMEREQD
+NI_NOFQDN
+NI_NUMERICHOST
+NI_NUMERICSERV
+
+SHUT_RD
+SHUT_WR
+SHUT_RDWR
+
+SO_BINDANY
+SO_BROADCAST
+SO_DEBUG
+SO_DONTROUTE
+SO_ERROR
+SO_KEEPALIVE
+SO_LINGER
+SO_NOSIGPIPE
+SO_OOBINLINE
+SO_PEERCRED
+SO_RCVBUF
+SO_RCVLOWAT
+SO_RCVTIMEO
+SO_REUSEADDR
+SO_REUSEPORT
+SO_RTABLE
+SO_SNDBUF
+SO_SNDLOWAT
+SO_SNDTIMEO
+SO_SPLICE
+SO_TIMESTAMP
+SO_TYPE
+
+SOCK_DGRAM
+SOCK_RAW
+SOCK_SEQPACKET
+SOCK_STREAM
+
+SOL_SOCKET
+SOL_IP
+SOL_TCP
+
+TCP_CONGCTL
+TCP_CONGESTION
+TCP_CORK
+TCP_DEFER_ACCEPT
+TCP_INFO
+TCP_KEEPCNT
+TCP_KEEPIDLE
+TCP_KEEPINIT
+TCP_KEEPINTVL
+TCP_LINGER2
+TCP_MAXSEG
+TCP_MD5SIG
+TCP_NODELAY
+TCP_QUICKACK
+TCP_SACK_ENABLE
+TCP_SYNCNT
+TCP_WINDOW_CLAMP
diff --git a/third-party/mruby/mrbgems/mruby-socket/src/gen.rb b/third-party/mruby/mrbgems/mruby-socket/src/gen.rb
new file mode 100755 (executable)
index 0000000..ba2f53a
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/env ruby
+
+Dir.chdir(File.dirname($0))
+
+f = File.open("const.cstub", "w")
+
+IO.readlines("const.def").each { |name|
+  name.sub(/^#.*/, "")
+  name.strip!
+  next if name.empty?
+
+  f.write <<CODE
+#if defined(#{name})#{name.start_with?('IPPROTO_') ? ' || defined(_WINSOCKAPI_)' : ''}
+  define_const(#{name});
+#endif
+CODE
+}
diff --git a/third-party/mruby/mrbgems/mruby-socket/src/socket.c b/third-party/mruby/mrbgems/mruby-socket/src/socket.c
new file mode 100644 (file)
index 0000000..4d56d53
--- /dev/null
@@ -0,0 +1,943 @@
+/*
+** socket.c - Socket module
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifdef _WIN32
+  #define _WIN32_WINNT 0x0501
+
+  #include <winsock2.h>
+  #include <ws2tcpip.h>
+  #include <windows.h>
+
+  #define SHUT_RDWR SD_BOTH
+  #ifndef _SSIZE_T_DEFINED
+  typedef int ssize_t;
+  #endif
+  typedef int fsize_t;
+#else
+  #include <sys/types.h>
+  #include <sys/socket.h>
+  #include <sys/un.h>
+  #include <netinet/in.h>
+  #include <netinet/tcp.h>
+  #include <arpa/inet.h>
+  #include <fcntl.h>
+  #include <netdb.h>
+  #include <unistd.h>
+  typedef size_t fsize_t;
+#endif
+
+#include <stddef.h>
+#include <string.h>
+
+#include "mruby.h"
+#include "mruby/array.h"
+#include "mruby/class.h"
+#include "mruby/data.h"
+#include "mruby/string.h"
+#include "mruby/variable.h"
+#include "error.h"
+
+#include "mruby/ext/io.h"
+
+#define E_SOCKET_ERROR             (mrb_class_get(mrb, "SocketError"))
+
+#if !defined(mrb_cptr)
+#define mrb_cptr_value(m,p) mrb_voidp_value((m),(p))
+#define mrb_cptr(o) mrb_voidp(o)
+#define mrb_cptr_p(o) mrb_voidp_p(o)
+#endif
+
+#ifdef _WIN32
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
+{
+    if (af == AF_INET)
+    {
+       struct sockaddr_in in;
+       memset(&in, 0, sizeof(in));
+       in.sin_family = AF_INET;
+       memcpy(&in.sin_addr, src, sizeof(struct in_addr));
+       getnameinfo((struct sockaddr *)&in, sizeof(struct
+                   sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST);
+       return dst;
+    }
+    else if (af == AF_INET6)
+    {
+       struct sockaddr_in6 in;
+       memset(&in, 0, sizeof(in));
+       in.sin6_family = AF_INET6;
+       memcpy(&in.sin6_addr, src, sizeof(struct in_addr6));
+       getnameinfo((struct sockaddr *)&in, sizeof(struct
+                   sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST);
+       return dst;
+    }
+    return NULL;
+}
+
+int inet_pton(int af, const char *src, void *dst)
+{
+    struct addrinfo hints, *res, *ressave;
+
+    memset(&hints, 0, sizeof(struct addrinfo));
+    hints.ai_family = af;
+
+    if (getaddrinfo(src, NULL, &hints, &res) != 0)
+    {
+       printf("Couldn't resolve host %s\n", src);
+       return -1;
+    }
+
+    ressave = res;
+
+    while (res)
+    {
+       memcpy(dst, res->ai_addr, res->ai_addrlen);
+       res = res->ai_next;
+    }
+
+    freeaddrinfo(ressave);
+    return 0;
+}
+
+#endif
+
+static mrb_value
+mrb_addrinfo_getaddrinfo(mrb_state *mrb, mrb_value klass)
+{
+  struct addrinfo hints, *res0, *res;
+  mrb_value ai, ary, family, lastai, nodename, protocol, sa, service, socktype;
+  mrb_int flags;
+  int arena_idx, error;
+  const char *hostname = NULL, *servname = NULL;
+
+  ary = mrb_ary_new(mrb);
+  arena_idx = mrb_gc_arena_save(mrb);  /* ary must be on arena! */
+
+  family = socktype = protocol = mrb_nil_value();
+  flags = 0;
+  mrb_get_args(mrb, "oo|oooi", &nodename, &service, &family, &socktype, &protocol, &flags);
+
+  if (mrb_string_p(nodename)) {
+    hostname = mrb_str_to_cstr(mrb, nodename);
+  } else if (mrb_nil_p(nodename)) {
+    hostname = NULL;
+  } else {
+    mrb_raise(mrb, E_TYPE_ERROR, "nodename must be String or nil");
+  }
+
+  if (mrb_string_p(service)) {
+    servname = mrb_str_to_cstr(mrb, service);
+  } else if (mrb_fixnum_p(service)) {
+    servname = mrb_str_to_cstr(mrb, mrb_funcall(mrb, service, "to_s", 0));
+  } else if (mrb_nil_p(service)) {
+    servname = NULL;
+  } else {
+    mrb_raise(mrb, E_TYPE_ERROR, "service must be String, Fixnum, or nil");
+  }
+
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_flags = (int)flags;
+
+  if (mrb_fixnum_p(family)) {
+    hints.ai_family = (int)mrb_fixnum(family);
+  }
+
+  if (mrb_fixnum_p(socktype)) {
+    hints.ai_socktype = (int)mrb_fixnum(socktype);
+  }
+
+  if (mrb_fixnum_p(protocol)) {
+    hints.ai_protocol = (int)mrb_fixnum(protocol);
+  }
+
+  lastai = mrb_cv_get(mrb, klass, mrb_intern_lit(mrb, "_lastai"));
+  if (mrb_cptr_p(lastai)) {
+    freeaddrinfo((struct addrinfo*)mrb_cptr(lastai));
+    mrb_cv_set(mrb, klass, mrb_intern_lit(mrb, "_lastai"), mrb_nil_value());
+  }
+
+  error = getaddrinfo(hostname, servname, &hints, &res0);
+  if (error) {
+    mrb_raisef(mrb, E_SOCKET_ERROR, "getaddrinfo: %S", mrb_str_new_cstr(mrb, gai_strerror(error)));
+  }
+  mrb_cv_set(mrb, klass, mrb_intern_lit(mrb, "_lastai"), mrb_cptr_value(mrb, res0));
+
+  for (res = res0; res != NULL; res = res->ai_next) {
+    sa = mrb_str_new(mrb, (char*)res->ai_addr, res->ai_addrlen);
+    ai = mrb_funcall(mrb, klass, "new", 4, sa, mrb_fixnum_value(res->ai_family), mrb_fixnum_value(res->ai_socktype), mrb_fixnum_value(res->ai_protocol));
+    mrb_ary_push(mrb, ary, ai);
+    mrb_gc_arena_restore(mrb, arena_idx);
+  }
+
+  freeaddrinfo(res0);
+  mrb_cv_set(mrb, klass, mrb_intern_lit(mrb, "_lastai"), mrb_nil_value());
+
+  return ary;
+}
+
+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, (int)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;
+}
+
+#ifndef _WIN32
+static mrb_value
+mrb_addrinfo_unix_path(mrb_state *mrb, mrb_value self)
+{
+  mrb_value sastr;
+
+  sastr = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@sockaddr"));
+  if (((struct sockaddr *)RSTRING_PTR(sastr))->sa_family != AF_UNIX)
+    mrb_raise(mrb, E_SOCKET_ERROR, "need AF_UNIX address");
+  return mrb_str_new_cstr(mrb, ((struct sockaddr_un *)RSTRING_PTR(sastr))->sun_path);
+}
+#endif
+
+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;
+}
+
+static int
+socket_fd(mrb_state *mrb, mrb_value sock)
+{
+  return (int)mrb_fixnum(mrb_funcall(mrb, sock, "fileno", 0));
+}
+
+static int
+socket_family(int s)
+{
+  struct sockaddr_storage ss;
+  socklen_t salen;
+
+  salen = sizeof(ss);
+  if (getsockname(s, (struct sockaddr *)&ss, &salen) == -1)
+    return AF_UNSPEC;
+  return ss.ss_family;
+}
+
+static mrb_value
+mrb_basicsocket_getpeereid(mrb_state *mrb, mrb_value self)
+{
+#ifdef HAVE_GETPEEREID
+  mrb_value ary;
+  gid_t egid;
+  uid_t euid;
+  int s;
+
+  s = socket_fd(mrb, self);
+  if (getpeereid(s, &euid, &egid) != 0)
+    mrb_sys_fail(mrb, "getpeereid");
+
+  ary = mrb_ary_new_capa(mrb, 2);
+  mrb_ary_push(mrb, ary, mrb_fixnum_value((mrb_int)euid));
+  mrb_ary_push(mrb, ary, mrb_fixnum_value((mrb_int)egid));
+  return ary;
+#else
+  mrb_raise(mrb, E_RUNTIME_ERROR, "getpeereid is not avaialble on this system");
+  return mrb_nil_value();
+#endif
+}
+
+static mrb_value
+mrb_basicsocket_getpeername(mrb_state *mrb, mrb_value self)
+{
+  struct sockaddr_storage ss;
+  socklen_t salen;
+
+  salen = sizeof(ss);
+  if (getpeername(socket_fd(mrb, self), (struct sockaddr *)&ss, &salen) != 0)
+    mrb_sys_fail(mrb, "getpeername");
+
+  return mrb_str_new(mrb, (char*)&ss, salen);
+}
+
+static mrb_value
+mrb_basicsocket_getsockname(mrb_state *mrb, mrb_value self)
+{
+  struct sockaddr_storage ss;
+  socklen_t salen;
+
+  salen = sizeof(ss);
+  if (getsockname(socket_fd(mrb, self), (struct sockaddr *)&ss, &salen) != 0)
+    mrb_sys_fail(mrb, "getsockname");
+
+  return mrb_str_new(mrb, (char*)&ss, salen);
+}
+
+static mrb_value
+mrb_basicsocket_getsockopt(mrb_state *mrb, mrb_value self)
+{
+  char opt[8];
+  int s;
+  mrb_int family, level, optname;
+  mrb_value c, data;
+  socklen_t optlen;
+
+  mrb_get_args(mrb, "ii", &level, &optname);
+  s = socket_fd(mrb, self);
+  optlen = sizeof(opt);
+  if (getsockopt(s, (int)level, (int)optname, opt, &optlen) == -1)
+    mrb_sys_fail(mrb, "getsockopt");
+  c = mrb_const_get(mrb, mrb_obj_value(mrb_class_get(mrb, "Socket")), mrb_intern_lit(mrb, "Option"));
+  family = socket_family(s);
+  data = mrb_str_new(mrb, opt, optlen);
+  return mrb_funcall(mrb, c, "new", 4, mrb_fixnum_value(family), mrb_fixnum_value(level), mrb_fixnum_value(optname), data);
+}
+
+static mrb_value
+mrb_basicsocket_recv(mrb_state *mrb, mrb_value self)
+{
+  ssize_t 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), (fsize_t)maxlen, (int)flags);
+  if (n == -1)
+    mrb_sys_fail(mrb, "recv");
+  mrb_str_resize(mrb, buf, (mrb_int)n);
+  return buf;
+}
+
+static mrb_value
+mrb_basicsocket_recvfrom(mrb_state *mrb, mrb_value self)
+{
+  ssize_t 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), (fsize_t)maxlen, (int)flags, (struct sockaddr *)RSTRING_PTR(sa), &socklen);
+  if (n == -1)
+    mrb_sys_fail(mrb, "recvfrom");
+  mrb_str_resize(mrb, buf, (mrb_int)n);
+  mrb_str_resize(mrb, sa, (mrb_int)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
+mrb_basicsocket_send(mrb_state *mrb, mrb_value self)
+{
+  ssize_t n;
+  mrb_int flags;
+  mrb_value dest, mesg;
+
+  dest = mrb_nil_value();
+  mrb_get_args(mrb, "Si|S", &mesg, &flags, &dest);
+  if (mrb_nil_p(dest)) {
+    n = send(socket_fd(mrb, self), RSTRING_PTR(mesg), (fsize_t)RSTRING_LEN(mesg), (int)flags);
+  } else {
+    n = sendto(socket_fd(mrb, self), RSTRING_PTR(mesg), (fsize_t)RSTRING_LEN(mesg), (int)flags, (const struct sockaddr*)RSTRING_PTR(dest), (fsize_t)RSTRING_LEN(dest));
+  }
+  if (n == -1)
+    mrb_sys_fail(mrb, "send");
+  return mrb_fixnum_value((mrb_int)n);
+}
+
+static mrb_value
+mrb_basicsocket_setnonblock(mrb_state *mrb, mrb_value self)
+{
+  int fd, flags;
+  mrb_bool nonblocking;
+#ifdef _WIN32
+  u_long mode = 1;
+#endif
+
+  mrb_get_args(mrb, "b", &nonblocking);
+  fd = socket_fd(mrb, self);
+#ifdef _WIN32
+  flags = ioctlsocket(fd, FIONBIO, &mode);
+  if (flags != NO_ERROR)
+    mrb_sys_fail(mrb, "ioctlsocket");
+#else
+  flags = fcntl(fd, F_GETFL, 0);
+  if (flags == 1)
+    mrb_sys_fail(mrb, "fcntl");
+  if (nonblocking)
+    flags |= O_NONBLOCK;
+  else
+    flags &= ~O_NONBLOCK;
+  if (fcntl(fd, F_SETFL, flags) == -1)
+    mrb_sys_fail(mrb, "fcntl");
+#endif
+  return mrb_nil_value();
+}
+
+static mrb_value
+mrb_basicsocket_setsockopt(mrb_state *mrb, mrb_value self)
+{
+  int s;
+  mrb_int argc, level = 0, optname;
+  mrb_value optval, so;
+
+  argc = mrb_get_args(mrb, "o|io", &so, &optname, &optval);
+  if (argc == 3) {
+    if (!mrb_fixnum_p(so)) {
+      mrb_raise(mrb, E_ARGUMENT_ERROR, "level is not an integer");
+    }
+    level = mrb_fixnum(so);
+    if (mrb_string_p(optval)) {
+      /* that's good */
+    } else if (mrb_type(optval) == MRB_TT_TRUE || mrb_type(optval) == MRB_TT_FALSE) {
+      mrb_int i = mrb_test(optval) ? 1 : 0;
+      optval = mrb_str_new(mrb, (char*)&i, sizeof(i));
+    } else if (mrb_fixnum_p(optval)) {
+      if (optname == IP_MULTICAST_TTL || optname == IP_MULTICAST_LOOP) {
+        char uc = (char)mrb_fixnum(optval);
+        optval = mrb_str_new(mrb, &uc, sizeof(uc));
+      } else {
+        mrb_int i = mrb_fixnum(optval);
+        optval = mrb_str_new(mrb, (char*)&i, sizeof(i));
+      }
+    } else {
+      mrb_raise(mrb, E_ARGUMENT_ERROR, "optval should be true, false, an integer, or a string");
+    }
+  } else if (argc == 1) {
+    if (strcmp(mrb_obj_classname(mrb, so), "Socket::Option") != 0)
+      mrb_raisef(mrb, E_ARGUMENT_ERROR, "not an instance of Socket::Option");
+    level = mrb_fixnum(mrb_funcall(mrb, so, "level", 0));
+    optname = mrb_fixnum(mrb_funcall(mrb, so, "optname", 0));
+    optval = mrb_funcall(mrb, so, "data", 0);
+  } else {
+    mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%d for 3)", argc);
+  }
+
+  s = socket_fd(mrb, self);
+  if (setsockopt(s, (int)level, (int)optname, RSTRING_PTR(optval), (socklen_t)RSTRING_LEN(optval)) == -1)
+    mrb_sys_fail(mrb, "setsockopt");
+  return mrb_fixnum_value(0);
+}
+
+static mrb_value
+mrb_basicsocket_shutdown(mrb_state *mrb, mrb_value self)
+{
+  mrb_int how = SHUT_RDWR;
+
+  mrb_get_args(mrb, "|i", &how);
+  if (shutdown(socket_fd(mrb, self), (int)how) != 0)
+    mrb_sys_fail(mrb, "shutdown");
+  return mrb_fixnum_value(0);
+}
+
+static mrb_value
+mrb_basicsocket_set_is_socket(mrb_state *mrb, mrb_value self)
+{
+  mrb_bool b;
+  struct mrb_io *io_p;
+  mrb_get_args(mrb, "b", &b);
+
+  io_p = (struct mrb_io*)DATA_PTR(self);
+  if (io_p) {
+    io_p->is_socket = b;
+  }
+
+  return mrb_bool_value(b);
+}
+
+static mrb_value
+mrb_ipsocket_ntop(mrb_state *mrb, mrb_value klass)
+{
+  mrb_int af, n;
+  char *addr, buf[50];
+
+  mrb_get_args(mrb, "is", &af, &addr, &n);
+  if ((af == AF_INET && n != 4) || (af == AF_INET6 && n != 16))
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid address");
+  if (inet_ntop((int)af, addr, buf, sizeof(buf)) == NULL)
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid address");
+  return mrb_str_new_cstr(mrb, buf);
+}
+
+static mrb_value
+mrb_ipsocket_pton(mrb_state *mrb, mrb_value klass)
+{
+  mrb_int af, n;
+  char *bp, buf[50];
+
+  mrb_get_args(mrb, "is", &af, &bp, &n);
+  if ((size_t)n > sizeof(buf) - 1)
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid address");
+  memcpy(buf, bp, n);
+  buf[n] = '\0';
+
+  if (af == AF_INET) {
+    struct in_addr in;
+    if (inet_pton(AF_INET, buf, (void *)&in.s_addr) != 1)
+      goto invalid;
+    return mrb_str_new(mrb, (char*)&in.s_addr, 4);
+  } else if (af == AF_INET6) {
+    struct in6_addr in6;
+    if (inet_pton(AF_INET6, buf, (void *)&in6.s6_addr) != 1)
+      goto invalid;
+    return mrb_str_new(mrb, (char*)&in6.s6_addr, 16);
+  } else
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "unsupported address family");
+
+invalid:
+  mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid address");
+  return mrb_nil_value(); /* dummy */
+}
+
+static mrb_value
+mrb_ipsocket_recvfrom(mrb_state *mrb, mrb_value self)
+{
+  struct sockaddr_storage ss;
+  socklen_t socklen;
+  mrb_value a, buf, pair;
+  mrb_int flags, maxlen;
+  ssize_t n;
+  int fd;
+
+  fd = socket_fd(mrb, self);
+  flags = 0;
+  mrb_get_args(mrb, "i|i", &maxlen, &flags);
+  buf = mrb_str_buf_new(mrb, maxlen);
+  socklen = sizeof(ss);
+  n = recvfrom(fd, RSTRING_PTR(buf), (fsize_t)maxlen, (int)flags,
+              (struct sockaddr *)&ss, &socklen);
+  if (n == -1) {
+    mrb_sys_fail(mrb, "recvfrom");
+  }
+  mrb_str_resize(mrb, buf, (mrb_int)n);
+  a = sa2addrlist(mrb, (struct sockaddr *)&ss, socklen);
+  pair = mrb_ary_new_capa(mrb, 2);
+  mrb_ary_push(mrb, pair, buf);
+  mrb_ary_push(mrb, pair, a);
+  return pair;
+}
+
+static mrb_value
+mrb_socket_gethostname(mrb_state *mrb, mrb_value cls)
+{
+  mrb_value buf;
+  size_t bufsize;
+
+#ifdef HOST_NAME_MAX
+  bufsize = HOST_NAME_MAX + 1;
+#else
+  bufsize = 256;
+#endif
+  buf = mrb_str_buf_new(mrb, (mrb_int)bufsize);
+  if (gethostname(RSTRING_PTR(buf), (fsize_t)bufsize) != 0)
+    mrb_sys_fail(mrb, "gethostname");
+  mrb_str_resize(mrb, buf, (mrb_int)strlen(RSTRING_PTR(buf)));
+  return buf;
+}
+
+static mrb_value
+mrb_socket_accept(mrb_state *mrb, mrb_value klass)
+{
+  int s1;
+  mrb_int s0;
+
+  mrb_get_args(mrb, "i", &s0);
+  s1 = (int)accept(s0, NULL, NULL);
+  if (s1 == -1) {
+    mrb_sys_fail(mrb, "accept");
+  }
+  return mrb_fixnum_value(s1);
+}
+
+static mrb_value
+mrb_socket_accept2(mrb_state *mrb, mrb_value klass)
+{
+  mrb_value ary, sastr;
+  int s1;
+  mrb_int s0;
+  socklen_t socklen;
+
+  mrb_get_args(mrb, "i", &s0);
+  socklen = sizeof(struct sockaddr_storage);
+  sastr = mrb_str_buf_new(mrb, socklen);
+  s1 = (int)accept(s0, (struct sockaddr *)RSTRING_PTR(sastr), &socklen);
+  if (s1 == -1) {
+    mrb_sys_fail(mrb, "accept");
+  }
+  // XXX: possible descriptor leakage here!
+  mrb_str_resize(mrb, sastr, socklen);
+  ary = mrb_ary_new_capa(mrb, 2);
+  mrb_ary_push(mrb, ary, mrb_fixnum_value(s1));
+  mrb_ary_push(mrb, ary, sastr);
+  return ary;
+}
+
+static mrb_value
+mrb_socket_bind(mrb_state *mrb, mrb_value klass)
+{
+  mrb_value sastr;
+  mrb_int s;
+
+  mrb_get_args(mrb, "iS", &s, &sastr);
+  if (bind((int)s, (struct sockaddr *)RSTRING_PTR(sastr), (socklen_t)RSTRING_LEN(sastr)) == -1) {
+    mrb_sys_fail(mrb, "bind");
+  }
+  return mrb_nil_value();
+}
+
+static mrb_value
+mrb_socket_connect(mrb_state *mrb, mrb_value klass)
+{
+  mrb_value sastr;
+  mrb_int s;
+
+  mrb_get_args(mrb, "iS", &s, &sastr);
+  if (connect((int)s, (struct sockaddr *)RSTRING_PTR(sastr), (socklen_t)RSTRING_LEN(sastr)) == -1) {
+    mrb_sys_fail(mrb, "connect");
+  }
+  return mrb_nil_value();
+}
+
+static mrb_value
+mrb_socket_listen(mrb_state *mrb, mrb_value klass)
+{
+  mrb_int backlog, s;
+
+  mrb_get_args(mrb, "ii", &s, &backlog);
+  if (listen((int)s, (int)backlog) == -1) {
+    mrb_sys_fail(mrb, "listen");
+  }
+  return mrb_nil_value();
+}
+
+static mrb_value
+mrb_socket_sockaddr_family(mrb_state *mrb, mrb_value klass)
+{
+  mrb_value sa;
+
+  mrb_get_args(mrb, "S", &sa);
+#ifdef __linux__
+  if ((size_t)RSTRING_LEN(sa) < offsetof(struct sockaddr, sa_family) + sizeof(sa_family_t)) {
+    mrb_raisef(mrb, E_SOCKET_ERROR, "invalid sockaddr (too short)");
+  }
+#else
+  if ((size_t)RSTRING_LEN(sa) < sizeof(struct sockaddr)) {
+    mrb_raisef(mrb, E_SOCKET_ERROR, "invalid sockaddr (too short)");
+  }
+#endif
+  return mrb_fixnum_value(((struct sockaddr *)RSTRING_PTR(sa))->sa_family);
+}
+
+static mrb_value
+mrb_socket_sockaddr_un(mrb_state *mrb, mrb_value klass)
+{
+#ifdef _WIN32
+  mrb_raise(mrb, E_NOTIMP_ERROR, "sockaddr_un unsupported on Windows");
+  return mrb_nil_value();
+#else
+  struct sockaddr_un *sunp;
+  mrb_value path, s;
+
+  mrb_get_args(mrb, "S", &path);
+  if ((size_t)RSTRING_LEN(path) > sizeof(sunp->sun_path) - 1) {
+    mrb_raisef(mrb, E_ARGUMENT_ERROR, "too long unix socket path (max: %ubytes)", (unsigned int)sizeof(sunp->sun_path) - 1);
+  }
+  s = mrb_str_buf_new(mrb, sizeof(struct sockaddr_un));
+  sunp = (struct sockaddr_un *)RSTRING_PTR(s);
+  sunp->sun_family = AF_UNIX;
+  memcpy(sunp->sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
+  sunp->sun_path[RSTRING_LEN(path)] = '\0';
+  mrb_str_resize(mrb, s, sizeof(struct sockaddr_un));
+  return s;
+#endif
+}
+
+static mrb_value
+mrb_socket_socketpair(mrb_state *mrb, mrb_value klass)
+{
+#ifdef _WIN32
+  mrb_raise(mrb, E_NOTIMP_ERROR, "socketpair unsupported on Windows");
+  return mrb_nil_value();
+#else
+  mrb_value ary;
+  mrb_int domain, type, protocol;
+  int sv[2];
+
+  mrb_get_args(mrb, "iii", &domain, &type, &protocol);
+  if (socketpair(domain, type, protocol, sv) == -1) {
+    mrb_sys_fail(mrb, "socketpair");
+  }
+  // XXX: possible descriptor leakage here!
+  ary = mrb_ary_new_capa(mrb, 2);
+  mrb_ary_push(mrb, ary, mrb_fixnum_value(sv[0]));
+  mrb_ary_push(mrb, ary, mrb_fixnum_value(sv[1]));
+  return ary;
+#endif
+}
+
+static mrb_value
+mrb_socket_socket(mrb_state *mrb, mrb_value klass)
+{
+  mrb_int domain, type, protocol;
+  int s;
+
+  mrb_get_args(mrb, "iii", &domain, &type, &protocol);
+  s = (int)socket((int)domain, (int)type, (int)protocol);
+  if (s == -1)
+    mrb_sys_fail(mrb, "socket");
+  return mrb_fixnum_value(s);
+}
+
+static mrb_value
+mrb_tcpsocket_allocate(mrb_state *mrb, mrb_value klass)
+{
+  struct RClass *c = mrb_class_ptr(klass);
+  enum mrb_vtype ttype = MRB_INSTANCE_TT(c);
+
+  /* copied from mrb_instance_alloc() */
+  if (ttype == 0) ttype = MRB_TT_OBJECT;
+  return mrb_obj_value((struct RObject*)mrb_obj_alloc(mrb, ttype, c));
+}
+
+/* Windows overrides for IO methods on BasicSocket objects.
+ * This is because sockets on Windows are not the same as file
+ * descriptors, and thus functions which operate on file descriptors
+ * will break on socket descriptors.
+ */
+#ifdef _WIN32
+static mrb_value
+mrb_win32_basicsocket_close(mrb_state *mrb, mrb_value self)
+{
+  if (closesocket(socket_fd(mrb, self)) != NO_ERROR)
+    mrb_raise(mrb, E_SOCKET_ERROR, "closesocket unsuccessful");
+  return mrb_nil_value();
+}
+
+#define E_EOF_ERROR                (mrb_class_get(mrb, "EOFError"))
+static mrb_value
+mrb_win32_basicsocket_sysread(mrb_state *mrb, mrb_value self)
+{
+  int sd, ret;
+  mrb_value buf = mrb_nil_value();
+  mrb_int maxlen;
+
+  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, NULL, maxlen);
+  }
+  if (RSTRING_LEN(buf) != maxlen) {
+    buf = mrb_str_resize(mrb, buf, maxlen);
+  }
+
+  sd = socket_fd(mrb, self);
+  ret = recv(sd, RSTRING_PTR(buf), (int)maxlen, 0);
+
+  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 SOCKET_ERROR: /* Error */
+      mrb_sys_fail(mrb, "recv");
+      break;
+    default:
+      if (RSTRING_LEN(buf) != ret) {
+        buf = mrb_str_resize(mrb, buf, ret);
+      }
+      break;
+  }
+
+  return buf;
+}
+
+static mrb_value
+mrb_win32_basicsocket_sysseek(mrb_state *mrb, mrb_value self)
+{
+  mrb_raise(mrb, E_NOTIMP_ERROR, "sysseek not implemented for windows sockets");
+  return mrb_nil_value();
+}
+
+static mrb_value
+mrb_win32_basicsocket_syswrite(mrb_state *mrb, mrb_value self)
+{
+  int n;
+  SOCKET sd;
+  mrb_value str;
+
+  sd = socket_fd(mrb, self);
+  mrb_get_args(mrb, "S", &str);
+  n = send(sd, RSTRING_PTR(str), (int)RSTRING_LEN(str), 0);
+  if (n == SOCKET_ERROR)
+    mrb_sys_fail(mrb, "send");
+  return mrb_fixnum_value(n);
+}
+
+#endif
+
+void
+mrb_mruby_socket_gem_init(mrb_state* mrb)
+{
+  struct RClass *io, *ai, *sock, *bsock, *ipsock, *tcpsock;
+  struct RClass *constants;
+
+#ifdef _WIN32
+  WSADATA wsaData;
+  int result;
+  result = WSAStartup(MAKEWORD(2,2), &wsaData);
+  if (result != NO_ERROR)
+    mrb_raise(mrb, E_RUNTIME_ERROR, "WSAStartup failed");
+#endif
+
+  ai = mrb_define_class(mrb, "Addrinfo", mrb->object_class);
+  mrb_mod_cv_set(mrb, ai, mrb_intern_lit(mrb, "_lastai"), mrb_nil_value());
+  mrb_define_class_method(mrb, ai, "getaddrinfo", mrb_addrinfo_getaddrinfo, MRB_ARGS_REQ(2)|MRB_ARGS_OPT(4));
+  mrb_define_method(mrb, ai, "getnameinfo", mrb_addrinfo_getnameinfo, MRB_ARGS_OPT(1));
+#ifndef _WIN32
+  mrb_define_method(mrb, ai, "unix_path", mrb_addrinfo_unix_path, MRB_ARGS_NONE());
+#endif
+
+  io = mrb_class_get(mrb, "IO");
+
+  bsock = mrb_define_class(mrb, "BasicSocket", io);
+  mrb_define_method(mrb, bsock, "_recvfrom", mrb_basicsocket_recvfrom, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+  mrb_define_method(mrb, bsock, "_setnonblock", mrb_basicsocket_setnonblock, MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, bsock, "getpeereid", mrb_basicsocket_getpeereid, MRB_ARGS_NONE());
+  mrb_define_method(mrb, bsock, "getpeername", mrb_basicsocket_getpeername, MRB_ARGS_NONE());
+  mrb_define_method(mrb, bsock, "getsockname", mrb_basicsocket_getsockname, MRB_ARGS_NONE());
+  mrb_define_method(mrb, bsock, "getsockopt", mrb_basicsocket_getsockopt, MRB_ARGS_REQ(2));
+  mrb_define_method(mrb, bsock, "recv", mrb_basicsocket_recv, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+  // #recvmsg(maxlen, flags=0)
+  mrb_define_method(mrb, bsock, "send", mrb_basicsocket_send, MRB_ARGS_REQ(2)|MRB_ARGS_OPT(1));
+  // #sendmsg
+  // #sendmsg_nonblock
+  mrb_define_method(mrb, bsock, "setsockopt", mrb_basicsocket_setsockopt, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(2));
+  mrb_define_method(mrb, bsock, "shutdown", mrb_basicsocket_shutdown, MRB_ARGS_OPT(1));
+  mrb_define_method(mrb, bsock, "_is_socket=", mrb_basicsocket_set_is_socket, MRB_ARGS_REQ(1));
+
+  ipsock = mrb_define_class(mrb, "IPSocket", bsock);
+  mrb_define_class_method(mrb, ipsock, "ntop", mrb_ipsocket_ntop, MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, ipsock, "pton", mrb_ipsocket_pton, MRB_ARGS_REQ(2));
+  mrb_define_method(mrb, ipsock, "recvfrom", mrb_ipsocket_recvfrom, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+
+  tcpsock = mrb_define_class(mrb, "TCPSocket", ipsock);
+  mrb_define_class_method(mrb, tcpsock, "_allocate", mrb_tcpsocket_allocate, MRB_ARGS_NONE());
+  mrb_define_class(mrb, "TCPServer", tcpsock);
+
+  mrb_define_class(mrb, "UDPSocket", ipsock);
+  //#recvfrom_nonblock
+
+  sock = mrb_define_class(mrb, "Socket", bsock);
+  mrb_define_class_method(mrb, sock, "_accept", mrb_socket_accept, MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, sock, "_accept2", mrb_socket_accept2, MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, sock, "_bind", mrb_socket_bind, MRB_ARGS_REQ(3));
+  mrb_define_class_method(mrb, sock, "_connect", mrb_socket_connect, MRB_ARGS_REQ(3));
+  mrb_define_class_method(mrb, sock, "_listen", mrb_socket_listen, MRB_ARGS_REQ(2));
+  mrb_define_class_method(mrb, sock, "_sockaddr_family", mrb_socket_sockaddr_family, MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, sock, "_socket", mrb_socket_socket, MRB_ARGS_REQ(3));
+  //mrb_define_class_method(mrb, sock, "gethostbyaddr", mrb_socket_gethostbyaddr, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+  //mrb_define_class_method(mrb, sock, "gethostbyname", mrb_socket_gethostbyname, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+  mrb_define_class_method(mrb, sock, "gethostname", mrb_socket_gethostname, MRB_ARGS_NONE());
+  //mrb_define_class_method(mrb, sock, "getservbyname", mrb_socket_getservbyname, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+  //mrb_define_class_method(mrb, sock, "getservbyport", mrb_socket_getservbyport, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+  mrb_define_class_method(mrb, sock, "sockaddr_un", mrb_socket_sockaddr_un, MRB_ARGS_REQ(1));
+  mrb_define_class_method(mrb, sock, "socketpair", mrb_socket_socketpair, MRB_ARGS_REQ(3));
+  //mrb_define_method(mrb, sock, "sysaccept", mrb_socket_accept, MRB_ARGS_NONE());
+
+#ifndef _WIN32
+  mrb_define_class(mrb, "UNIXSocket", bsock);
+  //mrb_define_class_method(mrb, usock, "pair", mrb_unixsocket_open, MRB_ARGS_OPT(2));
+  //mrb_define_class_method(mrb, usock, "socketpair", mrb_unixsocket_open, MRB_ARGS_OPT(2));
+
+  //mrb_define_method(mrb, usock, "recv_io", mrb_unixsocket_peeraddr, MRB_ARGS_NONE());
+  //mrb_define_method(mrb, usock, "recvfrom", mrb_unixsocket_peeraddr, MRB_ARGS_NONE());
+  //mrb_define_method(mrb, usock, "send_io", mrb_unixsocket_peeraddr, MRB_ARGS_NONE());
+#endif
+
+  /* Windows IO Method Overrides on BasicSocket */
+#ifdef _WIN32
+  mrb_define_method(mrb, bsock, "close", mrb_win32_basicsocket_close, MRB_ARGS_NONE());
+  mrb_define_method(mrb, bsock, "sysread", mrb_win32_basicsocket_sysread, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+  mrb_define_method(mrb, bsock, "sysseek", mrb_win32_basicsocket_sysseek, MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, bsock, "syswrite", mrb_win32_basicsocket_syswrite, MRB_ARGS_REQ(1));
+#endif
+
+  constants = mrb_define_module_under(mrb, sock, "Constants");
+
+#define define_const(SYM) \
+  do {                                                         \
+    mrb_define_const(mrb, constants, #SYM, mrb_fixnum_value(SYM));     \
+  } while (0)
+
+#include "const.cstub"
+}
+
+void
+mrb_mruby_socket_gem_final(mrb_state* mrb)
+{
+  mrb_value ai;
+  ai = mrb_mod_cv_get(mrb, mrb_class_get(mrb, "Addrinfo"), mrb_intern_lit(mrb, "_lastai"));
+  if (mrb_cptr_p(ai)) {
+    freeaddrinfo((struct addrinfo*)mrb_cptr(ai));
+  }
+#ifdef _WIN32
+  WSACleanup();
+#endif
+}
diff --git a/third-party/mruby/mrbgems/mruby-socket/test/addrinfo.rb b/third-party/mruby/mrbgems/mruby-socket/test/addrinfo.rb
new file mode 100644 (file)
index 0000000..3ae46cd
--- /dev/null
@@ -0,0 +1,91 @@
+assert('Addrinfo') do
+  assert_equal(Class, Addrinfo.class)
+end
+
+assert('super class of Addrinfo') do
+  assert_equal(Object, Addrinfo.superclass)
+end
+
+assert('Addrinfo.getaddrinfo') do
+  ary = Addrinfo.getaddrinfo("localhost", "domain", Socket::AF_INET, Socket::SOCK_STREAM)
+  assert_true(ary.size >= 1)
+  ai = ary[0]
+  assert_equal(ai.afamily, Socket::AF_INET)
+  assert_equal(ai.pfamily, Socket::PF_INET)
+  assert_equal(ai.socktype, Socket::SOCK_STREAM)
+  assert_equal(ai.ip_address, '127.0.0.1')
+  assert_equal(ai.ip_port, 53)
+end
+
+assert('Addrinfo.foreach') do
+  # assume Addrinfo.getaddrinfo works well
+  a = Addrinfo.getaddrinfo("localhost", "domain")
+  b = []
+  Addrinfo.foreach("localhost", "domain") { |ai| b << ai }
+  assert_equal(a.size, b.size)
+end
+
+assert('Addrinfo.ip') do
+  ai = Addrinfo.ip('127.0.0.1')
+  assert_equal('127.0.0.1', ai.ip_address)
+  assert_equal(Socket::AF_INET, ai.afamily)
+  assert_equal(0, ai.ip_port)
+  assert_equal(0, ai.socktype)
+  assert_equal(0, ai.protocol)
+end
+
+assert('Addrinfo.tcp') do
+  ai = Addrinfo.tcp('127.0.0.1', 'smtp')
+  assert_equal('127.0.0.1', ai.ip_address)
+  assert_equal(Socket::AF_INET, ai.afamily)
+  assert_equal(25, ai.ip_port)
+  assert_equal(Socket::SOCK_STREAM, ai.socktype)
+  assert_equal(Socket::IPPROTO_TCP, ai.protocol)
+end
+
+assert('Addrinfo.udp') do
+  ai = Addrinfo.udp('127.0.0.1', 'domain')
+  assert_equal('127.0.0.1', ai.ip_address)
+  assert_equal(Socket::AF_INET, ai.afamily)
+  assert_equal(53, ai.ip_port)
+  assert_equal(Socket::SOCK_DGRAM, ai.socktype)
+  assert_equal(Socket::IPPROTO_UDP, ai.protocol)
+end
+
+assert('Addrinfo.unix') do
+  skip "unix is not supported on Windows" if SocketTest.win?
+  a1 = Addrinfo.unix('/tmp/sock')
+  assert_true(a1.unix?)
+  assert_equal('/tmp/sock', a1.unix_path)
+  assert_equal(Socket::SOCK_STREAM, a1.socktype)
+  a2 = Addrinfo.unix('/tmp/sock', Socket::SOCK_DGRAM)
+  assert_equal(Socket::SOCK_DGRAM, a2.socktype)
+end
+
+assert('Addrinfo#afamily') do
+  skip "afamily is not supported on Windows" if SocketTest.win?
+  ai4 = Addrinfo.new(Socket.sockaddr_in(1, '127.0.0.1'))
+  ai6 = Addrinfo.new(Socket.sockaddr_in(1, '::1'))
+  aiu = Addrinfo.new(Socket.sockaddr_un('/tmp/sock'))
+  assert_equal(Socket::AF_INET, ai4.afamily)
+  assert_equal(Socket::AF_INET6, ai6.afamily)
+  assert_equal(Socket::AF_UNIX, aiu.afamily)
+end
+
+# assert('Addrinfo#canonname') do
+
+# #getnameinfo
+# assert('Addrinfo#inspect') do
+# assert('Addrinfo#inspect_socket') do
+# assert('Addrinfo#ip?') do
+# assert('Addrinfo#ip_address') do
+# assert('Addrinfo#ip_port') do
+# assert('Addrinfo#ip_unpack') do
+# assert('Addrinfo#ipv4?') do
+# assert('Addrinfo#ipv6?') do
+# assert('Addrinfo#pfamily') do
+# assert('Addrinfo#protocol') do
+# assert('Addrinfo#socktype') do
+# assert('Addrinfo#to_sockaddr') do
+# assert('Addrinfo#unix?') do
+# #unix_path
diff --git a/third-party/mruby/mrbgems/mruby-socket/test/basicsocket.rb b/third-party/mruby/mrbgems/mruby-socket/test/basicsocket.rb
new file mode 100644 (file)
index 0000000..8fbfbdd
--- /dev/null
@@ -0,0 +1,17 @@
+assert('BasicSocket') do
+  assert_equal(Class, BasicSocket.class)
+end
+
+assert('super class of BasicSocket') do
+  assert_equal(IO, BasicSocket.superclass)
+end
+
+assert('BasicSocket.do_not_reverse_lookup') do
+  assert_equal(BasicSocket.do_not_reverse_lookup, true)
+end
+
+assert('BasicSocket.do_not_reverse_lookup=') do
+  BasicSocket.do_not_reverse_lookup = false
+  assert_equal(BasicSocket.do_not_reverse_lookup, false)
+  BasicSocket.do_not_reverse_lookup = true
+end
diff --git a/third-party/mruby/mrbgems/mruby-socket/test/ipsocket.rb b/third-party/mruby/mrbgems/mruby-socket/test/ipsocket.rb
new file mode 100644 (file)
index 0000000..b57f5d0
--- /dev/null
@@ -0,0 +1,44 @@
+unless SocketTest.win?
+
+# Note: most of tests below will fail if UDPSocket is broken.
+
+assert('IPSocket.getaddress') do
+  l = IPSocket.getaddress("localhost")
+  assert_true (l == "127.0.0.1" or l == "::1")
+end
+
+assert('IPSocket.addr') do
+  localhost = "127.0.0.1"
+  s = UDPSocket.new
+  s.bind(localhost, 0)
+  port = Addrinfo.new(s.getsockname).ip_port
+
+  a = s.addr
+  assert_equal "AF_INET", a[0]
+  assert_equal port,      a[1]
+  assert_equal localhost, a[2]
+  assert_equal localhost, a[3]
+  s.close
+  true
+end
+
+assert('IPSocket.peeraddr') do
+  localhost = "127.0.0.1"
+  server = UDPSocket.new
+  server.bind(localhost, 0)
+  port = server.local_address.ip_port
+
+  client = UDPSocket.new
+  client.connect(localhost, port)
+
+  a = client.peeraddr
+  assert_equal "AF_INET", a[0]
+  assert_equal port,      a[1]
+  assert_equal localhost, a[2]
+  assert_equal localhost, a[3]
+  client.close
+  server.close
+  true
+end
+
+end # win?
diff --git a/third-party/mruby/mrbgems/mruby-socket/test/socket.rb b/third-party/mruby/mrbgems/mruby-socket/test/socket.rb
new file mode 100644 (file)
index 0000000..aa89358
--- /dev/null
@@ -0,0 +1,38 @@
+unless SocketTest.win?
+
+assert('Socket.gethostname') do
+  assert_true(Socket.gethostname.is_a? String)
+end
+
+assert('Socket::getaddrinfo') do
+  ret = Socket.getaddrinfo("localhost", "domain", Socket::AF_INET, Socket::SOCK_DGRAM)
+  assert_true ret.size >= 1
+  a = ret[0]
+  assert_equal "AF_INET",           a[0]
+  assert_equal 53,                  a[1]
+  # documents says it's a hostname but CRuby returns an address
+  #assert_equal "127.0.0.1",         a[2]
+  assert_equal "127.0.0.1",         a[3]
+  assert_equal Socket::AF_INET,     a[4]
+  assert_equal Socket::SOCK_DGRAM,  a[5]
+  assert_equal Socket::IPPROTO_UDP, a[6] unless SocketTest.cygwin?
+end
+
+assert('Socket#recvfrom') do
+  begin
+    sstr = "abcdefg"
+    s = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)
+    c = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)
+    s.bind(Socket.sockaddr_in(0, "127.0.0.1"))
+    c.send sstr, 0, s.getsockname
+    rstr, ai = s.recvfrom sstr.size
+
+    assert_equal sstr, rstr
+    assert_true "127.0.0.1", ai.ip_address
+  ensure
+    s.close rescue nil
+    c.close rescue nil
+  end
+end
+
+end   # win?
diff --git a/third-party/mruby/mrbgems/mruby-socket/test/sockettest.c b/third-party/mruby/mrbgems/mruby-socket/test/sockettest.c
new file mode 100644 (file)
index 0000000..086bc48
--- /dev/null
@@ -0,0 +1,82 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "mruby.h"
+#include "mruby/error.h"
+
+#if defined(_WIN32) || defined(_WIN64)
+
+#include <io.h>
+
+#ifdef _MSC_VER
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#define close _close
+#define unlink _unlink
+
+static int
+mkstemp(char *p)
+{
+  int fd;
+  char* fname = _mktemp(p);
+  if (fname == NULL)
+    return -1;
+  fd = open(fname, O_RDWR | O_CREAT | O_EXCL, _S_IREAD | _S_IWRITE);
+  if (fd >= 0)
+    return fd;
+  return -1;
+}
+#endif
+
+#else
+
+#include <unistd.h>
+
+#endif
+
+mrb_value
+mrb_sockettest_tmppath(mrb_state *mrb, mrb_value klass)
+{
+  char name[] = "mruby-socket.XXXXXXXX";
+  int fd = mkstemp(name);
+  if (fd == -1) {
+    mrb_sys_fail(mrb, 0);
+  }
+  if (close(fd) == -1) {
+    mrb_sys_fail(mrb, 0);
+  }
+  if (unlink(name) == -1) {
+    mrb_sys_fail(mrb, 0);
+  }
+  return mrb_str_new_cstr(mrb, name);
+}
+
+mrb_value
+mrb_sockettest_win_p(mrb_state *mrb, mrb_value klass)
+{
+#ifdef _WIN32
+  return mrb_true_value();
+#else
+  return mrb_false_value();
+#endif
+}
+
+mrb_value
+mrb_sockettest_cygwin_p(mrb_state *mrb, mrb_value klass)
+{
+#if defined(__CYGWIN__) || defined(__CYGWIN32__)
+  return mrb_true_value();
+#else
+  return mrb_false_value();
+#endif
+}
+
+void
+mrb_mruby_socket_gem_test(mrb_state* mrb)
+{
+  struct RClass *c = mrb_define_module(mrb, "SocketTest");
+  mrb_define_class_method(mrb, c, "tmppath", mrb_sockettest_tmppath, MRB_ARGS_NONE());
+  mrb_define_class_method(mrb, c, "win?", mrb_sockettest_win_p, MRB_ARGS_NONE());
+  mrb_define_class_method(mrb, c, "cygwin?", mrb_sockettest_cygwin_p, MRB_ARGS_NONE());
+}
diff --git a/third-party/mruby/mrbgems/mruby-socket/test/tcpsocket.rb b/third-party/mruby/mrbgems/mruby-socket/test/tcpsocket.rb
new file mode 100644 (file)
index 0000000..7056ec1
--- /dev/null
@@ -0,0 +1,4 @@
+#assert('TCPSocket.gethostbyname') do
+#assert('TCPSocket.new') do
+#assert('TCPSocket#close') do
+#assert('TCPSocket#write') do
diff --git a/third-party/mruby/mrbgems/mruby-socket/test/udpsocket.rb b/third-party/mruby/mrbgems/mruby-socket/test/udpsocket.rb
new file mode 100644 (file)
index 0000000..bb57ed0
--- /dev/null
@@ -0,0 +1,16 @@
+assert('UDPSocket.new') do
+  s = UDPSocket.new
+  assert_true(s.is_a? UDPSocket)
+  s.close
+  s = UDPSocket.new(Socket::AF_INET6)
+  assert_true(s.is_a? UDPSocket)
+  s.close
+  true
+end
+
+#assert('UDPSocket#connect') do
+#assert('UDPSocket#send') do
+#assert('UDPSocket#recv') do
+
+#assert('UDPSocket#bind') do
+#assert('UDPSocket#recvfrom_nonblock') do
diff --git a/third-party/mruby/mrbgems/mruby-socket/test/unix.rb b/third-party/mruby/mrbgems/mruby-socket/test/unix.rb
new file mode 100644 (file)
index 0000000..4a88fba
--- /dev/null
@@ -0,0 +1,130 @@
+unless SocketTest.win? || SocketTest.cygwin?
+
+def unixserver_test_block
+  path = SocketTest.tmppath
+  File.unlink path rescue nil
+  begin
+    result = yield path
+  ensure
+    File.unlink path rescue nil
+  end
+  result
+end
+
+def with_unix_server
+  unixserver_test_block do |path|
+    UNIXServer.open(path) { |server|
+      yield path, server
+    }
+  end
+end
+
+def with_unix_client
+  with_unix_server do |path, server|
+    UNIXSocket.open(path) do |csock|
+      ssock = server.accept
+      begin
+        yield path, server, ssock, csock
+      ensure
+        ssock.close unless ssock.closed? rescue nil
+      end
+    end
+  end
+end
+
+assert('UNIXServer.new') do
+  unixserver_test_block do |path|
+    server = UNIXServer.new(path)
+    assert_true server.is_a? UNIXServer
+    server.close
+    File.unlink path
+
+    s2 = nil
+    result = UNIXServer.open(path) { |s1|
+      assert_true s1.is_a? UNIXServer
+      s2 = s1
+      1234
+    }
+    assert_equal 1234, result
+    assert_true s2.is_a? UNIXServer
+    assert_true s2.closed?
+  end
+end
+
+# assert('UNIXServer#accept_nonblock') - would block if fails
+
+assert('UNIXServer#addr') do
+  with_unix_server do |path, server|
+    assert_equal [ "AF_UNIX", path], server.addr
+  end
+end
+
+assert('UNIXServer#path') do
+  with_unix_server do |path, server|
+    assert_equal path, server.path
+  end
+end
+
+# assert('UNIXServer#peeraddr') - will raise a runtime exception
+
+assert('UNIXServer#listen') do
+  with_unix_server do |path, server|
+    assert_equal 0, server.listen(1)
+  end
+end
+
+assert('UNIXServer#sysaccept') do
+  with_unix_server do |path, server|
+    UNIXSocket.open(path) do |csock|
+      begin
+        fd = server.sysaccept
+        assert_true fd.kind_of? Integer
+      ensure
+        IO._sysclose(fd) rescue nil
+      end
+    end
+  end
+end
+
+assert('UNIXSocket.new') do
+  with_unix_server do |path, server|
+    c = UNIXSocket.new(path)
+    assert_true c.is_a? UNIXSocket
+    c.close
+    true
+  end
+end
+
+assert('UNIXSocket#addr') do
+  with_unix_client do |path, server, ssock, csock|
+    assert_equal [ "AF_UNIX", path ], ssock.addr
+    assert_equal [ "AF_UNIX", "" ],   csock.addr
+  end
+end
+
+assert('UNIXSocket#path') do
+  with_unix_client do |path, server, ssock, csock|
+    assert_equal path, ssock.path
+    assert_equal "",   csock.path
+  end
+end
+
+assert('UNIXSocket#peeraddr') do
+  with_unix_client do |path, server, ssock, csock|
+    assert_equal [ "AF_UNIX", ""   ], ssock.peeraddr
+    assert_equal [ "AF_UNIX", path ], csock.peeraddr
+  end
+end
+
+assert('UNIXSocket#recvfrom') do
+  with_unix_client do |path, server, ssock, csock|
+    str = "0123456789"
+    ssock.send str, 0
+    a = csock.recvfrom(8)
+    assert_equal str[0, 8], a[0]
+    assert_equal "AF_UNIX", a[1][0]
+    # a[1][1] would be "" or something
+  end
+end
+
+end # SocketTest.win?
index 7b82017..7eea1a1 100644 (file)
 #include <mruby/string.h>
 #include <mruby/hash.h>
 #include <mruby/numeric.h>
+#ifndef MRB_WITHOUT_FLOAT
 #include <math.h>
+#endif
 #include <ctype.h>
 
 #define BIT_DIGITS(N)   (((N)*146)/485 + 1)  /* log2(10) =~ 146/485 */
 #define BITSPERDIG MRB_INT_BIT
 #define EXTENDSIGN(n, l) (((~0U << (n)) >> (((n)*(l)) % BITSPERDIG)) & ~(~0U << (n)))
 
-mrb_value mrb_str_format(mrb_state *, int, const mrb_value *, mrb_value);
+mrb_value mrb_str_format(mrb_state *, mrb_int, const mrb_value *, mrb_value);
+#ifndef MRB_WITHOUT_FLOAT
 static void fmt_setup(char*,size_t,int,int,mrb_int,mrb_int);
+#endif
 
 static char*
 remove_sign_bits(char *str, int base)
@@ -128,7 +132,7 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
 #define PUSH(s, l) do { \
   CHECK(l);\
   memcpy(&buf[blen], s, l);\
-  blen += (l);\
+  blen += (mrb_int)(l);\
 } while (0)
 
 #define FILL(c, l) do { \
@@ -153,7 +157,7 @@ check_next_arg(mrb_state *mrb, int posarg, int nextarg)
 }
 
 static void
-check_pos_arg(mrb_state *mrb, int posarg, int n)
+check_pos_arg(mrb_state *mrb, mrb_int posarg, mrb_int n)
 {
   if (posarg > 0) {
     mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after unnumbered(%S)",
@@ -168,7 +172,7 @@ check_pos_arg(mrb_state *mrb, int posarg, int n)
 }
 
 static void
-check_name_arg(mrb_state *mrb, int posarg, const char *name, int len)
+check_name_arg(mrb_state *mrb, int posarg, const char *name, mrb_int len)
 {
   if (posarg > 0) {
     mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after unnumbered(%S)",
@@ -198,7 +202,7 @@ check_name_arg(mrb_state *mrb, int posarg, const char *name, int len)
 
 #define GETNUM(n, val) \
   for (; p < end && ISDIGIT(*p); p++) {\
-    int next_n = 10 * n + (*p - '0'); \
+    mrb_int next_n = 10 * n + (*p - '0'); \
     if (next_n / 10 != n) {\
       mrb_raise(mrb, E_ARGUMENT_ERROR, #val " too big"); \
     } \
@@ -224,7 +228,7 @@ check_name_arg(mrb_state *mrb, int posarg, const char *name, int len)
 } while (0)
 
 static mrb_value
-get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv)
+get_hash(mrb_state *mrb, mrb_value *hash, mrb_int argc, const mrb_value *argv)
 {
   mrb_value tmp;
 
@@ -518,7 +522,7 @@ mrb_f_sprintf(mrb_state *mrb, mrb_value obj)
 }
 
 mrb_value
-mrb_str_format(mrb_state *mrb, int argc, const mrb_value *argv, mrb_value fmt)
+mrb_str_format(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value fmt)
 {
   const char *p, *end;
   char *buf;
@@ -556,7 +560,7 @@ mrb_str_format(mrb_state *mrb, int argc, const mrb_value *argv, mrb_value fmt)
   end = p + RSTRING_LEN(fmt);
   blen = 0;
   bsiz = 120;
-  result = mrb_str_buf_new(mrb, bsiz);
+  result = mrb_str_new_capa(mrb, bsiz);
   buf = RSTRING_PTR(result);
   memset(buf, 0, bsiz);
 
@@ -643,7 +647,7 @@ retry:
         }
         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));
+        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));
         }
@@ -765,7 +769,7 @@ retry:
           if ((flags&FPREC) && (prec < slen)) {
             char *p = RSTRING_PTR(str) + prec;
             slen = prec;
-            len = p - RSTRING_PTR(str);
+            len = (mrb_int)(p - RSTRING_PTR(str));
           }
           /* need to adjust multi-byte string pos */
           if ((flags&FWIDTH) && (width > slen)) {
@@ -814,10 +818,12 @@ retry:
 
   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;
@@ -990,6 +996,7 @@ retry:
       }
       break;
 
+#ifndef MRB_WITHOUT_FLOAT
       case 'f':
       case 'g':
       case 'G':
@@ -999,13 +1006,15 @@ retry:
       case 'A': {
         mrb_value val = GETARG();
         double fval;
-        int i, need = 6;
+        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 int elen = 3;
+          const mrb_int elen = 3;
           char sign = '\0';
 
           if (isnan(fval)) {
@@ -1020,7 +1029,7 @@ retry:
           else if (flags & (FPLUS|FSPACE))
             sign = (flags & FPLUS) ? '+' : ' ';
           if (sign)
-           ++need;
+            ++need;
           if ((flags & FWIDTH) && need < width)
             need = width;
 
@@ -1045,7 +1054,8 @@ retry:
         need = 0;
         if (*p != 'e' && *p != 'E') {
           i = INT_MIN;
-          frexp(fval, &i);
+          frexp(fval, &frexp_result);
+          i = (mrb_int)frexp_result;
           if (i > 0)
             need = BIT_DIGITS(i);
         }
@@ -1066,6 +1076,7 @@ retry:
         blen += n;
       }
       break;
+#endif
     }
     flags = FNONE;
   }
@@ -1085,6 +1096,7 @@ retry:
   return result;
 }
 
+#ifndef MRB_WITHOUT_FLOAT
 static void
 fmt_setup(char *buf, size_t size, int c, int flags, mrb_int width, mrb_int prec)
 {
@@ -1111,3 +1123,4 @@ fmt_setup(char *buf, size_t size, int c, int flags, mrb_int width, mrb_int prec)
   *buf++ = c;
   *buf = '\0';
 }
+#endif
index 84dba9d..a5fd4e6 100644 (file)
@@ -3,7 +3,8 @@
 
 assert('String#%') do
   assert_equal "one=1", "one=%d" % 1
-  assert_equal "1 one 1.0", "%d %s %3.1f" % [ 1, "one", 1.01 ]
+  assert_equal "1 one", "%d %s" % [ 1, "one" ]
+  assert_equal "1.0", "%3.1f" % 1.01 if class_defined?("Float")
   assert_equal "123 < 456", "%{num} < %<str>s" % { num: 123, str: "456" }
   assert_equal 15, ("%b" % (1<<14)).size
 end
@@ -34,7 +35,7 @@ assert('String#% with inf') do
   assert_equal " Inf", "% 3f" % inf
   assert_equal " Inf", "% 4f" % inf
   assert_equal "  Inf", "% 5f" % inf
-end
+end if class_defined?("Float")
 
 assert('String#% with nan') do
   nan = Float::NAN
@@ -62,7 +63,7 @@ assert('String#% with nan') do
   assert_equal " NaN", "% 3f" % nan
   assert_equal " NaN", "% 4f" % nan
   assert_equal "  NaN", "% 5f" % nan
-end
+end if class_defined?("Float")
 
 assert("String#% with invalid chr") do
   begin
@@ -94,7 +95,7 @@ end
 assert("String#% %d") do
   assert_equal("  10",   "%4d" % 10)
   assert_equal("1000",   "%4d" % 1000)
-  assert_equal("100000", "%4d" % 100000)
+  assert_equal("10000",  "%4d" % 10000)
 end
 
 assert("String#% invalid format") do
index c3a7eb3..0da84da 100644 (file)
@@ -95,7 +95,7 @@ class String
   #    "hello".lstrip!      #=> nil
   #
   def lstrip!
-    raise RuntimeError, "can't modify frozen String" if frozen?
+    raise FrozenError, "can't modify frozen String" if frozen?
     s = self.lstrip
     (s == self) ? nil : self.replace(s)
   end
@@ -125,7 +125,7 @@ class String
   #  <code>nil</code> if <i>str</i> was not altered.
   #
   def strip!
-    raise RuntimeError, "can't modify frozen String" if frozen?
+    raise FrozenError, "can't modify frozen String" if frozen?
     s = self.strip
     (s == self) ? nil : self.replace(s)
   end
@@ -144,7 +144,20 @@ class String
   def casecmp(str)
     self.downcase <=> str.to_str.downcase
   rescue NoMethodError
-    raise TypeError, "no implicit conversion of #{str.class} into String"
+    nil
+  end
+
+  ##
+  # call-seq:
+  #   str.casecmp?(other)  -> true, false, or nil
+  #
+  # Returns true if str and other_str are equal after case folding,
+  # false if they are not equal, and nil if other_str is not a string.
+
+  def casecmp?(str)
+    c = self.casecmp(str)
+    return nil if c.nil?
+    return c == 0
   end
 
   def partition(sep)
@@ -186,7 +199,7 @@ class String
   #    string                  #=> "thsa sting"
   #
   def slice!(arg1, arg2=nil)
-    raise RuntimeError, "can't modify frozen String" if frozen?
+    raise FrozenError, "can't modify frozen String" if frozen?
     raise "wrong number of arguments (for 1..2)" if arg1.nil? && arg2.nil?
 
     if !arg1.nil? && !arg2.nil?
index 0d834b6..45e8bb4 100644 (file)
@@ -23,7 +23,7 @@ static mrb_value
 mrb_str_setbyte(mrb_state *mrb, mrb_value str)
 {
   mrb_int pos, byte;
-  long len;
+  mrb_int len;
 
   mrb_get_args(mrb, "ii", &pos, &byte);
 
@@ -35,7 +35,7 @@ mrb_str_setbyte(mrb_state *mrb, mrb_value str)
 
   mrb_str_modify(mrb, mrb_str_ptr(str));
   byte &= 0xff;
-  RSTRING_PTR(str)[pos] = byte;
+  RSTRING_PTR(str)[pos] = (unsigned char)byte;
   return mrb_fixnum_value((unsigned char)byte);
 }
 
@@ -44,12 +44,13 @@ mrb_str_byteslice(mrb_state *mrb, mrb_value str)
 {
   mrb_value a1;
   mrb_int len;
-  int argc;
 
-  argc = mrb_get_args(mrb, "o|i", &a1, &len);
-  if (argc == 2) {
-    return mrb_str_substr(mrb, str, mrb_fixnum(a1), len);
+  if (mrb_get_argc(mrb) == 2) {
+    mrb_int pos;
+    mrb_get_args(mrb, "ii", &pos, &len);
+    return mrb_str_substr(mrb, str, pos, len);
   }
+  mrb_get_args(mrb, "o|i", &a1, &len);
   switch (mrb_type(a1)) {
   case MRB_TT_RANGE:
     {
@@ -67,9 +68,11 @@ mrb_str_byteslice(mrb_state *mrb, mrb_value str)
       }
       return mrb_nil_value();
     }
+#ifndef MRB_WITHOUT_FLOAT
   case MRB_TT_FLOAT:
     a1 = mrb_fixnum_value((mrb_int)mrb_float(a1));
     /* fall through */
+#endif
   case MRB_TT_FIXNUM:
     return mrb_str_substr(mrb, str, mrb_fixnum(a1), 1);
   default:
@@ -152,7 +155,7 @@ static mrb_value mrb_fixnum_chr(mrb_state *mrb, mrb_value num);
  *     a.concat(33)   #=> "hello world!"
  */
 static mrb_value
-mrb_str_concat2(mrb_state *mrb, mrb_value self)
+mrb_str_concat_m(mrb_state *mrb, mrb_value self)
 {
   mrb_value str;
 
@@ -650,6 +653,118 @@ mrb_str_upto(mrb_state *mrb, mrb_value beg)
   return beg;
 }
 
+/*
+ *  call-seq:
+ *     str.delete_prefix!(prefix) -> self or nil
+ *
+ *  Deletes leading <code>prefix</code> from <i>str</i>, returning
+ *  <code>nil</code> if no change was made.
+ *
+ *     "hello".delete_prefix!("hel") #=> "lo"
+ *     "hello".delete_prefix!("llo") #=> nil
+ */
+static mrb_value
+mrb_str_del_prefix_bang(mrb_state *mrb, mrb_value self)
+{
+  mrb_int plen, slen;
+  char *ptr, *s;
+  struct RString *str = RSTRING(self);
+
+  mrb_get_args(mrb, "s", &ptr, &plen);
+  slen = RSTR_LEN(str);
+  if (plen > slen) return mrb_nil_value();
+  s = RSTR_PTR(str);
+  if (memcmp(s, ptr, plen) != 0) return mrb_nil_value();
+  if (!MRB_FROZEN_P(str) && (RSTR_SHARED_P(str) || RSTR_FSHARED_P(str))) {
+    str->as.heap.ptr += plen;
+  }
+  else {
+    mrb_str_modify(mrb, str);
+    s = RSTR_PTR(str);
+    memmove(s, s+plen, slen-plen);
+  }
+  RSTR_SET_LEN(str, slen-plen);
+  return self;
+}
+
+/*
+ *  call-seq:
+ *     str.delete_prefix(prefix) -> new_str
+ *
+ *  Returns a copy of <i>str</i> with leading <code>prefix</code> deleted.
+ *
+ *     "hello".delete_prefix("hel") #=> "lo"
+ *     "hello".delete_prefix("llo") #=> "hello"
+ */
+static mrb_value
+mrb_str_del_prefix(mrb_state *mrb, mrb_value self)
+{
+  mrb_int plen, slen;
+  char *ptr;
+
+  mrb_get_args(mrb, "s", &ptr, &plen);
+  slen = RSTRING_LEN(self);
+  if (plen > slen) return mrb_str_dup(mrb, self);
+  if (memcmp(RSTRING_PTR(self), ptr, plen) != 0)
+    return mrb_str_dup(mrb, self);
+  return mrb_str_substr(mrb, self, plen, slen-plen);
+}
+
+/*
+ *  call-seq:
+ *     str.delete_suffix!(suffix) -> self or nil
+ *
+ *  Deletes trailing <code>suffix</code> from <i>str</i>, returning
+ *  <code>nil</code> if no change was made.
+ *
+ *     "hello".delete_suffix!("llo") #=> "he"
+ *     "hello".delete_suffix!("hel") #=> nil
+ */
+static mrb_value
+mrb_str_del_suffix_bang(mrb_state *mrb, mrb_value self)
+{
+  mrb_int plen, slen;
+  char *ptr, *s;
+  struct RString *str = RSTRING(self);
+
+  mrb_get_args(mrb, "s", &ptr, &plen);
+  slen = RSTR_LEN(str);
+  if (plen > slen) return mrb_nil_value();
+  s = RSTR_PTR(str);
+  if (memcmp(s+slen-plen, ptr, plen) != 0) return mrb_nil_value();
+  if (!MRB_FROZEN_P(str) && (RSTR_SHARED_P(str) || RSTR_FSHARED_P(str))) {
+    /* no need to modify string */
+  }
+  else {
+    mrb_str_modify(mrb, str);
+  }
+  RSTR_SET_LEN(str, slen-plen);
+  return self;
+}
+
+/*
+ *  call-seq:
+ *     str.delete_suffix(suffix) -> new_str
+ *
+ *  Returns a copy of <i>str</i> with leading <code>suffix</code> deleted.
+ *
+ *     "hello".delete_suffix("hel") #=> "lo"
+ *     "hello".delete_suffix("llo") #=> "hello"
+ */
+static mrb_value
+mrb_str_del_suffix(mrb_state *mrb, mrb_value self)
+{
+  mrb_int plen, slen;
+  char *ptr;
+
+  mrb_get_args(mrb, "s", &ptr, &plen);
+  slen = RSTRING_LEN(self);
+  if (plen > slen) return mrb_str_dup(mrb, self);
+  if (memcmp(RSTRING_PTR(self)+slen-plen, ptr, plen) != 0)
+    return mrb_str_dup(mrb, self);
+  return mrb_str_substr(mrb, self, 0, slen-plen);
+}
+
 void
 mrb_mruby_string_ext_gem_init(mrb_state* mrb)
 {
@@ -661,8 +776,8 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb)
   mrb_define_method(mrb, s, "byteslice",       mrb_str_byteslice,       MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
   mrb_define_method(mrb, s, "swapcase!",       mrb_str_swapcase_bang,   MRB_ARGS_NONE());
   mrb_define_method(mrb, s, "swapcase",        mrb_str_swapcase,        MRB_ARGS_NONE());
-  mrb_define_method(mrb, s, "concat",          mrb_str_concat2,         MRB_ARGS_REQ(1));
-  mrb_define_method(mrb, s, "<<",              mrb_str_concat2,         MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, s, "concat",          mrb_str_concat_m,        MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, s, "<<",              mrb_str_concat_m,        MRB_ARGS_REQ(1));
   mrb_define_method(mrb, s, "start_with?",     mrb_str_start_with,      MRB_ARGS_REST());
   mrb_define_method(mrb, s, "end_with?",       mrb_str_end_with,        MRB_ARGS_REST());
   mrb_define_method(mrb, s, "hex",             mrb_str_hex,             MRB_ARGS_NONE());
@@ -673,8 +788,12 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb)
   mrb_define_method(mrb, s, "succ!",           mrb_str_succ_bang,       MRB_ARGS_NONE());
   mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next"), mrb_intern_lit(mrb, "succ"));
   mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next!"), mrb_intern_lit(mrb, "succ!"));
-  mrb_define_method(mrb, s, "ord", mrb_str_ord, MRB_ARGS_NONE());
-  mrb_define_method(mrb, s, "upto", mrb_str_upto, MRB_ARGS_ANY());
+  mrb_define_method(mrb, s, "ord",             mrb_str_ord,             MRB_ARGS_NONE());
+  mrb_define_method(mrb, s, "upto",            mrb_str_upto,            MRB_ARGS_ANY());
+  mrb_define_method(mrb, s, "delete_prefix!",  mrb_str_del_prefix_bang, MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, s, "delete_prefix",   mrb_str_del_prefix,      MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, s, "delete_suffix!",  mrb_str_del_suffix_bang, MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, s, "delete_suffix",   mrb_str_del_suffix,      MRB_ARGS_REQ(1));
 
   mrb_define_method(mrb, mrb->fixnum_class, "chr", mrb_fixnum_chr, MRB_ARGS_NONE());
 }
index 2a568c7..b6146fb 100644 (file)
@@ -1,3 +1,4 @@
+# coding: utf-8
 ##
 # String(Ext) Test
 
@@ -665,3 +666,17 @@ assert('String#each_codepoint(UTF-8)') do
   end
   assert_equal expect, cp
 end if UTF8STRING
+
+assert('String#delete_prefix') do
+  assert_equal "llo", "hello".delete_prefix("he")
+  assert_equal "hello", "hello".delete_prefix("llo")
+  assert_equal "llo", "hello".delete_prefix!("he")
+  assert_nil "hello".delete_prefix!("llo")
+end
+
+assert('String#delete_suffix') do
+  assert_equal "he", "hello".delete_suffix("llo")
+  assert_equal "hello", "hello".delete_suffix("he")
+  assert_equal "he", "hello".delete_suffix!("llo")
+  assert_nil "hello".delete_suffix!("he")
+end
index 67762a9..adeb09b 100644 (file)
@@ -12,6 +12,7 @@
 #include <mruby/variable.h>
 #include <mruby/hash.h>
 #include <mruby/range.h>
+#include <mruby/proc.h>
 
 #define RSTRUCT_LEN(st) RARRAY_LEN(st)
 #define RSTRUCT_PTR(st) RARRAY_PTR(st)
@@ -88,7 +89,7 @@ static void
 mrb_struct_modify(mrb_state *mrb, mrb_value strct)
 {
   if (MRB_FROZEN_P(mrb_basic_ptr(strct))) {
-    mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen struct");
+    mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen struct");
   }
 
   mrb_write_barrier(mrb, mrb_basic_ptr(strct));
@@ -113,12 +114,14 @@ mrb_struct_members(mrb_state *mrb, mrb_value obj)
   return mrb_struct_s_members_m(mrb, mrb_obj_value(mrb_obj_class(mrb, obj)));
 }
 
-static mrb_value struct_aref_sym(mrb_state *mrb, mrb_value obj, mrb_sym id);
-
 static mrb_value
 mrb_struct_ref(mrb_state *mrb, mrb_value obj)
 {
-  return struct_aref_sym(mrb, obj, mrb->c->ci->mid);
+  mrb_int i = mrb_fixnum(mrb_proc_cfunc_env_get(mrb, 0));
+  mrb_value *ptr = RSTRUCT_PTR(obj);
+
+  if (!ptr) return mrb_nil_value();
+  return ptr[i];
 }
 
 static mrb_sym
@@ -140,24 +143,23 @@ mrb_id_attrset(mrb_state *mrb, mrb_sym id)
   return mid;
 }
 
-static mrb_value mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val);
-
 static mrb_value
 mrb_struct_set_m(mrb_state *mrb, mrb_value obj)
 {
+  mrb_int i = mrb_fixnum(mrb_proc_cfunc_env_get(mrb, 0));
+  mrb_value *ptr;
   mrb_value val;
 
-  const char *name;
-  mrb_int slen;
-  mrb_sym mid;
-
   mrb_get_args(mrb, "o", &val);
-
-  /* get base id */
-  name = mrb_sym2name_len(mrb, mrb->c->ci->mid, &slen);
-  mid = mrb_intern(mrb, name, slen-1); /* omit last "=" */
-
-  return mrb_struct_aset_sym(mrb, obj, mid, val);
+  mrb_struct_modify(mrb, obj);
+  ptr = RSTRUCT_PTR(obj);
+  if (ptr == NULL || i >= RSTRUCT_LEN(obj)) {
+    mrb_ary_set(mrb, obj, i, val);
+  }
+  else {
+    ptr[i] = val;
+  }
+  return val;
 }
 
 static mrb_bool
@@ -187,15 +189,21 @@ make_struct_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c
     const char *name = mrb_sym2name_len(mrb, id, NULL);
 
     if (is_local_id(mrb, name) || is_const_id(mrb, name)) {
-      mrb_define_method_id(mrb, c, id, mrb_struct_ref, MRB_ARGS_NONE());
-      mrb_define_method_id(mrb, c, mrb_id_attrset(mrb, id), mrb_struct_set_m, MRB_ARGS_REQ(1));
+      mrb_method_t m;
+      mrb_value at = mrb_fixnum_value(i);
+      struct RProc *aref = mrb_proc_new_cfunc_with_env(mrb, mrb_struct_ref, 1, &at);
+      struct RProc *aset = mrb_proc_new_cfunc_with_env(mrb, mrb_struct_set_m, 1, &at);
+      MRB_METHOD_FROM_PROC(m, aref);
+      mrb_define_method_raw(mrb, c, id, m);
+      MRB_METHOD_FROM_PROC(m, aset);
+      mrb_define_method_raw(mrb, c, mrb_id_attrset(mrb, id), m);
       mrb_gc_arena_restore(mrb, ai);
     }
   }
 }
 
 static mrb_value
-make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * klass)
+make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass *klass)
 {
   mrb_value nstr;
   mrb_sym id;
@@ -278,17 +286,21 @@ mrb_struct_s_def(mrb_state *mrb, mrb_value klass)
   name = mrb_nil_value();
   mrb_get_args(mrb, "*&", &argv, &argc, &b);
   if (argc == 0) { /* special case to avoid crash */
-    rest = mrb_ary_new(mrb);
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments");
   }
   else {
-    if (argc > 0) name = argv[0];
-    pargv = &argv[1];
-    argcnt = argc-1;
-    if (!mrb_nil_p(name) && mrb_symbol_p(name)) {
-      /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */
-      name = mrb_nil_value();
-      pargv = &argv[0];
-      argcnt++;
+    pargv = argv;
+    argcnt = argc;
+    if (argc > 0) {
+      name = argv[0];
+      if (mrb_symbol_p(name)) {
+        /* 1stArgument:symbol -> name=nil rest=argv[0..n] */
+        name = mrb_nil_value();
+      }
+      else {
+        pargv++;
+        argcnt--;
+      }
     }
     rest = mrb_ary_new_from_values(mrb, argcnt, pargv);
     for (i=0; i<RARRAY_LEN(rest); i++) {
index 421fe4b..982e344 100644 (file)
@@ -11,13 +11,6 @@ assert('Struct.new', '15.2.18.3.1') do
   assert_equal [:m1, :m2], c.members
 end
 
-# Check crash bug with Struc.new and no params.
-assert('Struct.new', '15.2.18.3.1') do
-  c = Struct.new()
-  assert_equal Struct, c.superclass
-  assert_equal [], c.members
-end
-
 assert('Struct#==', '15.2.18.4.1') do
   c = Struct.new(:m1, :m2)
   cc1 = c.new(1,2)
@@ -192,7 +185,7 @@ assert("Struct.new generates subclass of Struct") do
   begin
     original_struct = Struct
     Struct = String
-    assert_equal original_struct, original_struct.new.superclass
+    assert_equal original_struct, original_struct.new(:foo).superclass
   ensure
     Struct = original_struct
   end
index 1e3d24b..28cce31 100644 (file)
@@ -48,10 +48,23 @@ class Symbol
   def casecmp(other)
     return nil unless other.kind_of?(Symbol)
     lhs =  self.to_s; lhs.upcase!
-    rhs = other.to_s; rhs.upcase!
+    rhs = other.to_s.upcase
     lhs <=> rhs
   end
 
+  ##
+  # call-seq:
+  #   sym.casecmp?(other)  -> true, false, or nil
+  #
+  # Returns true if sym and other_sym are equal after case folding,
+  # false if they are not equal, and nil if other_sym is not a string.
+
+  def casecmp?(sym)
+    c = self.casecmp(sym)
+    return nil if c.nil?
+    return c == 0
+  end
+
   #
   # call-seq:
   #   sym.empty?   -> true or false
index 14e93ff..434d1fe 100644 (file)
@@ -60,12 +60,13 @@ static void
 t_printstr(mrb_state *mrb, mrb_value obj)
 {
   char *s;
-  int len;
+  mrb_int len;
 
   if (mrb_string_p(obj)) {
     s = RSTRING_PTR(obj);
     len = RSTRING_LEN(obj);
     fwrite(s, len, 1, stdout);
+    fflush(stdout);
   }
 }
 
@@ -94,11 +95,13 @@ mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose)
   mrb_define_const(mrb, mrbtest, "FIXNUM_MIN", mrb_fixnum_value(MRB_INT_MIN));
   mrb_define_const(mrb, mrbtest, "FIXNUM_BIT", mrb_fixnum_value(MRB_INT_BIT));
 
+#ifndef MRB_WITHOUT_FLOAT
 #ifdef MRB_USE_FLOAT
   mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-6));
 #else
   mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-12));
 #endif
+#endif
 
   if (verbose) {
     mrb_gv_set(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"), mrb_true_value());
index 17ac1bd..7678a52 100644 (file)
@@ -31,6 +31,7 @@ mrb_init_mrbtest(mrb_state *mrb)
 
   if (mrb->exc) {
     mrb_print_error(mrb);
+    mrb_close(mrb);
     exit(EXIT_FAILURE);
   }
   mrb_close(core_test);
index ae4c2f1..7da0b5e 100644 (file)
@@ -7,6 +7,10 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
   spec.add_dependency('mruby-compiler', :core => 'mruby-compiler')
 
   spec.test_rbfiles = Dir.glob("#{MRUBY_ROOT}/test/t/*.rb")
+  if build.cc.defines.flatten.include?("MRB_WITHOUT_FLOAT")
+    spec.test_rbfiles.delete("#{MRUBY_ROOT}/test/t/float.rb")
+  end
+
 
   clib = "#{build_dir}/mrbtest.c"
   mlib = clib.ext(exts.object)
@@ -97,6 +101,7 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
             end
             f.puts %Q[  if (mrb2->exc) {]
             f.puts %Q[    mrb_print_error(mrb2);]
+            f.puts %Q[    mrb_close(mrb2);]
             f.puts %Q[    exit(EXIT_FAILURE);]
             f.puts %Q[  }]
             f.puts %Q[  mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "GEMNAME"), mrb_str_new(mrb2, "#{g.name}", #{g.name.length}));]
index 5e86248..cfd51ac 100644 (file)
@@ -10,7 +10,7 @@
 #include <mruby/class.h>
 #include <mruby/data.h>
 
-#ifndef DISABLE_STDIO
+#ifndef MRB_DISABLE_STDIO
 #include <stdio.h>
 #else
 #include <string.h>
@@ -67,6 +67,11 @@ double round(double x) {
 /* define following macro to use probably faster timegm() on the platform */
 /* #define USE_SYSTEM_TIMEGM */
 
+/* time_t */
+/* If your platform supports time_t as uint (e.g. uint32_t, uint64_t), */
+/* uncomment following macro. */
+/* #define MRB_TIME_T_UINT */
+
 /** end of Time class configuration */
 
 #ifndef NO_GETTIMEOFDAY
@@ -138,8 +143,14 @@ timegm(struct tm *tm)
   int i;
   unsigned int *nday = (unsigned int*) ndays[is_leapyear(tm->tm_year+1900)];
 
-  for (i = 70; i < tm->tm_year; ++i)
-    r += is_leapyear(i+1900) ? 366*24*60*60 : 365*24*60*60;
+  static const int epoch_year = 70;
+  if(tm->tm_year >= epoch_year) {
+    for (i = epoch_year; i < tm->tm_year; ++i)
+      r += is_leapyear(i+1900) ? 366*24*60*60 : 365*24*60*60;
+  } else {
+    for (i = tm->tm_year; i < epoch_year; ++i)
+      r -= is_leapyear(i+1900) ? 366*24*60*60 : 365*24*60*60;
+  }
   for (i = 0; i < tm->tm_mon; ++i)
     r += nday[i] * 24 * 60 * 60;
   r += (tm->tm_mday - 1) * 24 * 60 * 60;
@@ -173,7 +184,7 @@ static const mrb_timezone_name timezone_names[] = {
   { "LOCAL", sizeof("LOCAL") - 1 },
 };
 
-#ifndef DISABLE_STDIO
+#ifndef MRB_DISABLE_STDIO
 static const char mon_names[12][4] = {
   "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
 };
@@ -234,13 +245,21 @@ time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone)
 
   mrb_check_num_exact(mrb, (mrb_float)sec);
   mrb_check_num_exact(mrb, (mrb_float)usec);
-
+#ifndef MRB_TIME_T_UINT
   if (sizeof(time_t) == 4 && (sec > (double)INT32_MAX || (double)INT32_MIN > sec)) {
     goto out_of_range;
   }
   if (sizeof(time_t) == 8 && (sec > (double)INT64_MAX || (double)INT64_MIN > sec)) {
     goto out_of_range;
   }
+#else
+  if (sizeof(time_t) == 4 && (sec > (double)UINT32_MAX || (double)0 > sec)) {
+    goto out_of_range;
+  }
+  if (sizeof(time_t) == 8 && (sec > (double)UINT64_MAX || (double)0 > sec)) {
+    goto out_of_range;
+  }
+#endif
   tsec  = (time_t)sec;
   if ((sec > 0 && tsec < 0) || (sec < 0 && (double)tsec > sec)) {
   out_of_range:
@@ -369,7 +388,7 @@ time_mktime(mrb_state *mrb, mrb_int ayear, mrb_int amonth, mrb_int aday,
     mrb_raise(mrb, E_ARGUMENT_ERROR, "Not a valid time.");
   }
 
-  return time_alloc(mrb, (double)nowsecs, ausec, timezone);
+  return time_alloc(mrb, (double)nowsecs, (double)ausec, timezone);
 }
 
 /* 15.2.19.6.2 */
@@ -541,7 +560,7 @@ mrb_time_asctime(mrb_state *mrb, mrb_value self)
   struct tm *d = &tm->datetime;
   int len;
 
-#if defined(DISABLE_STDIO)
+#if defined(MRB_DISABLE_STDIO)
   char *s;
 # ifdef NO_ASCTIME_R
   s = asctime(d);
@@ -634,7 +653,7 @@ mrb_time_initialize(mrb_state *mrb, mrb_value self)
 {
   mrb_int ayear = 0, amonth = 1, aday = 1, ahour = 0,
   amin = 0, asec = 0, ausec = 0;
-  int n;
+  mrb_int n;
   struct mrb_time *tm;
 
   n = mrb_get_args(mrb, "|iiiiiii",
index 52b9311..54c446c 100644 (file)
@@ -226,3 +226,7 @@ assert('2000 times 500us make a second') do
   end
   t.usec == 0
 end
+
+assert('Time.gm with Dec 31 23:59:59 1969 raise ArgumentError') do
+  assert_raise(ArgumentError) {Time.gm(1969, 12, 31, 23, 59, 59)}
+end
index f3762e8..1811236 100644 (file)
@@ -5,9 +5,10 @@ class Module
     attr_writer(*names)
   end
   # 15.2.2.4.11
-  def attr(name)
-    attr_reader(name)
-  end
+  alias attr attr_reader
+  #def attr(name)
+  #  attr_reader(name)
+  #end
 
   # 15.2.2.4.27
   def include(*args)
@@ -15,6 +16,7 @@ class Module
       m.append_features(self)
       m.included(self)
     end
+    self
   end
 
   def prepend(*args)
@@ -22,5 +24,6 @@ class Module
       m.prepend_features(self)
       m.prepended(self)
     end
+    self
   end
 end
index 22a8d1a..0d9f38d 100644 (file)
@@ -51,6 +51,9 @@ end
 class NotImplementedError < ScriptError
 end
 
+class FrozenError < RuntimeError
+end
+
 class StopIteration < IndexError
   attr_accessor :result
 end
index be98f21..d767563 100644 (file)
@@ -11,7 +11,7 @@ class Array
   #
   # ISO 15.2.12.5.10
   def each(&block)
-    return to_enum :each unless block_given?
+    return to_enum :each unless block
 
     idx = 0
     while idx < length
@@ -27,7 +27,7 @@ class Array
   #
   # ISO 15.2.12.5.11
   def each_index(&block)
-    return to_enum :each_index unless block_given?
+    return to_enum :each_index unless block
 
     idx = 0
     while idx < length
@@ -44,9 +44,14 @@ class Array
   #
   # ISO 15.2.12.5.7
   def collect!(&block)
-    return to_enum :collect! unless block_given?
+    return to_enum :collect! unless block
 
-    self.each_index { |idx| self[idx] = block.call(self[idx]) }
+    idx = 0
+    len = size
+    while idx < len
+      self[idx] = block.call self[idx]
+      idx += 1
+    end
     self
   end
 
@@ -179,12 +184,6 @@ class Array
     return block.call if ret.nil? && block
     ret
   end
-
-  # internal method to convert multi-value to single value
-  def __svalue
-    return self.first if self.size < 2
-    self
-  end
 end
 
 ##
@@ -195,28 +194,27 @@ class Array
 
   ##
   # Quick sort
-  # a     : the array to sort
   # left  : the beginning of sort region
   # right : the end of sort region
-  def __sort_sub__(a, left, right, &block)
+  def __sort_sub__(left, right, &block)
     if left < right
       i = left
       j = right
-      pivot = a[i + (j - i) / 2]
+      pivot = self[i + (j - i) / 2]
       while true
-        while ((block)? block.call(a[i], pivot): (a[i] <=> pivot)) < 0
+        while ((block)? block.call(self[i], pivot): (self[i] <=> pivot)) < 0
           i += 1
         end
-        while ((block)? block.call(pivot, a[j]): (pivot <=> a[j])) < 0
+        while ((block)? block.call(pivot, self[j]): (pivot <=> self[j])) < 0
           j -= 1
         end
         break if (i >= j)
-        tmp = a[i]; a[i] = a[j]; a[j] = tmp;
+        tmp = self[i]; self[i] = self[j]; self[j] = tmp;
         i += 1
         j -= 1
       end
-      __sort_sub__(a, left, i-1, &block)
-      __sort_sub__(a, j+1, right, &block)
+      __sort_sub__(left, i-1, &block)
+      __sort_sub__(j+1, right, &block)
     end
   end
   #  private :__sort_sub__
@@ -227,7 +225,7 @@ class Array
   def sort!(&block)
     size = self.size
     if size > 1
-      __sort_sub__(self, 0, size - 1, &block)
+      __sort_sub__(0, size - 1, &block)
     end
     self
   end
index 12bd1d3..a38f89d 100644 (file)
@@ -325,7 +325,7 @@ module Enumerable
   #
   # ISO 15.3.2.2.19
   def sort(&block)
-    self.map{|*val| val.__svalue}.sort
+    self.map{|*val| val.__svalue}.sort(&block)
   end
 
   ##
diff --git a/third-party/mruby/mrblib/float.rb b/third-party/mruby/mrblib/float.rb
new file mode 100644 (file)
index 0000000..2b86dc1
--- /dev/null
@@ -0,0 +1,9 @@
+##
+# Float
+#
+# ISO 15.2.9
+class Float
+  # mruby special - since mruby integers may be upgraded to floats,
+  # floats should be compatible to integers.
+  include Integral
+end if class_defined?("Float")
index 4727b08..6b4803c 100644 (file)
@@ -84,7 +84,7 @@ class Hash
   #
   # ISO 15.2.13.4.9
   def each(&block)
-    return to_enum :each unless block_given?
+    return to_enum :each unless block
 
     keys = self.keys
     vals = self.values
@@ -117,7 +117,7 @@ class Hash
   #
   # ISO 15.2.13.4.10
   def each_key(&block)
-    return to_enum :each_key unless block_given?
+    return to_enum :each_key unless block
 
     self.keys.each{|k| block.call(k)}
     self
@@ -143,7 +143,7 @@ class Hash
   #
   # ISO 15.2.13.4.11
   def each_value(&block)
-    return to_enum :each_value unless block_given?
+    return to_enum :each_value unless block
 
     self.keys.each{|k| block.call(self[k])}
     self
@@ -224,12 +224,12 @@ class Hash
   #
   #  1.8/1.9 Hash#reject! returns Hash; ISO says nothing.
   #
-  def reject!(&b)
-    return to_enum :reject! unless block_given?
+  def reject!(&block)
+    return to_enum :reject! unless block
 
     keys = []
     self.each{|k,v|
-      if b.call([k, v])
+      if block.call([k, v])
         keys.push(k)
       end
     }
@@ -255,12 +255,12 @@ class Hash
   #
   #  1.8/1.9 Hash#reject returns Hash; ISO says nothing.
   #
-  def reject(&b)
-    return to_enum :reject unless block_given?
+  def reject(&block)
+    return to_enum :reject unless block
 
     h = {}
     self.each{|k,v|
-      unless b.call([k, v])
+      unless block.call([k, v])
         h[k] = v
       end
     }
@@ -277,12 +277,12 @@ class Hash
   #
   #  1.9 Hash#select! returns Hash; ISO says nothing.
   #
-  def select!(&b)
-    return to_enum :select! unless block_given?
+  def select!(&block)
+    return to_enum :select! unless block
 
     keys = []
     self.each{|k,v|
-      unless b.call([k, v])
+      unless block.call([k, v])
         keys.push(k)
       end
     }
@@ -308,12 +308,12 @@ class Hash
   #
   #  1.9 Hash#select returns Hash; ISO says nothing
   #
-  def select(&b)
-    return to_enum :select unless block_given?
+  def select(&block)
+    return to_enum :select unless block
 
     h = {}
     self.each{|k,v|
-      if b.call([k, v])
+      if block.call([k, v])
         h[k] = v
       end
     }
index 19fd00d..fe4aae1 100644 (file)
@@ -8,6 +8,9 @@ MRuby.each_target do
   file objfile("#{current_build_dir}/mrblib") => "#{current_build_dir}/mrblib.c"
   file "#{current_build_dir}/mrblib.c" => [mrbcfile, __FILE__] + Dir.glob("#{current_dir}/*.rb").sort do |t|
     _, _, *rbfiles = t.prerequisites
+    if self.cc.defines.flatten.include?("MRB_WITHOUT_FLOAT")
+      rbfiles.delete("#{current_dir}/float.rb")
+    end
     FileUtils.mkdir_p File.dirname(t.name)
     open(t.name, 'w') do |f|
       _pp "GEN", "*.rb", "#{t.name.relative_path}"
index 975ad97..1b11e92 100644 (file)
@@ -45,7 +45,7 @@ module Integral
   #
   # ISO 15.2.8.3.15
   def downto(num, &block)
-    return to_enum(:downto, num) unless block_given?
+    return to_enum(:downto, num) unless block
 
     i = self.to_i
     while i >= num
@@ -70,7 +70,7 @@ module Integral
   #
   # ISO 15.2.8.3.22
   def times &block
-    return to_enum :times unless block_given?
+    return to_enum :times unless block
 
     i = 0
     while i < self
@@ -86,7 +86,7 @@ module Integral
   #
   # ISO 15.2.8.3.27
   def upto(num, &block)
-    return to_enum(:upto, num) unless block_given?
+    return to_enum(:upto, num) unless block
 
     i = self.to_i
     while i <= num
@@ -102,9 +102,9 @@ module Integral
   #
   def step(num=nil, step=1, &block)
     raise ArgumentError, "step can't be 0" if step == 0
-    return to_enum(:step, num, step) unless block_given?
+    return to_enum(:step, num, step) unless block
 
-    i = if num.kind_of? Float then self.to_f else self end
+    i = if class_defined?("Float") && num.kind_of?(Float) then self.to_f else self end
     if num == nil
       while true
         block.call(i)
@@ -161,13 +161,3 @@ class Integer
   # ISO 15.2.8.3.26
   alias truncate floor
 end
-
-##
-# Float
-#
-# ISO 15.2.9
-class Float
-  # mruby special - since mruby integers may be upgraded to floats,
-  # floats should be compatible to integers.
-  include Integral
-end
index 3322af5..5bd2521 100644 (file)
@@ -10,7 +10,7 @@ class Range
   #
   # ISO 15.2.14.4.4
   def each(&block)
-    return to_enum :each unless block_given?
+    return to_enum :each unless block
 
     val = self.first
     last = self.last
index 4c6114e..ee98cfa 100644 (file)
@@ -96,7 +96,7 @@ class String
   #
   # ISO 15.2.10.5.19
   def gsub!(*args, &block)
-    raise RuntimeError, "can't modify frozen String" if frozen?
+    raise FrozenError, "can't modify frozen String" if frozen?
     return to_enum(:gsub!, *args) if args.length == 1 && !block
     str = self.gsub(*args, &block)
     return nil if str == self
@@ -159,7 +159,7 @@ class String
   #
   # ISO 15.2.10.5.37
   def sub!(*args, &block)
-    raise RuntimeError, "can't modify frozen String" if frozen?
+    raise FrozenError, "can't modify frozen String" if frozen?
     str = self.sub(*args, &block)
     return nil if str == self
     self.replace(str)
index 9124ff9..bfa6e5f 100644 (file)
@@ -14,7 +14,7 @@
 #define ARY_DEFAULT_LEN   4
 #define ARY_SHRINK_RATIO  5 /* must be larger than 2 */
 #define ARY_C_MAX_SIZE (SIZE_MAX / sizeof(mrb_value))
-#define ARY_MAX_SIZE ((ARY_C_MAX_SIZE < (size_t)MRB_INT_MAX) ? (mrb_int)ARY_C_MAX_SIZE : MRB_INT_MAX-1)
+#define ARY_MAX_SIZE ((mrb_int)((ARY_C_MAX_SIZE < (size_t)MRB_INT_MAX) ? ARY_C_MAX_SIZE : MRB_INT_MAX-1))
 
 static struct RArray*
 ary_new_capa(mrb_state *mrb, mrb_int capa)
@@ -29,8 +29,7 @@ ary_new_capa(mrb_state *mrb, mrb_int capa)
 
   a = (struct RArray*)mrb_obj_alloc(mrb, MRB_TT_ARRAY, mrb->array_class);
   if (capa <= MRB_ARY_EMBED_LEN_MAX) {
-    ARY_SET_EMBED_FLAG(a);
-    /* ARY_SET_EMBED_LEN(a, 0); */
+    ARY_SET_EMBED_LEN(a, 0);
   }
   else {
     a->as.heap.ptr = (mrb_value *)mrb_malloc(mrb, blen);
@@ -78,14 +77,21 @@ array_copy(mrb_value *dst, const mrb_value *src, mrb_int size)
   }
 }
 
-MRB_API mrb_value
-mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals)
+static struct RArray*
+ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals)
 {
   struct RArray *a = ary_new_capa(mrb, size);
 
   array_copy(ARY_PTR(a), vals, size);
   ARY_SET_LEN(a, size);
 
+  return a;
+}
+
+MRB_API mrb_value
+mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals)
+{
+  struct RArray *a = ary_new_from_values(mrb, size, vals);
   return mrb_obj_value(a);
 }
 
@@ -115,7 +121,7 @@ static void
 ary_modify_check(mrb_state *mrb, struct RArray *a)
 {
   if (MRB_FROZEN_P(a)) {
-    mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen array");
+    mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen array");
   }
 }
 
@@ -257,7 +263,6 @@ mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len)
   ary_modify(mrb, a);
   old_len = RARRAY_LEN(ary);
   if (old_len != new_len) {
-    ARY_SET_LEN(a, new_len);
     if (new_len < old_len) {
       ary_shrink_capa(mrb, a);
     }
@@ -265,6 +270,7 @@ mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len)
       ary_expand_capa(mrb, a, new_len);
       ary_fill_with_nil(ARY_PTR(a) + old_len, new_len - old_len);
     }
+    ARY_SET_LEN(a, new_len);
   }
 
   return ary;
@@ -286,11 +292,17 @@ mrb_ary_s_create(mrb_state *mrb, mrb_value klass)
   return ary;
 }
 
+static void ary_replace(mrb_state*, struct RArray*, struct RArray*);
+
 static void
 ary_concat(mrb_state *mrb, struct RArray *a, struct RArray *a2)
 {
   mrb_int len;
 
+  if (ARY_LEN(a) == 0) {
+    ary_replace(mrb, a, a2);
+    return;
+  }
   if (ARY_LEN(a2) > ARY_MAX_SIZE - ARY_LEN(a)) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
   }
@@ -344,13 +356,42 @@ mrb_ary_plus(mrb_state *mrb, mrb_value self)
   return mrb_obj_value(a2);
 }
 
+#define ARY_REPLACE_SHARED_MIN 20
+
 static void
-ary_replace(mrb_state *mrb, struct RArray *a, mrb_value *argv, mrb_int len)
+ary_replace(mrb_state *mrb, struct RArray *a, struct RArray *b)
 {
-  ary_modify(mrb, a);
+  mrb_int len = ARY_LEN(b);
+
+  ary_modify_check(mrb, a);
+  if (a == b) return;
+  if (ARY_SHARED_P(a)) {
+    mrb_ary_decref(mrb, a->as.heap.aux.shared);
+    a->as.heap.aux.capa = 0;
+    a->as.heap.len = 0;
+    a->as.heap.ptr = NULL;
+    ARY_UNSET_SHARED_FLAG(a);
+  }
+  if (ARY_SHARED_P(b)) {
+  shared_b:
+    if (ARY_EMBED_P(a)) {
+      ARY_UNSET_EMBED_FLAG(a);
+    }
+    a->as.heap.ptr = b->as.heap.ptr;
+    a->as.heap.len = len;
+    a->as.heap.aux.shared = b->as.heap.aux.shared;
+    a->as.heap.aux.shared->refcnt++;
+    ARY_SET_SHARED_FLAG(a);
+    mrb_write_barrier(mrb, (struct RBasic*)a);
+    return;
+  }
+  if (!MRB_FROZEN_P(b) && len > ARY_REPLACE_SHARED_MIN) {
+    ary_make_shared(mrb, b);
+    goto shared_b;
+  }
   if (ARY_CAPA(a) < len)
     ary_expand_capa(mrb, a, len);
-  array_copy(ARY_PTR(a), argv, len);
+  array_copy(ARY_PTR(a), ARY_PTR(b), len);
   mrb_write_barrier(mrb, (struct RBasic*)a);
   ARY_SET_LEN(a, len);
 }
@@ -362,7 +403,7 @@ mrb_ary_replace(mrb_state *mrb, mrb_value self, mrb_value other)
   struct RArray *a2 = mrb_ary_ptr(other);
 
   if (a1 != a2) {
-    ary_replace(mrb, a1, ARY_PTR(a2), ARY_LEN(a2));
+    ary_replace(mrb, a1, a2);
   }
 }
 
@@ -465,12 +506,20 @@ static mrb_value
 mrb_ary_push_m(mrb_state *mrb, mrb_value self)
 {
   mrb_value *argv;
-  mrb_int len;
+  mrb_int len, len2, alen;
+  struct RArray *a;
 
-  mrb_get_args(mrb, "*!", &argv, &len);
-  while (len--) {
-    mrb_ary_push(mrb, self, *argv++);
+  mrb_get_args(mrb, "*!", &argv, &alen);
+  a = mrb_ary_ptr(self);
+  ary_modify(mrb, a);
+  len = ARY_LEN(a);
+  len2 = len + alen;
+  if (ARY_CAPA(a) < len2) {
+    ary_expand_capa(mrb, a, len2);
   }
+  array_copy(ARY_PTR(a)+len, argv, alen);
+  ARY_SET_LEN(a, len2);
+  mrb_write_barrier(mrb, (struct RBasic*)a);
 
   return self;
 }
@@ -563,6 +612,10 @@ mrb_ary_unshift_m(mrb_state *mrb, mrb_value self)
   mrb_int alen, len;
 
   mrb_get_args(mrb, "*!", &vals, &alen);
+  if (alen == 0) {
+    ary_modify_check(mrb, a);
+    return self;
+  }
   len = ARY_LEN(a);
   if (alen > ARY_MAX_SIZE - len) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
@@ -571,12 +624,11 @@ mrb_ary_unshift_m(mrb_state *mrb, mrb_value self)
       && a->as.heap.aux.shared->refcnt == 1 /* shared only referenced from this array */
       && a->as.heap.ptr - a->as.heap.aux.shared->ptr >= alen) /* there's room for unshifted item */ {
     ary_modify_check(mrb, a);
-    a->as.heap.ptr -= len;
+    a->as.heap.ptr -= alen;
     ptr = a->as.heap.ptr;
   }
   else {
     ary_modify(mrb, a);
-    if (alen == 0) return self;
     if (ARY_CAPA(a) < len + alen)
       ary_expand_capa(mrb, a, len + alen);
     ptr = ARY_PTR(a);
@@ -584,8 +636,8 @@ mrb_ary_unshift_m(mrb_state *mrb, mrb_value self)
   }
   array_copy(ptr, vals, alen);
   ARY_SET_LEN(a, len+alen);
-  while (len--) {
-    mrb_field_write_barrier_value(mrb, (struct RBasic*)a, vals[len]);
+  while (alen--) {
+    mrb_field_write_barrier_value(mrb, (struct RBasic*)a, vals[alen]);
   }
 
   return self;
@@ -632,11 +684,7 @@ mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val)
 static struct RArray*
 ary_dup(mrb_state *mrb, struct RArray *a)
 {
-  mrb_int len = ARY_LEN(a);
-  struct RArray *d = ary_new_capa(mrb, len);
-
-  ary_replace(mrb, d, ARY_PTR(a), len);
-  return d;
+  return ary_new_from_values(mrb, ARY_LEN(a), ARY_PTR(a));
 }
 
 MRB_API mrb_value
@@ -757,9 +805,11 @@ aget_index(mrb_state *mrb, mrb_value index)
   if (mrb_fixnum_p(index)) {
     return mrb_fixnum(index);
   }
+#ifndef MRB_WITHOUT_FLOAT
   else if (mrb_float_p(index)) {
     return (mrb_int)mrb_float(index);
   }
+#endif
   else {
     mrb_int i, argc;
     mrb_value *argv;
@@ -929,9 +979,10 @@ mrb_ary_first(mrb_state *mrb, mrb_value self)
   struct RArray *a = mrb_ary_ptr(self);
   mrb_int size, alen = ARY_LEN(a);
 
-  if (mrb_get_args(mrb, "|i", &size) == 0) {
+  if (mrb_get_argc(mrb) == 0) {
     return (alen > 0)? ARY_PTR(a)[0]: mrb_nil_value();
   }
+  mrb_get_args(mrb, "|i", &size);
   if (size < 0) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size");
   }
@@ -1048,7 +1099,6 @@ mrb_ary_clear(mrb_state *mrb, mrb_value self)
   else if (!ARY_EMBED_P(a)){
     mrb_free(mrb, a->as.heap.ptr);
   }
-  ARY_SET_EMBED_FLAG(a);
   ARY_SET_EMBED_LEN(a, 0);
 
   return self;
@@ -1074,7 +1124,10 @@ mrb_ary_entry(mrb_value ary, mrb_int offset)
   if (offset < 0) {
     offset += RARRAY_LEN(ary);
   }
-  return ary_elt(ary, offset);
+  if (offset < 0 || RARRAY_LEN(ary) <= offset) {
+    return mrb_nil_value();
+  }
+  return RARRAY_PTR(ary)[offset];
 }
 
 static mrb_value
@@ -1092,7 +1145,7 @@ join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list)
 
   mrb_ary_push(mrb, list, ary);
 
-  result = mrb_str_buf_new(mrb, 64);
+  result = mrb_str_new_capa(mrb, 64);
 
   for (i=0; i<RARRAY_LEN(ary); i++) {
     if (i > 0 && !mrb_nil_p(sep)) {
@@ -1192,6 +1245,21 @@ mrb_ary_cmp(mrb_state *mrb, mrb_value ary1)
   return ary2;
 }
 
+/* internal method to convert multi-value to single value */
+static mrb_value
+mrb_ary_svalue(mrb_state *mrb, mrb_value ary)
+{
+  mrb_get_args(mrb, "");
+  switch (RARRAY_LEN(ary)) {
+  case 0:
+    return mrb_nil_value();
+  case 1:
+    return RARRAY_PTR(ary)[0];
+  default:
+    return ary;
+  }
+}
+
 void
 mrb_init_array(mrb_state *mrb)
 {
@@ -1233,4 +1301,5 @@ mrb_init_array(mrb_state *mrb)
   mrb_define_method(mrb, a, "__ary_eq",        mrb_ary_eq,           MRB_ARGS_REQ(1));
   mrb_define_method(mrb, a, "__ary_cmp",       mrb_ary_cmp,          MRB_ARGS_REQ(1));
   mrb_define_method(mrb, a, "__ary_index",     mrb_ary_index_m,      MRB_ARGS_REQ(1)); /* kept for mruby-array-ext */
+  mrb_define_method(mrb, a, "__svalue",        mrb_ary_svalue,       MRB_ARGS_NONE());
 }
index f043955..30febdc 100644 (file)
@@ -21,14 +21,14 @@ struct backtrace_location {
   mrb_sym method_id;
 };
 
-typedef void (*each_backtrace_func)(mrb_state*, int i, struct backtrace_location*, void*);
+typedef void (*each_backtrace_func)(mrb_state*, struct backtrace_location*, void*);
 
 static const mrb_data_type bt_type = { "Backtrace", mrb_free };
 
 static void
-each_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, each_backtrace_func func, void *data)
+each_backtrace(mrb_state *mrb, ptrdiff_t ciidx, mrb_code *pc0, each_backtrace_func func, void *data)
 {
-  int i, j;
+  ptrdiff_t i, j;
 
   if (ciidx >= mrb->c->ciend - mrb->c->cibase)
     ciidx = 10; /* ciidx is broken... */
@@ -51,13 +51,13 @@ each_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, each_backtrace_func
       pc = mrb->c->cibase[i].err;
     }
     else if (i+1 <= ciidx) {
-      pc = mrb->c->cibase[i+1].pc - 1;
+      pc = &mrb->c->cibase[i+1].pc[-1];
     }
     else {
       pc = pc0;
     }
-    loc.filename = mrb_debug_get_filename(irep, (uint32_t)(pc - irep->iseq));
-    loc.lineno = mrb_debug_get_line(irep, (uint32_t)(pc - irep->iseq));
+    loc.filename = mrb_debug_get_filename(irep, pc - irep->iseq);
+    loc.lineno = mrb_debug_get_line(irep, pc - irep->iseq);
 
     if (loc.lineno == -1) continue;
 
@@ -66,7 +66,7 @@ each_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, each_backtrace_func
     }
 
     loc.method_id = ci->mid;
-    func(mrb, j, &loc, data);
+    func(mrb, &loc, data);
   }
 }
 
@@ -75,7 +75,8 @@ each_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, each_backtrace_func
 static void
 print_backtrace(mrb_state *mrb, mrb_value backtrace)
 {
-  int i, n;
+  int i;
+  mrb_int n;
   FILE *stream = stderr;
 
   if (!mrb_array_p(backtrace)) return;
@@ -83,7 +84,7 @@ print_backtrace(mrb_state *mrb, mrb_value backtrace)
   n = RARRAY_LEN(backtrace) - 1;
   if (n == 0) return;
 
-  fprintf(stream, "trace:\n");
+  fprintf(stream, "trace (most recent call last):\n");
   for (i=0; i<n; i++) {
     mrb_value entry = RARRAY_PTR(backtrace)[n-i-1];
 
@@ -120,7 +121,7 @@ print_packed_backtrace(mrb_state *mrb, mrb_value packed)
   n = (mrb_int)RDATA(packed)->flags;
 
   if (packed_bt_len(bt, n) == 0) return;
-  fprintf(stream, "trace:\n");
+  fprintf(stream, "trace (most recent call last):\n");
   for (i = 0; i<n; i++) {
     struct backtrace_location *entry = &bt[n-i-1];
     if (entry->filename == NULL) continue;
@@ -170,7 +171,6 @@ mrb_print_backtrace(mrb_state *mrb)
 
 static void
 count_backtrace_i(mrb_state *mrb,
-                 int i,
                  struct backtrace_location *loc,
                  void *data)
 {
@@ -182,7 +182,6 @@ count_backtrace_i(mrb_state *mrb,
 
 static void
 pack_backtrace_i(mrb_state *mrb,
-                 int i,
                  struct backtrace_location *loc,
                  void *data)
 {
@@ -206,7 +205,7 @@ packed_backtrace(mrb_state *mrb)
   each_backtrace(mrb, ciidx, mrb->c->ci->pc, count_backtrace_i, &len);
   size = len * sizeof(struct backtrace_location);
   ptr = mrb_malloc(mrb, size);
-  memset(ptr, 0, size);
+  if (ptr) memset(ptr, 0, size);
   backtrace = mrb_data_object_alloc(mrb, NULL, ptr, &bt_type);
   backtrace->flags = (unsigned int)len;
   each_backtrace(mrb, ciidx, mrb->c->ci->pc, pack_backtrace_i, &ptr);
@@ -216,11 +215,14 @@ packed_backtrace(mrb_state *mrb)
 void
 mrb_keep_backtrace(mrb_state *mrb, mrb_value exc)
 {
+  mrb_sym sym = mrb_intern_lit(mrb, "backtrace");
   mrb_value backtrace;
-  int ai = mrb_gc_arena_save(mrb);
+  int ai;
 
+  if (mrb_iv_defined(mrb, exc, sym)) return;
+  ai = mrb_gc_arena_save(mrb);
   backtrace = packed_backtrace(mrb);
-  mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "backtrace"), backtrace);
+  mrb_iv_set(mrb, exc, sym, backtrace);
   mrb_gc_arena_restore(mrb, ai);
 }
 
index 56f64fd..c761f46 100644 (file)
@@ -16,7 +16,7 @@
 #include <mruby/data.h>
 #include <mruby/istruct.h>
 
-KHASH_DEFINE(mt, mrb_sym, struct RProc*, TRUE, kh_int_hash_func, kh_int_hash_equal)
+KHASH_DEFINE(mt, mrb_sym, mrb_method_t, TRUE, kh_int_hash_func, kh_int_hash_equal)
 
 void
 mrb_gc_mark_mt(mrb_state *mrb, struct RClass *c)
@@ -27,9 +27,11 @@ mrb_gc_mark_mt(mrb_state *mrb, struct RClass *c)
   if (!h) return;
   for (k = kh_begin(h); k != kh_end(h); k++) {
     if (kh_exist(h, k)) {
-      struct RProc *m = kh_value(h, k);
-      if (m) {
-        mrb_gc_mark(mrb, (struct RBasic*)m);
+      mrb_method_t m = kh_value(h, k);
+
+      if (MRB_METHOD_PROC_P(m)) {
+        struct RProc *p = MRB_METHOD_PROC(m);
+        mrb_gc_mark(mrb, (struct RBasic*)p);
       }
     }
   }
@@ -50,22 +52,36 @@ mrb_gc_free_mt(mrb_state *mrb, struct RClass *c)
   kh_destroy(mt, mrb, c->mt);
 }
 
-static void
-name_class(mrb_state *mrb, struct RClass *c, mrb_sym name)
+void
+mrb_class_name_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb_sym id)
 {
-  mrb_obj_iv_set(mrb, (struct RObject*)c,
-                 mrb_intern_lit(mrb, "__classid__"), mrb_symbol_value(name));
+  mrb_value name;
+  mrb_sym nsym = mrb_intern_lit(mrb, "__classname__");
+
+  if (mrb_obj_iv_defined(mrb, (struct RObject*)c, nsym)) return;
+  if (outer == NULL || outer == mrb->object_class) {
+    name = mrb_symbol_value(id);
+  }
+  else {
+    name = mrb_class_path(mrb, outer);
+    if (mrb_nil_p(name)) {      /* unnamed outer class */
+      if (outer != mrb->object_class) {
+        mrb_obj_iv_set(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"),
+                       mrb_obj_value(outer));
+      }
+      return;
+    }
+    mrb_str_cat_cstr(mrb, name, "::");
+    mrb_str_cat_cstr(mrb, name, mrb_sym2name(mrb, id));
+  }
+  mrb_obj_iv_set(mrb, (struct RObject*)c, nsym, name);
 }
 
 static void
 setup_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb_sym id)
 {
-  name_class(mrb, c, id);
+  mrb_class_name_class(mrb, outer, c, id);
   mrb_obj_iv_set(mrb, (struct RObject*)outer, id, mrb_obj_value(c));
-  if (outer != mrb->object_class) {
-    mrb_obj_iv_set(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"),
-                   mrb_obj_value(outer));
-  }
 }
 
 #define make_metaclass(mrb, c) prepare_singleton_class((mrb), (struct RBasic*)(c))
@@ -77,6 +93,7 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o)
 
   if (o->c->tt == MRB_TT_SCLASS) return;
   sc = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_SCLASS, mrb->class_class);
+  sc->flags |= MRB_FLAG_IS_INHERITED;
   sc->mt = kh_init(mt, mrb);
   sc->iv = 0;
   if (o->tt == MRB_TT_CLASS) {
@@ -105,7 +122,7 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o)
   mrb_obj_iv_set(mrb, (struct RObject*)sc, mrb_intern_lit(mrb, "__attached__"), mrb_obj_value(o));
 }
 
-static struct RClass *
+static struct RClass*
 class_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id)
 {
   mrb_value c = mrb_const_get(mrb, mrb_obj_value(klass), id);
@@ -114,7 +131,7 @@ class_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id)
   return mrb_class_ptr(c);
 }
 
-static struct RClass *
+static struct RClass*
 module_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id)
 {
   mrb_value c = mrb_const_get(mrb, mrb_obj_value(klass), id);
@@ -136,27 +153,6 @@ class_ptr_p(mrb_value obj)
   }
 }
 
-MRB_API struct RClass*
-mrb_class_outer_module(mrb_state *mrb, struct RClass *c)
-{
-  mrb_value outer;
-  struct RClass *cls;
-
-  outer = mrb_obj_iv_get(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"));
-  if (mrb_nil_p(outer)) return NULL;
-  cls = mrb_class_ptr(outer);
-  if (cls->tt == MRB_TT_SCLASS)
-  {
-    mrb_value klass;
-    klass = mrb_obj_iv_get(mrb, (struct RObject *)cls,
-                           mrb_intern_lit(mrb, "__attached__"));
-    if (class_ptr_p(klass)) {
-      cls = mrb_class_ptr(klass);
-    }
-  }
-  return cls;
-}
-
 static void
 check_if_class_or_module(mrb_state *mrb, mrb_value obj)
 {
@@ -260,7 +256,16 @@ mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super)
   return mrb_define_class_id(mrb, mrb_intern_cstr(mrb, name), super);
 }
 
-static mrb_value mrb_bob_init(mrb_state *mrb, mrb_value cv);
+static mrb_value mrb_bob_init(mrb_state *mrb, mrb_value);
+#ifdef MRB_METHOD_CACHE
+static void mc_clear_all(mrb_state *mrb);
+static void mc_clear_by_class(mrb_state *mrb, struct RClass*);
+static void mc_clear_by_id(mrb_state *mrb, struct RClass*, mrb_sym);
+#else
+#define mc_clear_all(mrb)
+#define mc_clear_by_class(mrb,c)
+#define mc_clear_by_id(mrb,c,s)
+#endif
 
 static void
 mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass)
@@ -270,11 +275,13 @@ mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass)
 
   if (!super)
     super = mrb->object_class;
+  super->flags |= MRB_FLAG_IS_INHERITED;
   s = mrb_obj_value(super);
+  mc_clear_by_class(mrb, klass);
   mid = mrb_intern_lit(mrb, "inherited");
   if (!mrb_func_basic_p(mrb, s, mid, mrb_bob_init)) {
     mrb_value c = mrb_obj_value(klass);
-    mrb_funcall_argv(mrb, mrb_obj_value(super), mid, 1, &c);
+    mrb_funcall_argv(mrb, s, mid, 1, &c);
   }
 }
 
@@ -336,19 +343,19 @@ mrb_class_defined_under(mrb_state *mrb, struct RClass *outer, const char *name)
   return mrb_const_defined_at(mrb, mrb_obj_value(outer), mrb_symbol(sym));
 }
 
-MRB_API struct RClass *
+MRB_API struct RClass*
 mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name)
 {
   return class_from_sym(mrb, outer, mrb_intern_cstr(mrb, name));
 }
 
-MRB_API struct RClass *
+MRB_API struct RClass*
 mrb_class_get(mrb_state *mrb, const char *name)
 {
   return mrb_class_get_under(mrb, mrb->object_class, name);
 }
 
-MRB_API struct RClass *
+MRB_API struct RClass*
 mrb_exc_get(mrb_state *mrb, const char *name)
 {
   struct RClass *exc, *e;
@@ -368,13 +375,13 @@ mrb_exc_get(mrb_state *mrb, const char *name)
   return mrb->eException_class;
 }
 
-MRB_API struct RClass *
+MRB_API struct RClass*
 mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name)
 {
   return module_from_sym(mrb, outer, mrb_intern_cstr(mrb, name));
 }
 
-MRB_API struct RClass *
+MRB_API struct RClass*
 mrb_module_get(mrb_state *mrb, const char *name)
 {
   return mrb_module_get_under(mrb, mrb->object_class, name);
@@ -396,7 +403,7 @@ mrb_module_get(mrb_state *mrb, const char *name)
  * \note if a class named \a name is already defined and its superclass is
  *       \a super, the function just returns the defined class.
  */
-MRB_API struct RClass *
+MRB_API struct RClass*
 mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super)
 {
   mrb_sym id = mrb_intern_cstr(mrb, name);
@@ -414,7 +421,7 @@ mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, s
 }
 
 MRB_API void
-mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RProc *p)
+mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_method_t m)
 {
   khash_t(mt) *h;
   khiter_t k;
@@ -423,28 +430,34 @@ mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RPro
 
   if (MRB_FROZEN_P(c)) {
     if (c->tt == MRB_TT_MODULE)
-      mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen module");
+      mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen module");
     else
-      mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen class");
+      mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen class");
   }
   if (!h) h = c->mt = kh_init(mt, mrb);
   k = kh_put(mt, mrb, h, mid);
-  kh_value(h, k) = p;
-  if (p) {
+  kh_value(h, k) = m;
+  if (MRB_METHOD_PROC_P(m) && !MRB_METHOD_UNDEF_P(m)) {
+    struct RProc *p = MRB_METHOD_PROC(m);
+
+    p->flags |= MRB_PROC_SCOPE;
     p->c = NULL;
-    mrb_field_write_barrier(mrb, (struct RBasic *)c, (struct RBasic *)p);
+    mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)p);
+    if (!MRB_PROC_ENV_P(p)) {
+      MRB_PROC_SET_TARGET_CLASS(p, c);
+    }
   }
+  mc_clear_by_id(mrb, c, mid);
 }
 
 MRB_API void
 mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec)
 {
-  struct RProc *p;
+  mrb_method_t m;
   int ai = mrb_gc_arena_save(mrb);
 
-  p = mrb_proc_new_cfunc(mrb, func);
-  p->target_class = c;
-  mrb_define_method_raw(mrb, c, mid, p);
+  MRB_METHOD_FROM_FUNC(m, func);
+  mrb_define_method_raw(mrb, c, mid, m);
   mrb_gc_arena_restore(mrb, ai);
 }
 
@@ -526,6 +539,35 @@ to_sym(mrb_state *mrb, mrb_value ss)
   }
 }
 
+MRB_API mrb_int
+mrb_get_argc(mrb_state *mrb)
+{
+  mrb_int argc = mrb->c->ci->argc;
+
+  if (argc < 0) {
+    struct RArray *a = mrb_ary_ptr(mrb->c->stack[1]);
+
+    argc = ARY_LEN(a);
+  }
+  return argc;
+}
+
+MRB_API mrb_value*
+mrb_get_argv(mrb_state *mrb)
+{
+  mrb_int argc = mrb->c->ci->argc;
+  mrb_value *array_argv;
+  if (argc < 0) {
+    struct RArray *a = mrb_ary_ptr(mrb->c->stack[1]);
+
+    array_argv = ARY_PTR(a);
+  }
+  else {
+    array_argv = NULL;
+  }
+  return array_argv;
+}
+
 /*
   retrieve arguments from mrb_state.
 
@@ -551,7 +593,7 @@ to_sym(mrb_state *mrb, mrb_value ss)
     n:      Symbol         [mrb_sym]
     d:      Data           [void*,mrb_data_type const] 2nd argument will be used to check data type so it won't be modified
     I:      Inline struct  [void*]
-    &:      Block          [mrb_value]
+    &:      Block          [mrb_value]            &! raises exception if no block given
     *:      rest argument  [mrb_value*,mrb_int]   The rest of the arguments as an array; *! avoid copy of the stack
     |:      optional                              Following arguments are optional
     ?:      optional given [mrb_bool]             true if preceding argument (optional) is given
@@ -559,29 +601,43 @@ to_sym(mrb_state *mrb, mrb_value ss)
 MRB_API mrb_int
 mrb_get_args(mrb_state *mrb, const char *format, ...)
 {
+  const char *fmt = format;
   char c;
-  int i = 0;
+  mrb_int i = 0;
   va_list ap;
-  int argc = mrb->c->ci->argc;
-  int arg_i = 0;
-  mrb_value *array_argv;
+  mrb_int argc = mrb_get_argc(mrb);
+  mrb_int arg_i = 0;
+  mrb_value *array_argv = mrb_get_argv(mrb);
   mrb_bool opt = FALSE;
+  mrb_bool opt_skip = TRUE;
   mrb_bool given = TRUE;
 
   va_start(ap, format);
-  if (argc < 0) {
-    struct RArray *a = mrb_ary_ptr(mrb->c->stack[1]);
-
-    argc = ARY_LEN(a);
-    array_argv = ARY_PTR(a);
-  }
-  else {
-    array_argv = NULL;
-  }
 
 #define ARGV \
   (array_argv ? array_argv : (mrb->c->stack + 1))
 
+  while ((c = *fmt++)) {
+    switch (c) {
+    case '|':
+      opt = TRUE;
+      break;
+    case '*':
+      opt_skip = FALSE;
+      goto check_exit;
+    case '!':
+      break;
+    case '&': case '?':
+      if (opt) opt_skip = FALSE;
+      break;
+    default:
+      break;
+    }
+  }
+
+ check_exit:
+  opt = FALSE;
+  i = 0;
   while ((c = *format++)) {
     switch (c) {
     case '|': case '*': case '&': case '?':
@@ -775,6 +831,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
         }
       }
       break;
+#ifndef MRB_WITHOUT_FLOAT
     case 'f':
       {
         mrb_float *p;
@@ -787,6 +844,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
         }
       }
       break;
+#endif
     case 'i':
       {
         mrb_int *p;
@@ -797,6 +855,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
             case MRB_TT_FIXNUM:
               *p = mrb_fixnum(ARGV[arg_i]);
               break;
+#ifndef MRB_WITHOUT_FLOAT
             case MRB_TT_FLOAT:
               {
                 mrb_float f = mrb_float(ARGV[arg_i]);
@@ -807,6 +866,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
                 *p = (mrb_int)f;
               }
               break;
+#endif
             case MRB_TT_STRING:
               mrb_raise(mrb, E_TYPE_ERROR, "no implicit conversion of String into Integer");
               break;
@@ -877,10 +937,17 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
         else {
           bp = mrb->c->stack + mrb->c->ci->argc + 1;
         }
+        if (*format == '!') {
+          format ++;
+          if (mrb_nil_p(*bp)) {
+            mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
+          }
+        }
         *p = *bp;
       }
       break;
     case '|':
+      if (opt_skip && i == argc) return argc;
       opt = TRUE;
       break;
     case '?':
@@ -1001,7 +1068,7 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru
       return -1;
 
     p = c->super;
-    while(p) {
+    while (p) {
       if (p->tt == MRB_TT_ICLASS) {
         if (p->mt == m->mt) {
           if (!superclass_seen) {
@@ -1017,12 +1084,15 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru
     }
 
     ic = include_class_new(mrb, m, ins_pos->super);
+    m->flags |= MRB_FLAG_IS_INHERITED;
     ins_pos->super = ic;
-    mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ins_pos->super);
+    mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic);
+    mc_clear_by_class(mrb, ins_pos);
     ins_pos = ic;
   skip:
     m = m->super;
   }
+  mc_clear_all(mrb);
   return 0;
 }
 
@@ -1043,7 +1113,7 @@ mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
 
   if (!(c->flags & MRB_FLAG_IS_PREPENDED)) {
     origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c);
-    origin->flags |= MRB_FLAG_IS_ORIGIN;
+    origin->flags |= MRB_FLAG_IS_ORIGIN | MRB_FLAG_IS_INHERITED;
     origin->super = c->super;
     c->super = origin;
     origin->mt = c->mt;
@@ -1244,7 +1314,9 @@ mrb_singleton_class(mrb_state *mrb, mrb_value v)
     return mrb_obj_value(mrb->object_class);
   case MRB_TT_SYMBOL:
   case MRB_TT_FIXNUM:
+#ifndef MRB_WITHOUT_FLOAT
   case MRB_TT_FLOAT:
+#endif
     mrb_raise(mrb, E_TYPE_ERROR, "can't define singleton");
     return mrb_nil_value();    /* not reached */
   default:
@@ -1275,12 +1347,68 @@ mrb_define_module_function(mrb_state *mrb, struct RClass *c, const char *name, m
   mrb_define_method(mrb, c, name, func, aspec);
 }
 
-MRB_API struct RProc*
+#ifdef MRB_METHOD_CACHE
+static void
+mc_clear_all(mrb_state *mrb)
+{
+  struct mrb_cache_entry *mc = mrb->cache;
+  int i;
+
+  for (i=0; i<MRB_METHOD_CACHE_SIZE; i++) {
+    mc[i].c = 0;
+  }
+}
+
+static void
+mc_clear_by_class(mrb_state *mrb, struct RClass *c)
+{
+  struct mrb_cache_entry *mc = mrb->cache;
+  int i;
+
+  if (c->flags & MRB_FLAG_IS_INHERITED) {
+    mc_clear_all(mrb);
+    c->flags &= ~MRB_FLAG_IS_INHERITED;
+    return;
+  }
+  for (i=0; i<MRB_METHOD_CACHE_SIZE; i++) {
+    if (mc[i].c == c) mc[i].c = 0;
+  }
+}
+
+static void
+mc_clear_by_id(mrb_state *mrb, struct RClass *c, mrb_sym mid)
+{
+  struct mrb_cache_entry *mc = mrb->cache;
+  int i;
+
+  if (c->flags & MRB_FLAG_IS_INHERITED) {
+    mc_clear_all(mrb);
+    c->flags &= ~MRB_FLAG_IS_INHERITED;
+    return;
+  }
+  for (i=0; i<MRB_METHOD_CACHE_SIZE; i++) {
+    if (mc[i].c == c || mc[i].mid == mid)
+      mc[i].c = 0;
+  }
+}
+#endif
+
+MRB_API mrb_method_t
 mrb_method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid)
 {
   khiter_t k;
-  struct RProc *m;
+  mrb_method_t m;
   struct RClass *c = *cp;
+#ifdef MRB_METHOD_CACHE
+  struct RClass *oc = c;
+  int h = kh_int_hash_func(mrb, ((intptr_t)oc) ^ mid) & (MRB_METHOD_CACHE_SIZE-1);
+  struct mrb_cache_entry *mc = &mrb->cache[h];
+
+  if (mc->c == c && mc->mid == mid) {
+    *cp = mc->c0;
+    return mc->m;
+  }
+#endif
 
   while (c) {
     khash_t(mt) *h = c->mt;
@@ -1289,23 +1417,30 @@ mrb_method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid)
       k = kh_get(mt, mrb, h, mid);
       if (k != kh_end(h)) {
         m = kh_value(h, k);
-        if (!m) break;
+        if (MRB_METHOD_UNDEF_P(m)) break;
         *cp = c;
+#ifdef MRB_METHOD_CACHE
+        mc->c = oc;
+        mc->c0 = c;
+        mc->mid = mid;
+        mc->m = m;
+#endif
         return m;
       }
     }
     c = c->super;
   }
-  return NULL;                  /* no method */
+  MRB_METHOD_FROM_PROC(m, NULL);
+  return m;                  /* no method */
 }
 
-MRB_API struct RProc*
+MRB_API mrb_method_t
 mrb_method_search(mrb_state *mrb, struct RClass* c, mrb_sym mid)
 {
-  struct RProc *m;
+  mrb_method_t m;
 
   m = mrb_method_search_vm(mrb, &c, mid);
-  if (!m) {
+  if (MRB_METHOD_UNDEF_P(m)) {
     mrb_value inspect = mrb_funcall(mrb, mrb_obj_value(c), "inspect", 0);
     if (mrb_string_p(inspect) && RSTRING_LEN(inspect) > 64) {
       inspect = mrb_any_to_s(mrb, mrb_obj_value(c));
@@ -1336,17 +1471,20 @@ mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
   for (i=0; i<argc; i++) {
     mrb_value name, str;
     mrb_sym method, sym;
+    struct RProc *p;
+    mrb_method_t m;
 
     method = to_sym(mrb, argv[i]);
     name = mrb_sym2str(mrb, method);
-    str = mrb_str_buf_new(mrb, RSTRING_LEN(name)+1);
+    str = mrb_str_new_capa(mrb, RSTRING_LEN(name)+1);
     mrb_str_cat_lit(mrb, str, "@");
     mrb_str_cat_str(mrb, str, name);
     sym = mrb_intern_str(mrb, str);
     mrb_iv_check(mrb, sym);
     name = mrb_symbol_value(sym);
-    mrb_define_method_raw(mrb, c, method,
-                          mrb_proc_new_cfunc_with_env(mrb, attr_reader, 1, &name));
+    p = mrb_proc_new_cfunc_with_env(mrb, attr_reader, 1, &name);
+    MRB_METHOD_FROM_PROC(m, p);
+    mrb_define_method_raw(mrb, c, method, m);
     mrb_gc_arena_restore(mrb, ai);
   }
   return mrb_nil_value();
@@ -1376,12 +1514,14 @@ mrb_mod_attr_writer(mrb_state *mrb, mrb_value mod)
   for (i=0; i<argc; i++) {
     mrb_value name, str, attr;
     mrb_sym method, sym;
+    struct RProc *p;
+    mrb_method_t m;
 
     method = to_sym(mrb, argv[i]);
 
     /* prepare iv name (@name) */
     name = mrb_sym2str(mrb, method);
-    str = mrb_str_buf_new(mrb, RSTRING_LEN(name)+1);
+    str = mrb_str_new_capa(mrb, RSTRING_LEN(name)+1);
     mrb_str_cat_lit(mrb, str, "@");
     mrb_str_cat_str(mrb, str, name);
     sym = mrb_intern_str(mrb, str);
@@ -1389,13 +1529,14 @@ mrb_mod_attr_writer(mrb_state *mrb, mrb_value mod)
     attr = mrb_symbol_value(sym);
 
     /* prepare method name (name=) */
-    str = mrb_str_buf_new(mrb, RSTRING_LEN(str));
+    str = mrb_str_new_capa(mrb, RSTRING_LEN(str));
     mrb_str_cat_str(mrb, str, name);
     mrb_str_cat_lit(mrb, str, "=");
     method = mrb_intern_str(mrb, str);
 
-    mrb_define_method_raw(mrb, c, method,
-                          mrb_proc_new_cfunc_with_env(mrb, attr_writer, 1, &attr));
+    p = mrb_proc_new_cfunc_with_env(mrb, attr_writer, 1, &attr);
+    MRB_METHOD_FROM_PROC(m, p);
+    mrb_define_method_raw(mrb, c, method, m);
     mrb_gc_arena_restore(mrb, ai);
   }
   return mrb_nil_value();
@@ -1437,10 +1578,22 @@ mrb_instance_new(mrb_state *mrb, mrb_value cv)
   mrb_value obj, blk;
   mrb_value *argv;
   mrb_int argc;
+  mrb_sym init;
+  mrb_method_t m;
 
   mrb_get_args(mrb, "*&", &argv, &argc, &blk);
   obj = mrb_instance_alloc(mrb, cv);
-  mrb_funcall_with_block(mrb, obj, mrb_intern_lit(mrb, "initialize"), argc, argv, blk);
+  init = mrb_intern_lit(mrb, "initialize");
+  m = mrb_method_search(mrb, mrb_class(mrb, obj), init);
+  if (MRB_METHOD_CFUNC_P(m)) {
+    mrb_func_t f = MRB_METHOD_CFUNC(m);
+    if (f != mrb_bob_init) {
+      f(mrb, obj);
+    }
+  }
+  else {
+    mrb_funcall_with_block(mrb, obj, init, argc, argv, blk);
+  }
 
   return obj;
 }
@@ -1570,25 +1723,13 @@ mrb_obj_not_equal_m(mrb_state *mrb, mrb_value self)
 MRB_API mrb_bool
 mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid)
 {
-  khiter_t k;
-
-  while (c) {
-    khash_t(mt) *h = c->mt;
+  mrb_method_t m;
 
-    if (h) {
-      k = kh_get(mt, mrb, h, mid);
-      if (k != kh_end(h)) {
-        if (kh_value(h, k)) {
-          return TRUE;  /* method exists */
-        }
-        else {
-          return FALSE; /* undefined method */
-        }
-      }
-    }
-    c = c->super;
+  m = mrb_method_search_vm(mrb, &c, mid);
+  if (MRB_METHOD_UNDEF_P(m)) {
+    return FALSE;
   }
-  return FALSE;         /* no method */
+  return TRUE;
 }
 
 MRB_API mrb_bool
@@ -1601,45 +1742,25 @@ MRB_API mrb_value
 mrb_class_path(mrb_state *mrb, struct RClass *c)
 {
   mrb_value path;
-  const char *name;
-  mrb_sym classpath = mrb_intern_lit(mrb, "__classpath__");
+  mrb_sym nsym = mrb_intern_lit(mrb, "__classname__");
 
-  path = mrb_obj_iv_get(mrb, (struct RObject*)c, classpath);
+  path = mrb_obj_iv_get(mrb, (struct RObject*)c, nsym);
   if (mrb_nil_p(path)) {
-    struct RClass *outer = mrb_class_outer_module(mrb, c);
-    mrb_sym sym = mrb_class_sym(mrb, c, outer);
+    /* no name (yet) */
+    return mrb_class_find_path(mrb, c);
+  }
+  else if (mrb_symbol_p(path)) {
+    /* toplevel class/module */
+    const char *str;
     mrb_int len;
 
-    if (sym == 0) {
-      return mrb_nil_value();
-    }
-    else if (outer && outer != c && outer != mrb->object_class) {
-      mrb_value base = mrb_class_path(mrb, outer);
-      path = mrb_str_buf_new(mrb, 0);
-      if (mrb_nil_p(base)) {
-        mrb_str_cat_lit(mrb, path, "#<Class:");
-        mrb_str_concat(mrb, path, mrb_ptr_to_str(mrb, outer));
-        mrb_str_cat_lit(mrb, path, ">");
-      }
-      else {
-        mrb_str_concat(mrb, path, base);
-      }
-      mrb_str_cat_lit(mrb, path, "::");
-      name = mrb_sym2name_len(mrb, sym, &len);
-      mrb_str_cat(mrb, path, name, len);
-    }
-    else {
-      name = mrb_sym2name_len(mrb, sym, &len);
-      path = mrb_str_new(mrb, name, len);
-    }
-    if (!MRB_FROZEN_P(c)) {
-      mrb_obj_iv_set(mrb, (struct RObject*)c, classpath, path);
-    }
+    str = mrb_sym2name_len(mrb, mrb_symbol(path), &len);
+    return mrb_str_new(mrb, str, len);
   }
   return mrb_str_dup(mrb, path);
 }
 
-MRB_API struct RClass *
+MRB_API struct RClass*
 mrb_class_real(struct RClass* cl)
 {
   if (cl == 0)
@@ -1745,7 +1866,7 @@ mrb_obj_class(mrb_state *mrb, mrb_value obj)
 MRB_API void
 mrb_alias_method(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b)
 {
-  struct RProc *m = mrb_method_search(mrb, c, b);
+  mrb_method_t m = mrb_method_search(mrb, c, b);
 
   mrb_define_method_raw(mrb, c, a, m);
 }
@@ -1793,7 +1914,7 @@ mrb_mod_to_s(mrb_state *mrb, mrb_value klass)
     struct RClass *c;
     mrb_value path;
 
-    str = mrb_str_buf_new(mrb, 32);
+    str = mrb_str_new_capa(mrb, 32);
     c = mrb_class_ptr(klass);
     path = mrb_class_path(mrb, c);
 
@@ -1839,7 +1960,10 @@ undef_method(mrb_state *mrb, struct RClass *c, mrb_sym a)
     mrb_name_error(mrb, a, "undefined method '%S' for class '%S'", mrb_sym2str(mrb, a), mrb_obj_value(c));
   }
   else {
-    mrb_define_method_raw(mrb, c, a, NULL);
+    mrb_method_t m;
+
+    MRB_METHOD_FROM_PROC(m, NULL);
+    mrb_define_method_raw(mrb, c, a, m);
   }
 }
 
@@ -1875,6 +1999,7 @@ mod_define_method(mrb_state *mrb, mrb_value self)
 {
   struct RClass *c = mrb_class_ptr(self);
   struct RProc *p;
+  mrb_method_t m;
   mrb_sym mid;
   mrb_value proc = mrb_undef_value();
   mrb_value blk;
@@ -1897,10 +2022,17 @@ mod_define_method(mrb_state *mrb, mrb_value self)
   p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
   mrb_proc_copy(p, mrb_proc_ptr(blk));
   p->flags |= MRB_PROC_STRICT;
-  mrb_define_method_raw(mrb, c, mid, p);
+  MRB_METHOD_FROM_PROC(m, p);
+  mrb_define_method_raw(mrb, c, mid, m);
   return mrb_symbol_value(mid);
 }
 
+static mrb_value
+top_define_method(mrb_state *mrb, mrb_value self)
+{
+  return mod_define_method(mrb, mrb_obj_value(mrb->object_class));
+}
+
 static void
 check_cv_name_str(mrb_state *mrb, mrb_value str)
 {
@@ -2161,13 +2293,43 @@ mrb_mod_const_defined(mrb_state *mrb, mrb_value mod)
 }
 
 static mrb_value
+mrb_const_get_sym(mrb_state *mrb, mrb_value mod, mrb_sym id)
+{
+  check_const_name_sym(mrb, id);
+  return mrb_const_get(mrb, mod, id);
+}
+
+static mrb_value
 mrb_mod_const_get(mrb_state *mrb, mrb_value mod)
 {
+  mrb_value path;
   mrb_sym id;
+  char *ptr;
+  mrb_int off, end, len;
 
-  mrb_get_args(mrb, "n", &id);
-  check_const_name_sym(mrb, id);
-  return mrb_const_get(mrb, mod, id);
+  mrb_get_args(mrb, "o", &path);
+
+  if (mrb_symbol_p(path)) {
+    /* const get with symbol */
+    id = mrb_symbol(path);
+    return mrb_const_get_sym(mrb, mod, id);
+  }
+
+  /* const get with class path string */
+  path = mrb_string_type(mrb, path);
+  ptr = RSTRING_PTR(path);
+  len = RSTRING_LEN(path);
+  off = 0;
+
+  while (off < len) {
+    end = mrb_str_index_lit(mrb, path, "::", off);
+    end = (end == -1) ? len : end;
+    id = mrb_intern(mrb, ptr+off, end-off);
+    mod = mrb_const_get_sym(mrb, mod, id);
+    off = (end == len) ? end : end+2;
+  }
+
+  return mod;
 }
 
 static mrb_value
@@ -2178,14 +2340,7 @@ mrb_mod_const_set(mrb_state *mrb, mrb_value mod)
 
   mrb_get_args(mrb, "no", &id, &value);
   check_const_name_sym(mrb, id);
-  if ((mrb_type(value) == MRB_TT_CLASS || mrb_type(value) == MRB_TT_MODULE)
-      && !mrb_obj_iv_defined(mrb, mrb_obj_ptr(value), mrb_intern_lit(mrb, "__classid__"))) {
-    /* name unnamed classes/modules */
-    setup_class(mrb, mrb_class_ptr(mod), mrb_class_ptr(value), id);
-  }
-  else {
-    mrb_const_set(mrb, mod, id, value);
-  }
+  mrb_const_set(mrb, mod, id, value);
   return value;
 }
 
@@ -2249,7 +2404,7 @@ mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
   mrb_value *argv;
   mrb_int argc, i;
   mrb_sym mid;
-  struct RProc *method_rproc;
+  mrb_method_t m;
   struct RClass *rclass;
   int ai;
 
@@ -2269,11 +2424,11 @@ mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
 
     mid = mrb_symbol(argv[i]);
     rclass = mrb_class_ptr(mod);
-    method_rproc = mrb_method_search(mrb, rclass, mid);
+    m = mrb_method_search(mrb, rclass, mid);
 
     prepare_singleton_class(mrb, (struct RBasic*)rclass);
     ai = mrb_gc_arena_save(mrb);
-    mrb_define_method_raw(mrb, rclass->c, mid, method_rproc);
+    mrb_define_method_raw(mrb, rclass->c, mid, m);
     mrb_gc_arena_restore(mrb, ai);
   }
 
@@ -2284,6 +2439,14 @@ mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
 mrb_value mrb_obj_id_m(mrb_state *mrb, mrb_value self);
 /* implementation of instance_eval */
 mrb_value mrb_obj_instance_eval(mrb_state*, mrb_value);
+/* implementation of Module.nesting */
+mrb_value mrb_mod_s_nesting(mrb_state*, mrb_value);
+
+static mrb_value
+inspect_main(mrb_state *mrb, mrb_value mod)
+{
+  return mrb_str_new_lit(mrb, "main");
+}
 
 void
 mrb_init_class(mrb_state *mrb)
@@ -2313,10 +2476,10 @@ mrb_init_class(mrb_state *mrb)
   mrb_define_const(mrb, obj, "Class",       mrb_obj_value(cls));
 
   /* name each classes */
-  name_class(mrb, bob, mrb_intern_lit(mrb, "BasicObject"));
-  name_class(mrb, obj, mrb_intern_lit(mrb, "Object"));           /* 15.2.1 */
-  name_class(mrb, mod, mrb_intern_lit(mrb, "Module"));           /* 15.2.2 */
-  name_class(mrb, cls, mrb_intern_lit(mrb, "Class"));            /* 15.2.3 */
+  mrb_class_name_class(mrb, NULL, bob, mrb_intern_lit(mrb, "BasicObject"));
+  mrb_class_name_class(mrb, NULL, obj, mrb_intern_lit(mrb, "Object")); /* 15.2.1 */
+  mrb_class_name_class(mrb, NULL, mod, mrb_intern_lit(mrb, "Module")); /* 15.2.2 */
+  mrb_class_name_class(mrb, NULL, cls, mrb_intern_lit(mrb, "Class"));  /* 15.2.3 */
 
   mrb->proc_class = mrb_define_class(mrb, "Proc", mrb->object_class);  /* 15.2.17 */
   MRB_SET_INSTANCE_TT(mrb->proc_class, MRB_TT_PROC);
@@ -2377,7 +2540,13 @@ mrb_init_class(mrb_state *mrb)
   mrb_define_method(mrb, mod, "class_variables",         mrb_mod_class_variables,  MRB_ARGS_NONE()); /* 15.2.2.4.19 */
   mrb_define_method(mrb, mod, "===",                     mrb_mod_eqq,              MRB_ARGS_REQ(1));
   mrb_define_class_method(mrb, mod, "constants",         mrb_mod_s_constants,      MRB_ARGS_ANY());  /* 15.2.2.3.1 */
+  mrb_define_class_method(mrb, mod, "nesting",           mrb_mod_s_nesting,        MRB_ARGS_REQ(0)); /* 15.2.2.3.2 */
 
   mrb_undef_method(mrb, cls, "append_features");
   mrb_undef_method(mrb, cls, "extend_object");
+
+  mrb->top_self = (struct RObject*)mrb_obj_alloc(mrb, MRB_TT_OBJECT, mrb->object_class);
+  mrb_define_singleton_method(mrb, mrb->top_self, "inspect", inspect_main, MRB_ARGS_NONE());
+  mrb_define_singleton_method(mrb, mrb->top_self, "to_s", inspect_main, MRB_ARGS_NONE());
+  mrb_define_singleton_method(mrb, mrb->top_self, "define_method", top_define_method, MRB_ARGS_ARG(1,1));
 }
index 6b2c43b..d79a65a 100644 (file)
@@ -189,13 +189,13 @@ codedump(mrb_state *mrb, mrb_irep *irep)
       print_lv(mrb, irep, c, RA);
       break;
     case OP_JMP:
-      printf("OP_JMP\t%03d\n", i+GETARG_sBx(c));
+      printf("OP_JMP\t%03d (%d)\n", i+GETARG_sBx(c), GETARG_sBx(c));
       break;
     case OP_JMPIF:
-      printf("OP_JMPIF\tR%d\t%03d\n", GETARG_A(c), i+GETARG_sBx(c));
+      printf("OP_JMPIF\tR%d\t%03d (%d)\n", GETARG_A(c), i+GETARG_sBx(c), GETARG_sBx(c));
       break;
     case OP_JMPNOT:
-      printf("OP_JMPNOT\tR%d\t%03d\n", GETARG_A(c), i+GETARG_sBx(c));
+      printf("OP_JMPNOT\tR%d\t%03d (%d)\n", GETARG_A(c), i+GETARG_sBx(c), GETARG_sBx(c));
       break;
     case OP_SEND:
       printf("OP_SEND\tR%d\t:%s\t%d\n", GETARG_A(c),
@@ -207,6 +207,9 @@ codedump(mrb_state *mrb, mrb_irep *irep)
              mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
              GETARG_C(c));
       break;
+    case OP_CALL:
+      printf("OP_CALL\tR%d\n", GETARG_A(c));
+      break;
     case OP_TAILCALL:
       printf("OP_TAILCALL\tR%d\t:%s\t%d\n", GETARG_A(c),
              mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
@@ -311,22 +314,22 @@ codedump(mrb_state *mrb, mrb_irep *irep)
              GETARG_C(c));
       break;
     case OP_LT:
-      printf("OP_LT\tR%d\t:%s\t%d\n", GETARG_A(c),
+      printf("OP_LT\t\tR%d\t:%s\t%d\n", GETARG_A(c),
              mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
              GETARG_C(c));
       break;
     case OP_LE:
-      printf("OP_LE\tR%d\t:%s\t%d\n", GETARG_A(c),
+      printf("OP_LE\t\tR%d\t:%s\t%d\n", GETARG_A(c),
              mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
              GETARG_C(c));
       break;
     case OP_GT:
-      printf("OP_GT\tR%d\t:%s\t%d\n", GETARG_A(c),
+      printf("OP_GT\t\tR%d\t:%s\t%d\n", GETARG_A(c),
              mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
              GETARG_C(c));
       break;
     case OP_GE:
-      printf("OP_GE\tR%d\t:%s\t%d\n", GETARG_A(c),
+      printf("OP_GE\t\tR%d\t:%s\t%d\n", GETARG_A(c),
              mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
              GETARG_C(c));
       break;
@@ -459,7 +462,7 @@ codedump(mrb_state *mrb, mrb_irep *irep)
 static void
 codedump_recur(mrb_state *mrb, mrb_irep *irep)
 {
-  size_t i;
+  int i;
 
   codedump(mrb, irep);
   for (i=0; i<irep->rlen; i++) {
index d2bcd7d..e55f11d 100644 (file)
@@ -3,7 +3,7 @@
 #include <mruby/irep.h>
 #include <mruby/debug.h>
 
-static mrb_irep_debug_info_file *
+static mrb_irep_debug_info_file*
 get_file(mrb_irep_debug_info *info, uint32_t pc)
 {
   mrb_irep_debug_info_file **ret;
@@ -51,12 +51,12 @@ select_line_type(const uint16_t *lines, size_t lines_len)
 }
 
 MRB_API char const*
-mrb_debug_get_filename(mrb_irep *irep, uint32_t pc)
+mrb_debug_get_filename(mrb_irep *irep, ptrdiff_t pc)
 {
-  if (irep && pc < irep->ilen) {
+  if (irep && pc >= 0 && pc < irep->ilen) {
     mrb_irep_debug_info_file* f = NULL;
     if (!irep->debug_info) { return irep->filename; }
-    else if ((f = get_file(irep->debug_info, pc))) {
+    else if ((f = get_file(irep->debug_info, (uint32_t)pc))) {
       return f->filename;
     }
   }
@@ -64,14 +64,14 @@ mrb_debug_get_filename(mrb_irep *irep, uint32_t pc)
 }
 
 MRB_API int32_t
-mrb_debug_get_line(mrb_irep *irep, uint32_t pc)
+mrb_debug_get_line(mrb_irep *irep, ptrdiff_t pc)
 {
-  if (irep && pc < irep->ilen) {
+  if (irep && pc >= 0 && pc < irep->ilen) {
     mrb_irep_debug_info_file* f = NULL;
     if (!irep->debug_info) {
       return irep->lines? irep->lines[pc] : -1;
     }
-    else if ((f = get_file(irep->debug_info, pc))) {
+    else if ((f = get_file(irep->debug_info, (uint32_t)pc))) {
       switch (f->line_type) {
         case mrb_debug_line_ary:
           mrb_assert(f->start_pos <= pc && pc < (f->start_pos + f->line_entry_count));
@@ -108,7 +108,7 @@ mrb_debug_get_line(mrb_irep *irep, uint32_t pc)
   return -1;
 }
 
-MRB_API mrb_irep_debug_info *
+MRB_API mrb_irep_debug_info*
 mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep)
 {
   static const mrb_irep_debug_info initial = { 0, 0, NULL };
@@ -121,7 +121,7 @@ mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep)
   return ret;
 }
 
-MRB_API mrb_irep_debug_info_file *
+MRB_API mrb_irep_debug_info_file*
 mrb_debug_info_append_file(mrb_state *mrb, mrb_irep *irep,
                            uint32_t start_pos, uint32_t end_pos)
 {
index bb9ed8c..df1e171 100644 (file)
 #define FLAG_BYTEORDER_NATIVE 2
 #define FLAG_BYTEORDER_NONATIVE 0
 
+#ifndef MRB_WITHOUT_FLOAT
 #ifdef MRB_USE_FLOAT
 #define MRB_FLOAT_FMT "%.8e"
 #else
 #define MRB_FLOAT_FMT "%.16e"
 #endif
+#endif
 
 static size_t get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep);
 
@@ -79,7 +81,7 @@ static ptrdiff_t
 write_iseq_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf, uint8_t flags)
 {
   uint8_t *cur = buf;
-  uint32_t iseq_no;
+  int iseq_no;
 
   cur += uint32_to_bin(irep->ilen, cur); /* number of opcode */
   cur += write_padding(cur);
@@ -111,8 +113,8 @@ write_iseq_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf, uint8_t flags)
 static size_t
 get_pool_block_size(mrb_state *mrb, mrb_irep *irep)
 {
+  int pool_no;
   size_t size = 0;
-  size_t pool_no;
   mrb_value str;
 
   size += sizeof(uint32_t); /* plen */
@@ -131,6 +133,7 @@ get_pool_block_size(mrb_state *mrb, mrb_irep *irep)
       }
       break;
 
+#ifndef MRB_WITHOUT_FLOAT
     case MRB_TT_FLOAT:
       str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT);
       {
@@ -139,6 +142,7 @@ get_pool_block_size(mrb_state *mrb, mrb_irep *irep)
         size += (size_t)len;
       }
       break;
+#endif
 
     case MRB_TT_STRING:
       {
@@ -160,7 +164,7 @@ get_pool_block_size(mrb_state *mrb, mrb_irep *irep)
 static ptrdiff_t
 write_pool_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
 {
-  size_t pool_no;
+  int pool_no;
   uint8_t *cur = buf;
   uint16_t len;
   mrb_value str;
@@ -177,10 +181,12 @@ write_pool_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
       str = mrb_fixnum_to_str(mrb, irep->pool[pool_no], 10);
       break;
 
+#ifndef MRB_WITHOUT_FLOAT
     case MRB_TT_FLOAT:
       cur += uint8_to_bin(IREP_TT_FLOAT, cur); /* data type */
       str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT);
       break;
+#endif
 
     case MRB_TT_STRING:
       cur += uint8_to_bin(IREP_TT_STRING, cur); /* data type */
@@ -213,7 +219,7 @@ static size_t
 get_syms_block_size(mrb_state *mrb, mrb_irep *irep)
 {
   size_t size = 0;
-  uint32_t sym_no;
+  int sym_no;
   mrb_int len;
 
   size += sizeof(uint32_t); /* slen */
@@ -231,7 +237,7 @@ get_syms_block_size(mrb_state *mrb, mrb_irep *irep)
 static ptrdiff_t
 write_syms_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
 {
-  uint32_t sym_no;
+  int sym_no;
   uint8_t *cur = buf;
   const char *name;
 
@@ -273,7 +279,7 @@ static size_t
 get_irep_record_size(mrb_state *mrb, mrb_irep *irep)
 {
   size_t size = 0;
-  size_t irep_no;
+  int irep_no;
 
   size = get_irep_record_size_1(mrb, irep);
   for (irep_no = 0; irep_no < irep->rlen; irep_no++) {
@@ -285,7 +291,7 @@ get_irep_record_size(mrb_state *mrb, mrb_irep *irep)
 static int
 write_irep_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *irep_record_size, uint8_t flags)
 {
-  uint32_t i;
+  int i;
   uint8_t *src = bin;
 
   if (irep == NULL) {
@@ -399,7 +405,7 @@ static size_t
 write_lineno_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t* bin)
 {
   uint8_t *cur = bin;
-  size_t iseq_no;
+  int iseq_no;
   size_t filename_len;
   ptrdiff_t diff;
 
@@ -442,8 +448,8 @@ write_lineno_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t* bin)
 static size_t
 write_lineno_record(mrb_state *mrb, mrb_irep *irep, uint8_t* bin)
 {
-  size_t i;
   size_t rlen, size = 0;
+  int i;
 
   rlen = write_lineno_record_1(mrb, irep, bin);
   bin += rlen;
@@ -483,7 +489,7 @@ get_debug_record_size(mrb_state *mrb, mrb_irep *irep)
 {
   size_t ret = 0;
   uint16_t f_idx;
-  size_t i;
+  int i;
 
   ret += sizeof(uint32_t); /* record size */
   ret += sizeof(uint16_t); /* file count */
@@ -531,8 +537,9 @@ static size_t
 get_filename_table_size(mrb_state *mrb, mrb_irep *irep, mrb_sym **fp, uint16_t *lp)
 {
   mrb_sym *filenames = *fp;
-  size_t i, size = 0;
+  size_t size = 0;
   mrb_irep_debug_info *di = irep->debug_info;
+  int i;
 
   mrb_assert(lp);
   for (i = 0; i < di->flen; ++i) {
@@ -605,7 +612,7 @@ write_debug_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const
 
   ret = cur - bin;
   mrb_assert_int_fit(ptrdiff_t, ret, uint32_t, UINT32_MAX);
-  uint32_to_bin(ret, bin);
+  uint32_to_bin((uint32_t)ret, bin);
 
   mrb_assert_int_fit(ptrdiff_t, ret, size_t, SIZE_MAX);
   return (size_t)ret;
@@ -615,7 +622,7 @@ static size_t
 write_debug_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len)
 {
   size_t size, len;
-  size_t irep_no;
+  int irep_no;
 
   size = len = write_debug_record_1(mrb, irep, bin, filenames, filenames_len);
   bin += len;
@@ -653,7 +660,7 @@ write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const
   for (i = 0; i < filenames_len; ++i) {
     sym = mrb_sym2name_len(mrb, filenames[i], &sym_len);
     mrb_assert(sym);
-    cur += uint16_to_bin(sym_len, cur);
+    cur += uint16_to_bin((uint16_t)sym_len, cur);
     memcpy(cur, sym, sym_len);
     cur += sym_len;
     section_size += sizeof(uint16_t) + sym_len;
@@ -665,7 +672,7 @@ write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const
 
   memcpy(header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(header->section_ident));
   mrb_assert(section_size <= INT32_MAX);
-  uint32_to_bin(section_size, header->section_size);
+  uint32_to_bin((uint32_t)section_size, header->section_size);
 
   return MRB_DUMP_OK;
 }
@@ -673,7 +680,7 @@ write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const
 static void
 create_lv_sym_table(mrb_state *mrb, const mrb_irep *irep, mrb_sym **syms, uint32_t *syms_len)
 {
-  size_t i;
+  int i;
 
   if (*syms == NULL) {
     *syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * 1);
@@ -706,7 +713,7 @@ write_lv_sym_table(mrb_state *mrb, uint8_t **start, mrb_sym const *syms, uint32_
 
   for (i = 0; i < syms_len; ++i) {
     str = mrb_sym2name_len(mrb, syms[i], &str_len);
-    cur += uint16_to_bin(str_len, cur);
+    cur += uint16_to_bin((uint16_t)str_len, cur);
     memcpy(cur, str, str_len);
     cur += str_len;
   }
@@ -720,7 +727,7 @@ static int
 write_lv_record(mrb_state *mrb, const mrb_irep *irep, uint8_t **start, mrb_sym const *syms, uint32_t syms_len)
 {
   uint8_t *cur = *start;
-  size_t i;
+  int i;
 
   for (i = 0; i + 1 < irep->nlocals; ++i) {
     if (irep->lv[i].name == 0) {
@@ -748,7 +755,8 @@ write_lv_record(mrb_state *mrb, const mrb_irep *irep, uint8_t **start, mrb_sym c
 static size_t
 get_lv_record_size(mrb_state *mrb, mrb_irep *irep)
 {
-  size_t ret = 0, i;
+  size_t ret = 0;
+  int i;
 
   ret += (sizeof(uint16_t) + sizeof(uint16_t)) * (irep->nlocals - 1);
 
@@ -806,7 +814,7 @@ write_section_lv(mrb_state *mrb, mrb_irep *irep, uint8_t *start, mrb_sym const *
 
   diff = cur - start;
   mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
-  uint32_to_bin(diff, header->section_size);
+  uint32_to_bin((uint32_t)diff, header->section_size);
 
 lv_section_exit:
   return result;
@@ -841,7 +849,7 @@ write_rite_binary_header(mrb_state *mrb, size_t binary_size, uint8_t *bin, uint8
   mrb_assert(binary_size <= UINT32_MAX);
   uint32_to_bin((uint32_t)binary_size, header->binary_size);
 
-  offset = (&(header->binary_crc[0]) - bin) + sizeof(uint16_t);
+  offset = (uint32_t)((&(header->binary_crc[0]) - bin) + sizeof(uint16_t));
   crc = calc_crc_16_ccitt(bin + offset, binary_size - offset, 0);
   uint16_to_bin(crc, header->binary_crc);
 
@@ -851,7 +859,7 @@ write_rite_binary_header(mrb_state *mrb, size_t binary_size, uint8_t *bin, uint8
 static mrb_bool
 is_debug_info_defined(mrb_irep *irep)
 {
-  size_t i;
+  int i;
 
   if (!irep->debug_info) return FALSE;
   for (i=0; i<irep->rlen; i++) {
@@ -863,7 +871,7 @@ is_debug_info_defined(mrb_irep *irep)
 static mrb_bool
 is_lv_defined(mrb_irep *irep)
 {
-  size_t i;
+  int i;
 
   if (irep->lv) { return TRUE; }
 
index 0622827..5445b51 100644 (file)
@@ -71,7 +71,7 @@ exc_exception(mrb_state *mrb, mrb_value self)
 {
   mrb_value exc;
   mrb_value a;
-  int argc;
+  mrb_int argc;
 
   argc = mrb_get_args(mrb, "|o", &a);
   if (argc == 0) return self;
@@ -200,6 +200,7 @@ exc_debug_info(mrb_state *mrb, struct RObject *exc)
   mrb_callinfo *ci = mrb->c->ci;
   mrb_code *pc = ci->pc;
 
+  if (mrb_obj_iv_defined(mrb, exc, mrb_intern_lit(mrb, "file"))) return;
   while (ci >= mrb->c->cibase) {
     mrb_code *err = ci->err;
 
@@ -207,8 +208,8 @@ exc_debug_info(mrb_state *mrb, struct RObject *exc)
     if (err && ci->proc && !MRB_PROC_CFUNC_P(ci->proc)) {
       mrb_irep *irep = ci->proc->body.irep;
 
-      int32_t const line = mrb_debug_get_line(irep, (uint32_t)(err - irep->iseq));
-      char const* file = mrb_debug_get_filename(irep, (uint32_t)(err - irep->iseq));
+      int32_t const line = mrb_debug_get_line(irep, err - irep->iseq);
+      char const* file = mrb_debug_get_filename(irep, err - irep->iseq);
       if (line != -1 && file) {
         mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "file"), mrb_str_new_cstr(mrb, file));
         mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "line"), mrb_fixnum_value(line));
@@ -228,6 +229,10 @@ mrb_exc_set(mrb_state *mrb, mrb_value exc)
   }
   else {
     mrb->exc = mrb_obj_ptr(exc);
+    if (mrb->gc.arena_idx > 0 &&
+        (struct RBasic*)mrb->exc == mrb->gc.arena[mrb->gc.arena_idx-1]) {
+      mrb->gc.arena_idx--;
+    }
     if (!mrb->gc.out_of_memory) {
       exc_debug_info(mrb, mrb->exc);
       mrb_keep_backtrace(mrb, exc);
@@ -261,6 +266,7 @@ mrb_vformat(mrb_state *mrb, const char *format, va_list ap)
   const char *p = format;
   const char *b = p;
   ptrdiff_t size;
+  int ai0 = mrb_gc_arena_save(mrb);
   mrb_value ary = mrb_ary_new_capa(mrb, 4);
   int ai = mrb_gc_arena_save(mrb);
 
@@ -269,9 +275,12 @@ mrb_vformat(mrb_state *mrb, const char *format, va_list ap)
 
     if (c == '%') {
       if (*p == 'S') {
+        mrb_value val;
+
         size = p - b - 1;
         mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size));
-        mrb_ary_push(mrb, ary, va_arg(ap, mrb_value));
+        val = va_arg(ap, mrb_value);
+        mrb_ary_push(mrb, ary, mrb_obj_as_string(mrb, val));
         b = p + 1;
       }
     }
@@ -289,15 +298,20 @@ mrb_vformat(mrb_state *mrb, const char *format, va_list ap)
     mrb_gc_arena_restore(mrb, ai);
   }
   if (b == format) {
+    mrb_gc_arena_restore(mrb, ai0);
     return mrb_str_new_cstr(mrb, format);
   }
   else {
+    mrb_value val;
+
     size = p - b;
     if (size > 0) {
       mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size));
-      mrb_gc_arena_restore(mrb, ai);
     }
-    return mrb_ary_join(mrb, ary, mrb_nil_value());
+    val = mrb_ary_join(mrb, ary, mrb_nil_value());
+    mrb_gc_arena_restore(mrb, ai0);
+    mrb_gc_protect(mrb, val);
+    return val;
   }
 }
 
@@ -383,7 +397,7 @@ mrb_bug(mrb_state *mrb, const char *fmt, ...)
 }
 
 MRB_API mrb_value
-mrb_make_exception(mrb_state *mrb, int argc, const mrb_value *argv)
+mrb_make_exception(mrb_state *mrb, mrb_int argc, const mrb_value *argv)
 {
   mrb_value mesg;
   int n;
index e0810d5..1b8d44a 100644 (file)
@@ -87,19 +87,27 @@ mrb_obj_to_sym(mrb_state *mrb, mrb_value name)
 }
 
 MRB_API mrb_int
+#ifdef MRB_WITHOUT_FLOAT
+mrb_fixnum_id(mrb_int f)
+#else
 mrb_float_id(mrb_float f)
+#endif
 {
   const char *p = (const char*)&f;
   int len = sizeof(f);
-  mrb_int id = 0;
+  uint32_t id = 0;
 
+#ifndef MRB_WITHOUT_FLOAT
+  /* normalize -0.0 to 0.0 */
+  if (f == 0) f = 0.0;
+#endif
   while (len--) {
     id = id*65599 + *p;
     p++;
   }
   id = id + (id>>5);
 
-  return id;
+  return (mrb_int)id;
 }
 
 MRB_API mrb_int
@@ -123,9 +131,13 @@ mrb_obj_id(mrb_value obj)
   case MRB_TT_SYMBOL:
     return MakeID(mrb_symbol(obj));
   case MRB_TT_FIXNUM:
+#ifdef MRB_WITHOUT_FLOAT
+    return MakeID(mrb_fixnum_id(mrb_fixnum(obj)));
+#else
     return MakeID2(mrb_float_id((mrb_float)mrb_fixnum(obj)), MRB_TT_FLOAT);
   case MRB_TT_FLOAT:
     return MakeID(mrb_float_id(mrb_float(obj)));
+#endif
   case MRB_TT_STRING:
   case MRB_TT_OBJECT:
   case MRB_TT_CLASS:
@@ -146,6 +158,7 @@ mrb_obj_id(mrb_value obj)
 }
 
 #ifdef MRB_WORD_BOXING
+#ifndef MRB_WITHOUT_FLOAT
 MRB_API mrb_value
 mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f)
 {
@@ -165,6 +178,7 @@ mrb_word_boxing_float_pool(mrb_state *mrb, mrb_float f)
   nf->f = f;
   return mrb_obj_value(nf);
 }
+#endif  /* MRB_WITHOUT_FLOAT */
 
 MRB_API mrb_value
 mrb_word_boxing_cptr_value(mrb_state *mrb, void *p)
index 483e04c..f8a8f79 100644 (file)
@@ -36,6 +36,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #include <mruby.h>
 #include <mruby/string.h>
 
+#ifndef MRB_WITHOUT_FLOAT
 struct fmt_args {
   mrb_state *mrb;
   mrb_value str;
@@ -61,7 +62,7 @@ out(struct fmt_args *f, const char *s, size_t l)
 
 #define PAD_SIZE 256
 static void
-pad(struct fmt_args *f, char c, int w, int l, int fl)
+pad(struct fmt_args *f, char c, ptrdiff_t w, ptrdiff_t l, uint8_t fl)
 {
   char pad[PAD_SIZE];
   if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return;
@@ -91,16 +92,17 @@ typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)
 #endif
 
 static int
-fmt_fp(struct fmt_args *f, long double y, int w, int p, int fl, int t)
+fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t)
 {
   uint32_t big[(LDBL_MANT_DIG+28)/29 + 1          // mantissa expansion
     + (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion
   uint32_t *a, *d, *r, *z;
   uint32_t i;
-  int e2=0, e, j, l;
+  int e2=0, e, j;
+  ptrdiff_t l;
   char buf[9+LDBL_MANT_DIG/4], *s;
   const char *prefix="-0X+0X 0X-0x+0x 0x";
-  int pl;
+  ptrdiff_t pl;
   char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr;
 
   pl=1;
@@ -115,11 +117,11 @@ fmt_fp(struct fmt_args *f, long double y, int w, int p, int fl, int t)
   if (!isfinite(y)) {
     const char *ss = (t&32)?"inf":"INF";
     if (y!=y) ss=(t&32)?"nan":"NAN";
-    pad(f, ' ', w, 3+pl, fl&~ZERO_PAD);
+    pad(f, ' ', 0, 3+pl, fl&~ZERO_PAD);
     out(f, prefix, pl);
     out(f, ss, 3);
-    pad(f, ' ', w, 3+pl, fl^LEFT_ADJ);
-    return MAX(w, 3+pl);
+    pad(f, ' ', 0, 3+pl, fl^LEFT_ADJ);
+    return 3+(int)pl;
   }
 
   y = frexp((double)y, &e2) * 2;
@@ -127,7 +129,7 @@ fmt_fp(struct fmt_args *f, long double y, int w, int p, int fl, int t)
 
   if ((t|32)=='a') {
     long double round = 8.0;
-    int re;
+    ptrdiff_t re;
 
     if (t&32) prefix += 9;
     pl += 2;
@@ -167,14 +169,14 @@ fmt_fp(struct fmt_args *f, long double y, int w, int p, int fl, int t)
     else
       l = (s-buf) + (ebuf-estr);
 
-    pad(f, ' ', w, pl+l, fl);
+    pad(f, ' ', 0, pl+l, fl);
     out(f, prefix, pl);
-    pad(f, '0', w, pl+l, fl^ZERO_PAD);
+    pad(f, '0', 0, pl+l, fl^ZERO_PAD);
     out(f, buf, s-buf);
     pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0);
     out(f, estr, ebuf-estr);
-    pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
-    return MAX(w, pl+l);
+    pad(f, ' ', 0, pl+l, fl^LEFT_ADJ);
+    return (int)pl+(int)l;
   }
   if (p<0) p=6;
 
@@ -202,7 +204,7 @@ fmt_fp(struct fmt_args *f, long double y, int w, int p, int fl, int t)
   }
   while (e2<0) {
     uint32_t carry=0, *b;
-    int sh=MIN(9,-e2), need=1+(p+LDBL_MANT_DIG/3+8)/9;
+    int sh=MIN(9,-e2), need=1+((int)p+LDBL_MANT_DIG/3+8)/9;
     for (d=a; d<z; d++) {
       uint32_t rm = *d & ((1<<sh)-1);
       *d = (*d>>sh) + carry;
@@ -216,11 +218,11 @@ fmt_fp(struct fmt_args *f, long double y, int w, int p, int fl, int t)
     e2+=sh;
   }
 
-  if (a<z) for (i=10, e=9*(r-a); *a>=i; i*=10, e++);
+  if (a<z) for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++);
   else e=0;
 
   /* Perform rounding: j is precision after the radix (possibly neg) */
-  j = p - ((t|32)!='f')*e - ((t|32)=='g' && p);
+  j = (int)p - ((t|32)!='f')*e - ((t|32)=='g' && p);
   if (j < 9*(z-r-1)) {
     uint32_t x;
     /* We avoid C's broken division of negative numbers */
@@ -247,7 +249,7 @@ fmt_fp(struct fmt_args *f, long double y, int w, int p, int fl, int t)
           if (d<a) *--a=0;
           (*d)++;
         }
-        for (i=10, e=9*(r-a); *a>=i; i*=10, e++);
+        for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++);
       }
     }
     if (z>d+1) z=d+1;
@@ -286,9 +288,9 @@ fmt_fp(struct fmt_args *f, long double y, int w, int p, int fl, int t)
     l += ebuf-estr;
   }
 
-  pad(f, ' ', w, pl+l, fl);
+  pad(f, ' ', 0, pl+l, fl);
   out(f, prefix, pl);
-  pad(f, '0', w, pl+l, fl^ZERO_PAD);
+  pad(f, '0', 0, pl+l, fl^ZERO_PAD);
 
   if ((t|32)=='f') {
     if (a>r) a=r;
@@ -317,21 +319,21 @@ fmt_fp(struct fmt_args *f, long double y, int w, int p, int fl, int t)
         if (p>0||(fl&ALT_FORM)) out(f, ".", 1);
       }
       out(f, ss, MIN(buf+9-ss, p));
-      p -= buf+9-ss;
+      p -= (int)(buf+9-ss);
     }
     pad(f, '0', p+18, 18, 0);
     out(f, estr, ebuf-estr);
   }
 
-  pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
+  pad(f, ' ', 0, pl+l, fl^LEFT_ADJ);
 
-  return MAX(w, pl+l);
+  return (int)pl+(int)l;
 }
 
 static int
 fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo)
 {
-  int p;
+  ptrdiff_t p;
 
   if (*fmt != '%') {
     return -1;
@@ -351,7 +353,7 @@ fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo)
   switch (*fmt) {
   case 'e': case 'f': case 'g': case 'a':
   case 'E': case 'F': case 'G': case 'A':
-    return fmt_fp(f, flo, 0, p, 0, *fmt);
+    return fmt_fp(f, flo, p, 0, *fmt);
   default:
     return -1;
   }
@@ -363,9 +365,10 @@ mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
   struct fmt_args f;
 
   f.mrb = mrb;
-  f.str = mrb_str_buf_new(mrb, 24);
+  f.str = mrb_str_new_capa(mrb, 24);
   if (fmt_core(&f, fmt, mrb_float(flo)) < 0) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format string");
   }
   return f.str;
 }
+#endif
index 8cc8feb..8e20727 100644 (file)
@@ -114,7 +114,9 @@ typedef struct {
     struct RException exc;
     struct RBreak brk;
 #ifdef MRB_WORD_BOXING
+#ifndef MRB_WITHOUT_FLOAT
     struct RFloat floatv;
+#endif
     struct RCptr cptr;
 #endif
   } as;
@@ -175,7 +177,7 @@ gettimeofday_time(void)
 
 #define GC_STEP_SIZE 1024
 
-/* white: 011, black: 100, gray: 000 */
+/* white: 001 or 010, black: 100, gray: 000 */
 #define GC_GRAY 0
 #define GC_WHITE_A 1
 #define GC_WHITE_B (1 << 1)
@@ -342,7 +344,8 @@ add_heap(mrb_state *mrb, mrb_gc *gc)
 
 #define DEFAULT_GC_INTERVAL_RATIO 200
 #define DEFAULT_GC_STEP_RATIO 200
-#define DEFAULT_MAJOR_GC_INC_RATIO 200
+#define MAJOR_GC_INC_RATIO 120
+#define MAJOR_GC_TOOMANY 10000
 #define is_generational(gc) ((gc)->generational)
 #define is_major_gc(gc) (is_generational(gc) && (gc)->full)
 #define is_minor_gc(gc) (is_generational(gc) && !(gc)->full)
@@ -412,7 +415,7 @@ gc_protect(mrb_state *mrb, mrb_gc *gc, struct RBasic *p)
 #else
   if (gc->arena_idx >= gc->arena_capa) {
     /* extend arena */
-    gc->arena_capa = (int)(gc->arena_capa * 1.5);
+    gc->arena_capa = (int)(gc->arena_capa * 3 / 2);
     gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*gc->arena_capa);
   }
 #endif
@@ -551,7 +554,7 @@ mark_context_stack(mrb_state *mrb, struct mrb_context *c)
   size_t i;
   size_t e;
   mrb_value nil;
-  int nregs;
+  mrb_int nregs;
 
   if (c->stack == NULL) return;
   e = c->stack - c->stbase;
@@ -582,6 +585,7 @@ mark_context(mrb_state *mrb, struct mrb_context *c)
   int i;
   mrb_callinfo *ci;
 
+ start:
   if (c->status == MRB_FIBER_TERMINATED) return;
 
   /* mark VM stack */
@@ -596,14 +600,14 @@ mark_context(mrb_state *mrb, struct mrb_context *c)
     }
   }
   /* mark ensure stack */
-  for (i=0; i<c->esize; i++) {
-    if (c->ensure[i] == NULL) break;
+  for (i=0; i<c->eidx; i++) {
     mrb_gc_mark(mrb, (struct RBasic*)c->ensure[i]);
   }
   /* mark fibers */
   mrb_gc_mark(mrb, (struct RBasic*)c->fib);
   if (c->prev) {
-    mark_context(mrb, c->prev);
+    c = c->prev;
+    goto start;
   }
 }
 
@@ -645,8 +649,8 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
     {
       struct RProc *p = (struct RProc*)obj;
 
-      mrb_gc_mark(mrb, (struct RBasic*)p->env);
-      mrb_gc_mark(mrb, (struct RBasic*)p->target_class);
+      mrb_gc_mark(mrb, (struct RBasic*)p->upper);
+      mrb_gc_mark(mrb, (struct RBasic*)p->e.env);
     }
     break;
 
@@ -655,11 +659,8 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
       struct REnv *e = (struct REnv*)obj;
       mrb_int i, len;
 
-      if (MRB_ENV_STACK_SHARED_P(e)) {
-        if (e->cxt.c->fib) {
-          mrb_gc_mark(mrb, (struct RBasic*)e->cxt.c->fib);
-        }
-        break;
+      if (MRB_ENV_STACK_SHARED_P(e) && e->cxt && e->cxt->fib) {
+        mrb_gc_mark(mrb, (struct RBasic*)e->cxt->fib);
       }
       len = MRB_ENV_STACK_LEN(e);
       for (i=0; i<len; i++) {
@@ -693,6 +694,10 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
     break;
 
   case MRB_TT_STRING:
+    if (RSTR_FSHARED_P(obj) && !RSTR_NOFREE_P(obj)) {
+      struct RString *s = (struct RString*)obj;
+      mrb_gc_mark(mrb, (struct RBasic*)s->as.heap.aux.fshared);
+    }
     break;
 
   case MRB_TT_RANGE:
@@ -732,12 +737,14 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end)
     /* cannot happen */
     return;
 
+#ifndef MRB_WITHOUT_FLOAT
   case MRB_TT_FLOAT:
 #ifdef MRB_WORD_BOXING
     break;
 #else
     return;
 #endif
+#endif
 
   case MRB_TT_OBJECT:
     mrb_gc_free_iv(mrb, (struct RObject*)obj);
@@ -774,17 +781,19 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end)
     {
       struct mrb_context *c = ((struct RFiber*)obj)->cxt;
 
-      if (!end && c && c != mrb->root_c) {
+      if (c && c != mrb->root_c) {
         mrb_callinfo *ci = c->ci;
         mrb_callinfo *ce = c->cibase;
 
-        while (ce <= ci) {
-          struct REnv *e = ci->env;
-          if (e && !is_dead(&mrb->gc, e) &&
-              e->tt == MRB_TT_ENV && MRB_ENV_STACK_SHARED_P(e)) {
-            mrb_env_unshare(mrb, e);
+        if (!end) {
+          while (ce <= ci) {
+            struct REnv *e = ci->env;
+            if (e && !is_dead(&mrb->gc, e) &&
+                e->tt == MRB_TT_ENV && MRB_ENV_STACK_SHARED_P(e)) {
+              mrb_env_unshare(mrb, e);
+            }
+            ci--;
           }
-          ci--;
         }
         mrb_free_context(mrb, c);
       }
@@ -812,7 +821,11 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end)
       struct RProc *p = (struct RProc*)obj;
 
       if (!MRB_PROC_CFUNC_P(p) && p->body.irep) {
-        mrb_irep_decref(mrb, p->body.irep);
+        mrb_irep *irep = p->body.irep;
+        if (end) {
+          mrb_irep_cutref(mrb, irep);
+        }
+        mrb_irep_decref(mrb, irep);
       }
     }
     break;
@@ -862,8 +875,11 @@ root_scan_phase(mrb_state *mrb, mrb_gc *gc)
   mrb_gc_mark(mrb, (struct RBasic*)mrb->string_class);
   mrb_gc_mark(mrb, (struct RBasic*)mrb->array_class);
   mrb_gc_mark(mrb, (struct RBasic*)mrb->hash_class);
+  mrb_gc_mark(mrb, (struct RBasic*)mrb->range_class);
 
+#ifndef MRB_WITHOUT_FLOAT
   mrb_gc_mark(mrb, (struct RBasic*)mrb->float_class);
+#endif
   mrb_gc_mark(mrb, (struct RBasic*)mrb->fixnum_class);
   mrb_gc_mark(mrb, (struct RBasic*)mrb->true_class);
   mrb_gc_mark(mrb, (struct RBasic*)mrb->false_class);
@@ -1194,8 +1210,17 @@ mrb_incremental_gc(mrb_state *mrb)
     }
 
     if (is_major_gc(gc)) {
-      gc->majorgc_old_threshold = gc->live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO;
+      size_t threshold = gc->live_after_mark/100 * MAJOR_GC_INC_RATIO;
+
       gc->full = FALSE;
+      if (threshold < MAJOR_GC_TOOMANY) {
+        gc->majorgc_old_threshold = threshold;
+      }
+      else {
+        /* too many objects allocated during incremental GC, */
+        /* instead of increasing threshold, invoke full GC. */
+        mrb_full_gc(mrb);
+      }
     }
     else if (is_minor_gc(gc)) {
       if (gc->live > gc->majorgc_old_threshold) {
@@ -1233,7 +1258,7 @@ mrb_full_gc(mrb_state *mrb)
   gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio;
 
   if (is_generational(gc)) {
-    gc->majorgc_old_threshold = gc->live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO;
+    gc->majorgc_old_threshold = gc->live_after_mark/100 * MAJOR_GC_INC_RATIO;
     gc->full = FALSE;
   }
 
@@ -1383,7 +1408,7 @@ gc_interval_ratio_set(mrb_state *mrb, mrb_value obj)
   mrb_int ratio;
 
   mrb_get_args(mrb, "i", &ratio);
-  mrb->gc.interval_ratio = ratio;
+  mrb->gc.interval_ratio = (int)ratio;
   return mrb_nil_value();
 }
 
@@ -1416,7 +1441,7 @@ gc_step_ratio_set(mrb_state *mrb, mrb_value obj)
   mrb_int ratio;
 
   mrb_get_args(mrb, "i", &ratio);
-  mrb->gc.step_ratio = ratio;
+  mrb->gc.step_ratio = (int)ratio;
   return mrb_nil_value();
 }
 
@@ -1434,7 +1459,7 @@ change_gen_gc_mode(mrb_state *mrb, mrb_gc *gc, mrb_bool enable)
   }
   else if (!is_generational(gc) && enable) {
     incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
-    gc->majorgc_old_threshold = gc->live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO;
+    gc->majorgc_old_threshold = gc->live_after_mark/100 * MAJOR_GC_INC_RATIO;
     gc->full = FALSE;
   }
   gc->generational = enable;
@@ -1499,6 +1524,7 @@ mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, vo
 {
   mrb_bool iterating = mrb->gc.iterating;
 
+  mrb_full_gc(mrb);
   mrb->gc.iterating = TRUE;
   if (iterating) {
     gc_each_objects(mrb, &mrb->gc, callback, data);
index 27d25b3..d587069 100644 (file)
 #include <mruby/string.h>
 #include <mruby/variable.h>
 
+#ifndef MRB_WITHOUT_FLOAT
 /* a function to get hash value of a float number */
 mrb_int mrb_float_id(mrb_float f);
+#endif
 
 static inline khint_t
 mrb_hash_ht_hash_func(mrb_state *mrb, mrb_value key)
 {
   enum mrb_vtype t = mrb_type(key);
   mrb_value hv;
-  const char *p;
-  mrb_int i, len;
   khint_t h;
 
   switch (t) {
   case MRB_TT_STRING:
-    p = RSTRING_PTR(key);
-    len = RSTRING_LEN(key);
-    h = 0;
-    for (i=0; i<len; i++) {
-      h = (h << 5) - h + *p++;
-    }
-    return h;
+    h = mrb_str_hash(mrb, key);
+    break;
 
+  case MRB_TT_TRUE:
+  case MRB_TT_FALSE:
   case MRB_TT_SYMBOL:
-    h = (khint_t)mrb_symbol(key);
-    return kh_int_hash_func(mrb, h);
-
   case MRB_TT_FIXNUM:
-    h = (khint_t)mrb_float_id((mrb_float)mrb_fixnum(key));
-    return kh_int_hash_func(mrb, h);
-
+#ifndef MRB_WITHOUT_FLOAT
   case MRB_TT_FLOAT:
-    h = (khint_t)mrb_float_id(mrb_float(key));
-    return kh_int_hash_func(mrb, h);
+#endif
+    h = (khint_t)mrb_obj_id(key);
+    break;
 
   default:
     hv = mrb_funcall(mrb, key, "hash", 0);
-    h = (khint_t)t ^ mrb_fixnum(hv);
-    return kh_int_hash_func(mrb, h);
+    h = (khint_t)t ^ (khint_t)mrb_fixnum(hv);
+    break;
   }
+  return kh_int_hash_func(mrb, h);
 }
 
 static inline khint_t
@@ -70,12 +64,15 @@ mrb_hash_ht_hash_equal(mrb_state *mrb, mrb_value a, mrb_value b)
     switch (mrb_type(b)) {
     case MRB_TT_FIXNUM:
       return mrb_fixnum(a) == mrb_fixnum(b);
+#ifndef MRB_WITHOUT_FLOAT
     case MRB_TT_FLOAT:
       return (mrb_float)mrb_fixnum(a) == mrb_float(b);
+#endif
     default:
       return FALSE;
     }
 
+#ifndef MRB_WITHOUT_FLOAT
   case MRB_TT_FLOAT:
     switch (mrb_type(b)) {
     case MRB_TT_FIXNUM:
@@ -85,6 +82,7 @@ mrb_hash_ht_hash_equal(mrb_state *mrb, mrb_value a, mrb_value b)
     default:
       return FALSE;
     }
+#endif
 
   default:
     return mrb_eql(mrb, a, b);
@@ -145,10 +143,11 @@ mrb_hash_new_capa(mrb_state *mrb, mrb_int capa)
   struct RHash *h;
 
   h = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class);
-  h->ht = kh_init(ht, mrb);
-  if (capa > 0) {
-    kh_resize(ht, mrb, h->ht, capa);
-  }
+  /* khash needs 1/4 empty space so it is not resized immediately */
+  if (capa == 0)
+    h->ht = 0;
+  else
+    h->ht = kh_init_size(ht, mrb, (khint_t)(capa*4/3));
   h->iv = 0;
   return mrb_obj_value(h);
 }
@@ -287,7 +286,7 @@ static void
 mrb_hash_modify(mrb_state *mrb, mrb_value hash)
 {
   if (MRB_FROZEN_P(mrb_hash_ptr(hash))) {
-    mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen hash");
+    mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen hash");
   }
   mrb_hash_tbl(mrb, hash);
 }
index bd21ddf..e9dc93b 100644 (file)
@@ -31,8 +31,14 @@ typedef enum {
 MRB_API mrb_bool
 mrb_func_basic_p(mrb_state *mrb, mrb_value obj, mrb_sym mid, mrb_func_t func)
 {
-  struct RProc *me = mrb_method_search(mrb, mrb_class(mrb, obj), mid);
-  if (MRB_PROC_CFUNC_P(me) && (me->body.func == func))
+  mrb_method_t m = mrb_method_search(mrb, mrb_class(mrb, obj), mid);
+  struct RProc *p;
+
+  if (MRB_METHOD_UNDEF_P(m)) return FALSE;
+  if (MRB_METHOD_FUNC_P(m))
+    return MRB_METHOD_FUNC(m) == func;
+  p = MRB_METHOD_PROC(m);
+  if (MRB_PROC_CFUNC_P(p) && (MRB_PROC_CFUNC(p) == func))
     return TRUE;
   return FALSE;
 }
@@ -134,37 +140,53 @@ mrb_obj_id_m(mrb_state *mrb, mrb_value self)
 static mrb_value
 mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self)
 {
-  mrb_callinfo *ci = mrb->c->ci;
+  mrb_callinfo *ci = &mrb->c->ci[-1];
+  mrb_callinfo *cibase = mrb->c->cibase;
   mrb_value *bp;
+  struct RProc *p;
 
-  bp = ci->stackent + 1;
-  ci--;
-  if (ci <= mrb->c->cibase) {
+  if (ci <= cibase) {
+    /* toplevel does not have block */
+    return mrb_false_value();
+  }
+  p = ci->proc;
+  /* search method/class/module proc */
+  while (p) {
+    if (MRB_PROC_SCOPE_P(p)) break;
+    p = p->upper;
+  }
+  if (p == NULL) return mrb_false_value();
+  /* search ci corresponding to proc */
+  while (cibase < ci) {
+    if (ci->proc == p) break;
+    ci--;
+  }
+  if (ci == cibase) {
     return mrb_false_value();
   }
-  /* block_given? called within block; check upper scope */
-  if (ci->proc->env) {
-    struct REnv *e = ci->proc->env;
+  else if (ci->env) {
+    struct REnv *e = ci->env;
+    int bidx;
 
-    while (e->c) {
-      e = (struct REnv*)e->c;
-    }
     /* top-level does not have block slot (always false) */
     if (e->stack == mrb->c->stbase)
       return mrb_false_value();
-    if (e->stack && e->cioff < 0) {
-      /* use saved block arg position */
-      bp = &e->stack[-e->cioff];
-      ci = 0;                 /* no callinfo available */
+    /* use saved block arg position */
+    bidx = MRB_ENV_BIDX(e);
+    /* bidx may be useless (e.g. define_method) */
+    if (bidx >= MRB_ENV_STACK_LEN(e))
+      return mrb_false_value();
+    bp = &e->stack[bidx];
+  }
+  else {
+    bp = ci[1].stackent+1;
+    if (ci->argc >= 0) {
+      bp += ci->argc;
     }
     else {
-      ci = e->cxt.c->cibase + e->cioff;
-      bp = ci[1].stackent + 1;
+      bp++;
     }
   }
-  if (ci && ci->argc > 0) {
-    bp += ci->argc;
-  }
   if (mrb_nil_p(*bp))
     return mrb_false_value();
   return mrb_true_value();
@@ -426,7 +448,9 @@ mrb_obj_freeze(mrb_state *mrb, mrb_value self)
     case MRB_TT_TRUE:
     case MRB_TT_FIXNUM:
     case MRB_TT_SYMBOL:
+#ifndef MRB_WITHOUT_FLOAT
     case MRB_TT_FLOAT:
+#endif
       return self;
     default:
       break;
@@ -449,7 +473,9 @@ mrb_obj_frozen(mrb_state *mrb, mrb_value self)
     case MRB_TT_TRUE:
     case MRB_TT_FIXNUM:
     case MRB_TT_SYMBOL:
+#ifndef MRB_WITHOUT_FLOAT
     case MRB_TT_FLOAT:
+#endif
       return mrb_true_value();
     default:
       break;
@@ -655,9 +681,11 @@ method_entry_loop(mrb_state *mrb, struct RClass* klass, khash_t(st)* set)
   khint_t i;
 
   khash_t(mt) *h = klass->mt;
-  if (!h) return;
+  if (!h || kh_size(h) == 0) return;
   for (i=0;i<kh_end(h);i++) {
-    if (kh_exist(h, i) && kh_value(h, i)) {
+    if (kh_exist(h, i)) {
+      mrb_method_t m = kh_value(h, i);
+      if (MRB_METHOD_UNDEF_P(m)) continue;
       kh_put(st, mrb, set, kh_key(h, i));
     }
   }
@@ -690,7 +718,7 @@ mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass* kl
     klass = klass->super;
   }
 
-  ary = mrb_ary_new(mrb);
+  ary = mrb_ary_new_capa(mrb, kh_size(set));
   for (i=0;i<kh_end(set);i++) {
     if (kh_exist(set, i)) {
       mrb_ary_push(mrb, ary, mrb_symbol_value(kh_key(set, i)));
@@ -855,7 +883,7 @@ MRB_API mrb_value
 mrb_f_raise(mrb_state *mrb, mrb_value self)
 {
   mrb_value a[2], exc;
-  int argc;
+  mrb_int argc;
 
 
   argc = mrb_get_args(mrb, "|oo", &a[0], &a[1]);
@@ -878,6 +906,16 @@ mrb_f_raise(mrb_state *mrb, mrb_value self)
   return mrb_nil_value();            /* not reached */
 }
 
+static mrb_value
+mrb_krn_class_defined(mrb_state *mrb, mrb_value self)
+{
+  mrb_value str;
+
+  mrb_get_args(mrb, "S", &str);
+  return mrb_bool_value(mrb_class_defined(mrb, RSTRING_PTR(str)));
+}
+
+
 /* 15.3.1.3.41 */
 /*
  *  call-seq:
@@ -918,26 +956,7 @@ mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self)
 void
 mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args)
 {
-  mrb_sym inspect;
-  mrb_value repr;
-
-  inspect = mrb_intern_lit(mrb, "inspect");
-  if (mrb->c->ci > mrb->c->cibase && mrb->c->ci[-1].mid == inspect) {
-    /* method missing in inspect; avoid recursion */
-    repr = mrb_any_to_s(mrb, self);
-  }
-  else if (mrb_respond_to(mrb, self, inspect) && mrb->c->ci - mrb->c->cibase < 16) {
-    repr = mrb_funcall_argv(mrb, self, inspect, 0, 0);
-    if (mrb_string_p(repr) && RSTRING_LEN(repr) > 64) {
-      repr = mrb_any_to_s(mrb, self);
-    }
-  }
-  else {
-    repr = mrb_any_to_s(mrb, self);
-  }
-
-  mrb_no_method_error(mrb, name, args, "undefined method '%S' for %S",
-                      mrb_sym2str(mrb, name), repr);
+  mrb_no_method_error(mrb, name, args, "undefined method '%S'", mrb_sym2str(mrb, name));
 }
 
 /* 15.3.1.3.30 */
@@ -1104,6 +1123,7 @@ static mrb_value
 mod_define_singleton_method(mrb_state *mrb, mrb_value self)
 {
   struct RProc *p;
+  mrb_method_t m;
   mrb_sym mid;
   mrb_value blk = mrb_nil_value();
 
@@ -1114,7 +1134,8 @@ mod_define_singleton_method(mrb_state *mrb, mrb_value self)
   p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
   mrb_proc_copy(p, mrb_proc_ptr(blk));
   p->flags |= MRB_PROC_STRICT;
-  mrb_define_method_raw(mrb, mrb_class_ptr(mrb_singleton_class(mrb, self)), mid, p);
+  MRB_METHOD_FROM_PROC(m, p);
+  mrb_define_method_raw(mrb, mrb_class_ptr(mrb_singleton_class(mrb, self)), mid, m);
   return mrb_symbol_value(mid);
 }
 
@@ -1135,12 +1156,24 @@ mrb_obj_ceqq(mrb_state *mrb, mrb_value self)
   return mrb_false_value();
 }
 
+/* 15.3.1.2.7 */
+/*
+ *  call-seq:
+ *     local_variables   -> array
+ *
+ *  Returns the names of local variables in the current scope.
+ *
+ *  [mruby limitation]
+ *  If variable symbol information was stripped out from
+ *  compiled binary files using `mruby-strip -l`, this
+ *  method always returns an empty array.
+ */
 static mrb_value
 mrb_local_variables(mrb_state *mrb, mrb_value self)
 {
   struct RProc *proc;
+  mrb_irep *irep;
   mrb_value vars;
-  struct mrb_irep *irep;
   size_t i;
 
   proc = mrb->c->ci[-1].proc;
@@ -1148,34 +1181,20 @@ mrb_local_variables(mrb_state *mrb, mrb_value self)
   if (MRB_PROC_CFUNC_P(proc)) {
     return mrb_ary_new(mrb);
   }
-
-  irep = proc->body.irep;
-  if (!irep->lv) {
-    return mrb_ary_new(mrb);
-  }
   vars = mrb_hash_new(mrb);
-  for (i = 0; i + 1 < irep->nlocals; ++i) {
-    if (irep->lv[i].name) {
-      mrb_hash_set(mrb, vars, mrb_symbol_value(irep->lv[i].name), mrb_true_value());
-    }
-  }
-  if (proc->env) {
-    struct REnv *e = proc->env;
-
-    while (e) {
-      if (MRB_ENV_STACK_SHARED_P(e) &&
-          !MRB_PROC_CFUNC_P(e->cxt.c->cibase[e->cioff].proc)) {
-        irep = e->cxt.c->cibase[e->cioff].proc->body.irep;
-        if (irep->lv) {
-          for (i = 0; i + 1 < irep->nlocals; ++i) {
-            if (irep->lv[i].name) {
-              mrb_hash_set(mrb, vars, mrb_symbol_value(irep->lv[i].name), mrb_true_value());
-            }
-          }
-        }
+  while (proc) {
+    if (MRB_PROC_CFUNC_P(proc)) break;
+    irep = proc->body.irep;
+    if (!irep->lv) break;
+    for (i = 0; i + 1 < irep->nlocals; ++i) {
+      if (irep->lv[i].name) {
+        mrb_hash_set(mrb, vars, mrb_symbol_value(irep->lv[i].name), mrb_true_value());
       }
-      e = (struct REnv*)e->c;
     }
+    if (!MRB_PROC_ENV_P(proc)) break;
+    proc = proc->upper;
+    //if (MRB_PROC_SCOPE_P(proc)) break;
+    if (!proc->c) break;
   }
 
   return mrb_hash_keys(mrb, vars);
@@ -1238,6 +1257,8 @@ mrb_init_kernel(mrb_state *mrb)
   mrb_define_method(mrb, krn, "to_s",                       mrb_any_to_s,                    MRB_ARGS_NONE());    /* 15.3.1.3.46 */
   mrb_define_method(mrb, krn, "__case_eqq",                 mrb_obj_ceqq,                    MRB_ARGS_REQ(1));    /* internal */
 
+  mrb_define_method(mrb, krn, "class_defined?",             mrb_krn_class_defined,           MRB_ARGS_REQ(1));
+
   mrb_include_module(mrb, mrb->object_class, mrb->kernel_module);
   mrb_alias_method(mrb, mrb->module_class, mrb_intern_lit(mrb, "dup"), mrb_intern_lit(mrb, "clone"));
 }
index 4a0dcb0..3a6ce7c 100644 (file)
@@ -24,7 +24,7 @@
 #define FLAG_SRC_MALLOC 1
 #define FLAG_SRC_STATIC 0
 
-#define SIZE_ERROR_MUL(nmemb, size) ((nmemb) > SIZE_MAX / (size))
+#define SIZE_ERROR_MUL(nmemb, size) ((size_t)(nmemb) > SIZE_MAX / (size))
 
 static size_t
 skip_padding(const uint8_t *buf)
@@ -43,11 +43,11 @@ offset_crc_body(void)
 static mrb_irep*
 read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags)
 {
-  size_t i;
+  int i;
   const uint8_t *src = bin;
   ptrdiff_t diff;
   uint16_t tt, pool_data_len, snl;
-  size_t plen;
+  int plen;
   int ai = mrb_gc_arena_save(mrb);
   mrb_irep *irep = mrb_add_irep(mrb);
 
@@ -104,7 +104,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag
   }
 
   /* POOL BLOCK */
-  plen = (size_t)bin_to_uint32(src); /* number of pool */
+  plen = bin_to_uint32(src); /* number of pool */
   src += sizeof(uint32_t);
   if (plen > 0) {
     if (SIZE_ERROR_MUL(plen, sizeof(mrb_value))) {
@@ -126,13 +126,16 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag
       }
       src += pool_data_len;
       switch (tt) { /* pool data */
-      case IREP_TT_FIXNUM:
-        irep->pool[i] = mrb_str_to_inum(mrb, s, 10, FALSE);
-        break;
+      case IREP_TT_FIXNUM: {
+        mrb_value num = mrb_str_to_inum(mrb, s, 10, FALSE);
+        irep->pool[i] = mrb_float_p(num)? mrb_float_pool(mrb, mrb_float(num)) : num;
+      } break;
 
+#ifndef MRB_WITHOUT_FLOAT
       case IREP_TT_FLOAT:
         irep->pool[i] = mrb_float_pool(mrb, mrb_str_to_dbl(mrb, s, FALSE));
         break;
+#endif
 
       case IREP_TT_STRING:
         irep->pool[i] = mrb_str_pool(mrb, s);
@@ -191,7 +194,7 @@ static mrb_irep*
 read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags)
 {
   mrb_irep *irep = read_irep_record_1(mrb, bin, len, flags);
-  size_t i;
+  int i;
 
   if (irep == NULL) {
     return NULL;
@@ -262,7 +265,7 @@ static int
 read_lineno_record(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, size_t *lenp)
 {
   int result = read_lineno_record_1(mrb, bin, irep, lenp);
-  size_t i;
+  int i;
 
   if (result != MRB_DUMP_OK) return result;
   for (i = 0; i < irep->rlen; i++) {
@@ -293,13 +296,14 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *
 {
   const uint8_t *bin = start;
   ptrdiff_t diff;
-  size_t record_size, i;
+  size_t record_size;
   uint16_t f_idx;
+  int i;
 
   if (irep->debug_info) { return MRB_DUMP_INVALID_IREP; }
 
   irep->debug_info = (mrb_irep_debug_info*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info));
-  irep->debug_info->pc_count = irep->ilen;
+  irep->debug_info->pc_count = (uint32_t)irep->ilen;
 
   record_size = (size_t)bin_to_uint32(bin);
   bin += sizeof(uint32_t);
@@ -432,8 +436,8 @@ static int
 read_lv_record(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, size_t *record_len, mrb_sym const *syms, uint32_t syms_len)
 {
   const uint8_t *bin = start;
-  size_t i;
   ptrdiff_t diff;
+  int i;
 
   irep->lv = (struct mrb_locals*)mrb_malloc(mrb, sizeof(struct mrb_locals) * (irep->nlocals - 1));
 
@@ -622,10 +626,11 @@ irep_error(mrb_state *mrb)
   mrb_exc_set(mrb, mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "irep load error"));
 }
 
-MRB_API mrb_value
-mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c)
+void mrb_codedump_all(mrb_state*, struct RProc*);
+
+static mrb_value
+load_irep(mrb_state *mrb, mrb_irep *irep, mrbc_context *c)
 {
-  mrb_irep *irep = mrb_read_irep(mrb, bin);
   struct RProc *proc;
 
   if (!irep) {
@@ -633,12 +638,20 @@ mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c)
     return mrb_nil_value();
   }
   proc = mrb_proc_new(mrb, irep);
+  proc->c = NULL;
   mrb_irep_decref(mrb, irep);
+  if (c && c->dump_result) mrb_codedump_all(mrb, proc);
   if (c && c->no_exec) return mrb_obj_value(proc);
   return mrb_top_run(mrb, proc, mrb_top_self(mrb), 0);
 }
 
 MRB_API mrb_value
+mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c)
+{
+  return load_irep(mrb, mrb_read_irep(mrb, bin), c);
+}
+
+MRB_API mrb_value
 mrb_load_irep(mrb_state *mrb, const uint8_t *bin)
 {
   return mrb_load_irep_cxt(mrb, bin, NULL);
@@ -680,25 +693,10 @@ irep_exit:
   return irep;
 }
 
-void mrb_codedump_all(mrb_state*, struct RProc*);
-
 MRB_API mrb_value
 mrb_load_irep_file_cxt(mrb_state *mrb, FILE* fp, mrbc_context *c)
 {
-  mrb_irep *irep = mrb_read_irep_file(mrb, fp);
-  mrb_value val;
-  struct RProc *proc;
-
-  if (!irep) {
-    irep_error(mrb);
-    return mrb_nil_value();
-  }
-  proc = mrb_proc_new(mrb, irep);
-  mrb_irep_decref(mrb, irep);
-  if (c && c->dump_result) mrb_codedump_all(mrb, proc);
-  if (c && c->no_exec) return mrb_obj_value(proc);
-  val = mrb_top_run(mrb, proc, mrb_top_self(mrb), 0);
-  return val;
+  return load_irep(mrb, mrb_read_irep_file(mrb, fp), c);
 }
 
 MRB_API mrb_value
index 4558493..bb3d7b6 100644 (file)
@@ -5,6 +5,7 @@ MRuby.each_target do
 
   objs = Dir.glob("#{current_dir}/*.c").map { |f|
     next nil if cxx_exception_enabled? and f =~ /(error|vm).c$/
+    next nil if self.cc.defines.flatten.include?("MRB_WITHOUT_FLOAT") and f =~ /fmt_fp.c$/
     objfile(f.pathmap("#{current_build_dir}/%n"))
   }.compact
 
index be71f11..b898fd0 100644 (file)
@@ -4,9 +4,11 @@
 ** See Copyright Notice in mruby.h
 */
 
+#ifndef MRB_WITHOUT_FLOAT
 #include <float.h>
-#include <limits.h>
 #include <math.h>
+#endif
+#include <limits.h>
 #include <stdlib.h>
 
 #include <mruby.h>
@@ -15,6 +17,7 @@
 #include <mruby/string.h>
 #include <mruby/class.h>
 
+#ifndef MRB_WITHOUT_FLOAT
 #ifdef MRB_USE_FLOAT
 #define trunc(f) truncf(f)
 #define floor(f) floorf(f)
@@ -24,7 +27,9 @@
 #else
 #define MRB_FLO_TO_STR_FMT "%.14g"
 #endif
+#endif
 
+#ifndef MRB_WITHOUT_FLOAT
 MRB_API mrb_float
 mrb_to_flo(mrb_state *mrb, mrb_value val)
 {
@@ -38,6 +43,7 @@ mrb_to_flo(mrb_state *mrb, mrb_value val)
   }
   return mrb_float(val);
 }
+#endif
 
 /*
  * call-seq:
@@ -52,7 +58,9 @@ static mrb_value
 num_pow(mrb_state *mrb, mrb_value x)
 {
   mrb_value y;
+#ifndef MRB_WITHOUT_FLOAT
   mrb_float d;
+#endif
 
   mrb_get_args(mrb, "o", &y);
   if (mrb_fixnum_p(x) && mrb_fixnum_p(y)) {
@@ -61,24 +69,37 @@ num_pow(mrb_state *mrb, mrb_value x)
     mrb_int exp = mrb_fixnum(y);
     mrb_int result = 1;
 
-    if (exp < 0) goto float_pow;
+    if (exp < 0)
+#ifdef MRB_WITHOUT_FLOAT
+      return mrb_fixnum_value(0);
+#else
+      goto float_pow;
+#endif
     for (;;) {
       if (exp & 1) {
         if (mrb_int_mul_overflow(result, base, &result)) {
+#ifndef MRB_WITHOUT_FLOAT
           goto float_pow;
+#endif
         }
       }
       exp >>= 1;
       if (exp == 0) break;
       if (mrb_int_mul_overflow(base, base, &base)) {
+#ifndef MRB_WITHOUT_FLOAT
         goto float_pow;
+#endif
       }
     }
     return mrb_fixnum_value(result);
   }
+#ifdef MRB_WITHOUT_FLOAT
+  mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
+#else
  float_pow:
   d = pow(mrb_to_flo(mrb, x), mrb_to_flo(mrb, y));
   return mrb_float_value(mrb, d);
+#endif
 }
 
 /* 15.2.8.3.4  */
@@ -95,7 +116,14 @@ num_pow(mrb_state *mrb, mrb_value x)
 mrb_value
 mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y)
 {
+#ifdef MRB_WITHOUT_FLOAT
+  if (!mrb_fixnum_p(y)) {
+    mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
+  }
+  return mrb_fixnum_value(mrb_fixnum(x) / mrb_fixnum(y));
+#else
   return mrb_float_value(mrb, mrb_to_flo(mrb, x) / mrb_to_flo(mrb, y));
+#endif
 }
 
 /* 15.2.9.3.19(x) */
@@ -109,12 +137,23 @@ mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y)
 static mrb_value
 num_div(mrb_state *mrb, mrb_value x)
 {
+#ifdef MRB_WITHOUT_FLOAT
+  mrb_value y;
+
+  mrb_get_args(mrb, "o", &y);
+  if (!mrb_fixnum_p(y)) {
+    mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
+  }
+  return mrb_fixnum_value(mrb_fixnum(x) / mrb_fixnum(y));
+#else
   mrb_float y;
 
   mrb_get_args(mrb, "f", &y);
   return mrb_float_value(mrb, mrb_to_flo(mrb, x) / y);
+#endif
 }
 
+#ifndef MRB_WITHOUT_FLOAT
 /********************************************************************
  *
  * Document-class: Float
@@ -187,12 +226,9 @@ flodivmod(mrb_state *mrb, mrb_float x, mrb_float y, mrb_float *divp, mrb_float *
   mrb_float mod;
 
   if (y == 0.0) {
-    if (x == 0.0) {
-      div = NAN;
-    }
-    else {
-      div = INFINITY;
-    }
+    if (x > 0.0) div = INFINITY;
+    else if (x < 0.0) div = -INFINITY;
+    else div = NAN;             /* x == 0.0 */
     mod = NAN;
   }
   else {
@@ -234,6 +270,7 @@ flo_mod(mrb_state *mrb, mrb_value x)
   flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), 0, &mod);
   return mrb_float_value(mrb, mod);
 }
+#endif
 
 /* 15.2.8.3.16 */
 /*
@@ -257,6 +294,7 @@ fix_eql(mrb_state *mrb, mrb_value x)
   return mrb_bool_value(mrb_fixnum(x) == mrb_fixnum(y));
 }
 
+#ifndef MRB_WITHOUT_FLOAT
 static mrb_value
 flo_eql(mrb_state *mrb, mrb_value x)
 {
@@ -383,7 +421,11 @@ flo_shift(mrb_state *mrb, mrb_value x, mrb_int width)
 #if defined(_ISOC99_SOURCE)
     val = trunc(val);
 #else
-    val = val > 0 ? floor(val) : ceil(val);
+    if (val > 0){
+        val = floor(val);    
+    } else {
+        val = ceil(val);
+    }
 #endif
     if (val == 0 && mrb_float(x) < 0) {
       return mrb_fixnum_value(-1);
@@ -418,32 +460,6 @@ flo_rshift(mrb_state *mrb, mrb_value x)
   return flo_shift(mrb, x, width);
 }
 
-/* 15.2.8.3.18 */
-/*
- * call-seq:
- *   flt.hash  ->  integer
- *
- * Returns a hash code for this float.
- */
-static mrb_value
-flo_hash(mrb_state *mrb, mrb_value num)
-{
-  mrb_float d;
-  char *c;
-  size_t i;
-  mrb_int hash;
-
-  d = (mrb_float)mrb_fixnum(num);
-  /* normalize -0.0 to 0.0 */
-  if (d == 0) d = 0.0;
-  c = (char*)&d;
-  for (hash=0,i=0; i<sizeof(mrb_float); i++) {
-    hash = (hash * 971) ^ (unsigned char)c[i];
-  }
-  if (hash < 0) hash = -hash;
-  return mrb_fixnum_value(hash);
-}
-
 /* 15.2.9.3.13 */
 /*
  * call-seq:
@@ -673,6 +689,7 @@ flo_nan_p(mrb_state *mrb, mrb_value num)
 {
   return mrb_bool_value(isnan(mrb_float(num)));
 }
+#endif
 
 /*
  * Document-class: Integer
@@ -710,11 +727,17 @@ mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
     if (a == 0) return x;
     b = mrb_fixnum(y);
     if (mrb_int_mul_overflow(a, b, &c)) {
+#ifndef MRB_WITHOUT_FLOAT
       return mrb_float_value(mrb, (mrb_float)a * (mrb_float)b);
+#endif
     }
     return mrb_fixnum_value(c);
   }
+#ifdef MRB_WITHOUT_FLOAT
+  mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
+#else
   return mrb_float_value(mrb, (mrb_float)a * mrb_to_flo(mrb, y));
+#endif
 }
 
 /* 15.2.8.3.3  */
@@ -786,17 +809,26 @@ fix_mod(mrb_state *mrb, mrb_value x)
     mrb_int b, mod;
 
     if ((b=mrb_fixnum(y)) == 0) {
+#ifdef MRB_WITHOUT_FLOAT
+      /* ZeroDivisionError */
+      return mrb_fixnum_value(0);
+#else
       return mrb_float_value(mrb, NAN);
+#endif
     }
     fixdivmod(mrb, a, b, 0, &mod);
     return mrb_fixnum_value(mod);
   }
+#ifdef MRB_WITHOUT_FLOAT
+  mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
+#else
   else {
     mrb_float mod;
 
     flodivmod(mrb, (mrb_float)a, mrb_to_flo(mrb, y), 0, &mod);
     return mrb_float_value(mrb, mod);
   }
+#endif
 }
 
 /*
@@ -816,14 +848,21 @@ fix_divmod(mrb_state *mrb, mrb_value x)
     mrb_int div, mod;
 
     if (mrb_fixnum(y) == 0) {
+#ifdef MRB_WITHOUT_FLOAT
+      return mrb_assoc_new(mrb, mrb_fixnum_value(0), mrb_fixnum_value(0));
+#else
       return mrb_assoc_new(mrb, ((mrb_fixnum(x) == 0) ?
                                  mrb_float_value(mrb, NAN):
                                  mrb_float_value(mrb, INFINITY)),
                            mrb_float_value(mrb, NAN));
+#endif
     }
     fixdivmod(mrb, mrb_fixnum(x), mrb_fixnum(y), &div, &mod);
     return mrb_assoc_new(mrb, mrb_fixnum_value(div), mrb_fixnum_value(mod));
   }
+#ifdef MRB_WITHOUT_FLOAT
+  mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
+#else
   else {
     mrb_float div, mod;
     mrb_value a, b;
@@ -833,8 +872,10 @@ fix_divmod(mrb_state *mrb, mrb_value x)
     b = mrb_float_value(mrb, mod);
     return mrb_assoc_new(mrb, a, b);
   }
+#endif
 }
 
+#ifndef MRB_WITHOUT_FLOAT
 static mrb_value
 flo_divmod(mrb_state *mrb, mrb_value x)
 {
@@ -849,6 +890,7 @@ flo_divmod(mrb_state *mrb, mrb_value x)
   b = mrb_float_value(mrb, mod);
   return mrb_assoc_new(mrb, a, b);
 }
+#endif
 
 /* 15.2.8.3.7  */
 /*
@@ -871,8 +913,10 @@ fix_equal(mrb_state *mrb, mrb_value x)
   switch (mrb_type(y)) {
   case MRB_TT_FIXNUM:
     return mrb_bool_value(mrb_fixnum(x) == mrb_fixnum(y));
+#ifndef MRB_WITHOUT_FLOAT
   case MRB_TT_FLOAT:
     return mrb_bool_value((mrb_float)mrb_fixnum(x) == mrb_float(y));
+#endif
   default:
     return mrb_false_value();
   }
@@ -897,13 +941,19 @@ fix_rev(mrb_state *mrb, mrb_value num)
   return mrb_fixnum_value(~val);
 }
 
+#ifdef MRB_WITHOUT_FLOAT
+#define bit_op(x,y,op1,op2) do {\
+  return mrb_fixnum_value(mrb_fixnum(x) op2 mrb_fixnum(y));\
+} while(0)
+#else
 static mrb_value flo_and(mrb_state *mrb, mrb_value x);
 static mrb_value flo_or(mrb_state *mrb, mrb_value x);
 static mrb_value flo_xor(mrb_state *mrb, mrb_value x);
 #define bit_op(x,y,op1,op2) do {\
   if (mrb_fixnum_p(y)) return mrb_fixnum_value(mrb_fixnum(x) op2 mrb_fixnum(y));\
-  return flo_ ## op1(mrb, mrb_float_value(mrb, mrb_fixnum(x)));\
+  return flo_ ## op1(mrb, mrb_float_value(mrb, (mrb_float)mrb_fixnum(x)));\
 } while(0)
+#endif
 
 /* 15.2.8.3.9  */
 /*
@@ -962,23 +1012,36 @@ static mrb_value
 lshift(mrb_state *mrb, mrb_int val, mrb_int width)
 {
   if (width < 0) {              /* mrb_int overflow */
+#ifdef MRB_WITHOUT_FLOAT
+    return mrb_fixnum_value(0);
+#else
     return mrb_float_value(mrb, INFINITY);
+#endif
   }
   if (val > 0) {
     if ((width > NUMERIC_SHIFT_WIDTH_MAX) ||
         (val   > (MRB_INT_MAX >> width))) {
+#ifdef MRB_WITHOUT_FLOAT
+      return mrb_fixnum_value(-1);
+#else
       goto bit_overflow;
+#endif
     }
     return mrb_fixnum_value(val << width);
   }
   else {
     if ((width > NUMERIC_SHIFT_WIDTH_MAX) ||
         (val   < (MRB_INT_MIN >> width))) {
+#ifdef MRB_WITHOUT_FLOAT
+      return mrb_fixnum_value(0);
+#else
       goto bit_overflow;
+#endif
     }
-    return mrb_fixnum_value(val * (1u << width));
+    return mrb_fixnum_value(val * ((mrb_int)1 << width));
   }
 
+#ifndef MRB_WITHOUT_FLOAT
 bit_overflow:
   {
     mrb_float f = (mrb_float)val;
@@ -987,6 +1050,7 @@ bit_overflow:
     }
     return mrb_float_value(mrb, f);
   }
+#endif
 }
 
 static mrb_value
@@ -1063,6 +1127,7 @@ fix_rshift(mrb_state *mrb, mrb_value x)
  *
  */
 
+#ifndef MRB_WITHOUT_FLOAT
 static mrb_value
 fix_to_f(mrb_state *mrb, mrb_value num)
 {
@@ -1110,6 +1175,7 @@ mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x)
   }
   return mrb_fixnum_value(z);
 }
+#endif
 
 mrb_value
 mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y)
@@ -1123,11 +1189,17 @@ mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y)
     if (a == 0) return y;
     b = mrb_fixnum(y);
     if (mrb_int_add_overflow(a, b, &c)) {
+#ifndef MRB_WITHOUT_FLOAT
       return mrb_float_value(mrb, (mrb_float)a + (mrb_float)b);
+#endif
     }
     return mrb_fixnum_value(c);
   }
+#ifdef MRB_WITHOUT_FLOAT
+  mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
+#else
   return mrb_float_value(mrb, (mrb_float)a + mrb_to_flo(mrb, y));
+#endif
 }
 
 /* 15.2.8.3.1  */
@@ -1159,11 +1231,17 @@ mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y)
 
     b = mrb_fixnum(y);
     if (mrb_int_sub_overflow(a, b, &c)) {
+#ifndef MRB_WITHOUT_FLOAT
       return mrb_float_value(mrb, (mrb_float)a - (mrb_float)b);
+#endif
     }
     return mrb_fixnum_value(c);
   }
+#ifdef MRB_WITHOUT_FLOAT
+  mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
+#else
   return mrb_float_value(mrb, (mrb_float)a - mrb_to_flo(mrb, y));
+#endif
 }
 
 /* 15.2.8.3.2  */
@@ -1187,7 +1265,7 @@ fix_minus(mrb_state *mrb, mrb_value self)
 
 
 MRB_API mrb_value
-mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, int base)
+mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, mrb_int base)
 {
   char buf[MRB_INT_BIT+1];
   char *b = buf + sizeof buf;
@@ -1240,6 +1318,46 @@ fix_to_s(mrb_state *mrb, mrb_value self)
   return mrb_fixnum_to_str(mrb, self, base);
 }
 
+/* compare two numbers: (1:0:-1; -2 for error) */
+static mrb_int
+cmpnum(mrb_state *mrb, mrb_value v1, mrb_value v2)
+{
+#ifdef MRB_WITHOUT_FLOAT
+  mrb_int x, y;
+#else
+  mrb_float x, y;
+#endif
+
+#ifdef MRB_WITHOUT_FLOAT
+  x = mrb_fixnum(v1);
+#else
+  x = mrb_to_flo(mrb, v1);
+#endif
+  switch (mrb_type(v2)) {
+  case MRB_TT_FIXNUM:
+#ifdef MRB_WITHOUT_FLOAT
+    y = mrb_fixnum(v2);
+#else
+    y = (mrb_float)mrb_fixnum(v2);
+#endif
+    break;
+#ifndef MRB_WITHOUT_FLOAT
+  case MRB_TT_FLOAT:
+    y = mrb_float(v2);
+    break;
+#endif
+  default:
+    return -2;
+  }
+  if (x > y)
+    return 1;
+  else {
+    if (x < y)
+      return -1;
+    return 0;
+  }
+}
+
 /* 15.2.9.3.6  */
 /*
  * call-seq:
@@ -1255,28 +1373,86 @@ static mrb_value
 num_cmp(mrb_state *mrb, mrb_value self)
 {
   mrb_value other;
-  mrb_float x, y;
+  mrb_int n;
 
   mrb_get_args(mrb, "o", &other);
+  n = cmpnum(mrb, self, other);
+  if (n == -2) return mrb_nil_value();
+  return mrb_fixnum_value(n);
+}
 
-  x = mrb_to_flo(mrb, self);
-  switch (mrb_type(other)) {
-  case MRB_TT_FIXNUM:
-    y = (mrb_float)mrb_fixnum(other);
-    break;
-  case MRB_TT_FLOAT:
-    y = mrb_float(other);
-    break;
-  default:
-    return mrb_nil_value();
-  }
-  if (x > y)
-    return mrb_fixnum_value(1);
-  else {
-    if (x < y)
-      return mrb_fixnum_value(-1);
-    return mrb_fixnum_value(0);
-  }
+static void
+cmperr(mrb_state *mrb, mrb_value v1, mrb_value v2)
+{
+  mrb_raisef(mrb, E_ARGUMENT_ERROR, "comparison of %S with %S failed",
+             mrb_obj_value(mrb_class(mrb, v1)),
+             mrb_obj_value(mrb_class(mrb, v2)));
+}
+
+static mrb_value
+num_lt(mrb_state *mrb, mrb_value self)
+{
+  mrb_value other;
+  mrb_int n;
+
+  mrb_get_args(mrb, "o", &other);
+  n = cmpnum(mrb, self, other);
+  if (n == -2) cmperr(mrb, self, other);
+  if (n < 0) return mrb_true_value();
+  return mrb_false_value();
+}
+
+static mrb_value
+num_le(mrb_state *mrb, mrb_value self)
+{
+  mrb_value other;
+  mrb_int n;
+
+  mrb_get_args(mrb, "o", &other);
+  n = cmpnum(mrb, self, other);
+  if (n == -2) cmperr(mrb, self, other);
+  if (n <= 0) return mrb_true_value();
+  return mrb_false_value();
+}
+
+static mrb_value
+num_gt(mrb_state *mrb, mrb_value self)
+{
+  mrb_value other;
+  mrb_int n;
+
+  mrb_get_args(mrb, "o", &other);
+  n = cmpnum(mrb, self, other);
+  if (n == -2) cmperr(mrb, self, other);
+  if (n > 0) return mrb_true_value();
+  return mrb_false_value();
+}
+
+static mrb_value
+num_ge(mrb_state *mrb, mrb_value self)
+{
+  mrb_value other;
+  mrb_int n;
+
+  mrb_get_args(mrb, "o", &other);
+  n = cmpnum(mrb, self, other);
+  if (n == -2) cmperr(mrb, self, other);
+  if (n >= 0) return mrb_true_value();
+  return mrb_false_value();
+}
+
+static mrb_value
+num_finite_p(mrb_state *mrb, mrb_value self)
+{
+  mrb_get_args(mrb, "");
+  return mrb_true_value();
+}
+
+static mrb_value
+num_infinite_p(mrb_state *mrb, mrb_value self)
+{
+  mrb_get_args(mrb, "");
+  return mrb_false_value();
 }
 
 /* 15.2.9.3.1  */
@@ -1287,6 +1463,7 @@ num_cmp(mrb_state *mrb, mrb_value self)
  * Returns a new float which is the sum of <code>float</code>
  * and <code>other</code>.
  */
+#ifndef MRB_WITHOUT_FLOAT
 static mrb_value
 flo_plus(mrb_state *mrb, mrb_value x)
 {
@@ -1295,12 +1472,16 @@ flo_plus(mrb_state *mrb, mrb_value x)
   mrb_get_args(mrb, "o", &y);
   return mrb_float_value(mrb, mrb_float(x) + mrb_to_flo(mrb, y));
 }
+#endif
 
 /* ------------------------------------------------------------------------*/
 void
 mrb_init_numeric(mrb_state *mrb)
 {
-  struct RClass *numeric, *integer, *fixnum, *fl;
+  struct RClass *numeric, *integer, *fixnum;
+#ifndef MRB_WITHOUT_FLOAT
+  struct RClass *fl;
+#endif
 
   /* Numeric Class */
   numeric = mrb_define_class(mrb, "Numeric",  mrb->object_class);                /* 15.2.7 */
@@ -1309,6 +1490,12 @@ mrb_init_numeric(mrb_state *mrb)
   mrb_define_method(mrb, numeric, "/",        num_div,         MRB_ARGS_REQ(1)); /* 15.2.8.3.4  */
   mrb_define_method(mrb, numeric, "quo",      num_div,         MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */
   mrb_define_method(mrb, numeric, "<=>",      num_cmp,         MRB_ARGS_REQ(1)); /* 15.2.9.3.6  */
+  mrb_define_method(mrb, numeric, "<",        num_lt,          MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, numeric, "<=",       num_le,          MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, numeric, ">",        num_gt,          MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, numeric, ">=",       num_ge,          MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, numeric, "finite?",  num_finite_p,    MRB_ARGS_NONE());
+  mrb_define_method(mrb, numeric, "infinite?",num_infinite_p,  MRB_ARGS_NONE());
 
   /* Integer Class */
   integer = mrb_define_class(mrb, "Integer",  numeric);                          /* 15.2.8 */
@@ -1316,10 +1503,12 @@ mrb_init_numeric(mrb_state *mrb)
   mrb_undef_class_method(mrb, integer, "new");
   mrb_define_method(mrb, integer, "to_i",     int_to_i,        MRB_ARGS_NONE()); /* 15.2.8.3.24 */
   mrb_define_method(mrb, integer, "to_int",   int_to_i,        MRB_ARGS_NONE());
+#ifndef MRB_WITHOUT_FLOAT
   mrb_define_method(mrb, integer, "ceil",     int_to_i,        MRB_ARGS_REQ(1)); /* 15.2.8.3.8 (x) */
   mrb_define_method(mrb, integer, "floor",    int_to_i,        MRB_ARGS_REQ(1)); /* 15.2.8.3.10 (x) */
   mrb_define_method(mrb, integer, "round",    int_to_i,        MRB_ARGS_REQ(1)); /* 15.2.8.3.12 (x) */
   mrb_define_method(mrb, integer, "truncate", int_to_i,        MRB_ARGS_REQ(1)); /* 15.2.8.3.15 (x) */
+#endif
 
   /* Fixnum Class */
   mrb->fixnum_class = fixnum = mrb_define_class(mrb, "Fixnum", integer);
@@ -1335,12 +1524,14 @@ mrb_init_numeric(mrb_state *mrb)
   mrb_define_method(mrb, fixnum,  "<<",       fix_lshift,      MRB_ARGS_REQ(1)); /* 15.2.8.3.12 */
   mrb_define_method(mrb, fixnum,  ">>",       fix_rshift,      MRB_ARGS_REQ(1)); /* 15.2.8.3.13 */
   mrb_define_method(mrb, fixnum,  "eql?",     fix_eql,         MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */
-  mrb_define_method(mrb, fixnum,  "hash",     flo_hash,        MRB_ARGS_NONE()); /* 15.2.8.3.18 */
+#ifndef MRB_WITHOUT_FLOAT
   mrb_define_method(mrb, fixnum,  "to_f",     fix_to_f,        MRB_ARGS_NONE()); /* 15.2.8.3.23 */
+#endif
   mrb_define_method(mrb, fixnum,  "to_s",     fix_to_s,        MRB_ARGS_NONE()); /* 15.2.8.3.25 */
   mrb_define_method(mrb, fixnum,  "inspect",  fix_to_s,        MRB_ARGS_NONE());
   mrb_define_method(mrb, fixnum,  "divmod",   fix_divmod,      MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */
 
+#ifndef MRB_WITHOUT_FLOAT
   /* Float Class */
   mrb->float_class = fl = mrb_define_class(mrb, "Float", numeric);                 /* 15.2.9 */
   MRB_SET_INSTANCE_TT(fl, MRB_TT_FLOAT);
@@ -1378,4 +1569,6 @@ mrb_init_numeric(mrb_state *mrb)
 #ifdef NAN
   mrb_define_const(mrb, fl, "NAN", mrb_float_value(mrb, NAN));
 #endif
+#endif
+  mrb_define_module(mrb, "Integral");
 }
index a78e9a4..8724c54 100644 (file)
@@ -24,8 +24,10 @@ mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2)
   case MRB_TT_SYMBOL:
     return (mrb_symbol(v1) == mrb_symbol(v2));
 
+#ifndef MRB_WITHOUT_FLOAT
   case MRB_TT_FLOAT:
     return (mrb_float(v1) == mrb_float(v2));
+#endif
 
   default:
     return (mrb_ptr(v1) == mrb_ptr(v2));
@@ -373,7 +375,9 @@ static const struct types {
   {MRB_TT_ICLASS, "iClass"},  /* internal use: mixed-in module holder */
   {MRB_TT_SCLASS, "SClass"},
   {MRB_TT_PROC,   "Proc"},
+#ifndef MRB_WITHOUT_FLOAT
   {MRB_TT_FLOAT,  "Float"},
+#endif
   {MRB_TT_ARRAY,  "Array"},
   {MRB_TT_HASH,   "Hash"},
   {MRB_TT_STRING, "String"},
@@ -438,7 +442,7 @@ mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t)
 MRB_API mrb_value
 mrb_any_to_s(mrb_state *mrb, mrb_value obj)
 {
-  mrb_value str = mrb_str_buf_new(mrb, 20);
+  mrb_value str = mrb_str_new_capa(mrb, 20);
   const char *cname = mrb_obj_classname(mrb, obj);
 
   mrb_str_cat_lit(mrb, str, "#<");
@@ -523,7 +527,7 @@ mrb_to_int(mrb_state *mrb, mrb_value val)
 }
 
 MRB_API mrb_value
-mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base)
+mrb_convert_to_integer(mrb_state *mrb, mrb_value val, mrb_int base)
 {
   mrb_value tmp;
 
@@ -532,6 +536,7 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base)
       mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Integer");
   }
   switch (mrb_type(val)) {
+#ifndef MRB_WITHOUT_FLOAT
     case MRB_TT_FLOAT:
       if (base != 0) goto arg_error;
       else {
@@ -541,6 +546,7 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base)
         }
       }
       return mrb_flo_to_fixnum(mrb, val);
+#endif
 
     case MRB_TT_FIXNUM:
       if (base != 0) goto arg_error;
@@ -563,8 +569,8 @@ arg_error:
     mrb_raise(mrb, E_ARGUMENT_ERROR, "base specified for non string value");
   }
   tmp = convert_type(mrb, val, "Integer", "to_int", FALSE);
-  if (mrb_nil_p(tmp)) {
-    return mrb_to_integer(mrb, val, "to_i");
+  if (mrb_nil_p(tmp) || !mrb_fixnum_p(tmp)) {
+    tmp = mrb_to_integer(mrb, val, "to_i");
   }
   return tmp;
 }
@@ -575,6 +581,7 @@ mrb_Integer(mrb_state *mrb, mrb_value val)
   return mrb_convert_to_integer(mrb, val, 0);
 }
 
+#ifndef MRB_WITHOUT_FLOAT
 MRB_API mrb_value
 mrb_Float(mrb_state *mrb, mrb_value val)
 {
@@ -595,6 +602,7 @@ mrb_Float(mrb_state *mrb, mrb_value val)
       return mrb_convert_type(mrb, val, MRB_TT_FLOAT, "Float", "to_f");
   }
 }
+#endif
 
 MRB_API mrb_value
 mrb_inspect(mrb_state *mrb, mrb_value obj)
index db4546a..b87d2cf 100644 (file)
 #endif
 /* end of configuration section */
 
+/* Disable MSVC warning "C4200: nonstandard extension used: zero-sized array
+ * in struct/union" when in C++ mode */
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4200)
+#endif
+
 struct mrb_pool_page {
   struct mrb_pool_page *next;
   size_t offset;
@@ -33,6 +40,10 @@ struct mrb_pool_page {
   char page[];
 };
 
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
 struct mrb_pool {
   mrb_state *mrb;
   struct mrb_pool_page *pages;
index ee4a493..c6e9be4 100644 (file)
@@ -13,88 +13,109 @@ static mrb_code call_iseq[] = {
   MKOP_A(OP_CALL, 0),
 };
 
-struct RProc *
+struct RProc*
 mrb_proc_new(mrb_state *mrb, mrb_irep *irep)
 {
   struct RProc *p;
   mrb_callinfo *ci = mrb->c->ci;
 
   p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
-  p->target_class = 0;
   if (ci) {
-    if (ci->proc)
-      p->target_class = ci->proc->target_class;
-    if (!p->target_class)
-      p->target_class = ci->target_class;
+    struct RClass *tc = NULL;
+
+    if (ci->proc) {
+      tc = MRB_PROC_TARGET_CLASS(ci->proc);
+    }
+    if (tc == NULL) {
+      tc = ci->target_class;
+    }
+    p->upper = ci->proc;
+    p->e.target_class = tc;
   }
   p->body.irep = irep;
-  p->env = 0;
   mrb_irep_incref(mrb, irep);
 
   return p;
 }
 
 static struct REnv*
-env_new(mrb_state *mrb, int nlocals)
+env_new(mrb_state *mrb, mrb_int nlocals)
 {
   struct REnv *e;
-
-  e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)mrb->c->ci->proc->env);
-  MRB_SET_ENV_STACK_LEN(e, nlocals);
-  e->cxt.c = mrb->c;
-  e->cioff = mrb->c->ci - mrb->c->cibase;
+  mrb_callinfo *ci = mrb->c->ci;
+  int bidx;
+
+  e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, NULL);
+  MRB_ENV_SET_STACK_LEN(e, nlocals);
+  bidx = ci->argc;
+  if (ci->argc < 0) bidx = 2;
+  else bidx += 1;
+  MRB_ENV_SET_BIDX(e, bidx);
+  e->mid = ci->mid;
   e->stack = mrb->c->stack;
+  e->cxt = mrb->c;
 
   return e;
 }
 
 static void
-closure_setup(mrb_state *mrb, struct RProc *p, int nlocals)
+closure_setup(mrb_state *mrb, struct RProc *p)
 {
+  mrb_callinfo *ci = mrb->c->ci;
+  struct RProc *up = p->upper;
   struct REnv *e;
 
-  if (!mrb->c->ci->env) {
-    e = env_new(mrb, nlocals);
-    mrb->c->ci->env = e;
+  if (ci->env) {
+    e = ci->env;
   }
   else {
-    e = mrb->c->ci->env;
+    struct RClass *tc = MRB_PROC_TARGET_CLASS(p);
+
+    e = env_new(mrb, up->body.irep->nlocals);
+    ci->env = e;
+    if (tc) {
+      e->c = tc;
+      mrb_field_write_barrier(mrb, (struct RBasic*)e, (struct RBasic*)tc);
+    }
   }
-  p->env = e;
-  mrb_field_write_barrier(mrb, (struct RBasic *)p, (struct RBasic *)p->env);
+  p->e.env = e;
+  p->flags |= MRB_PROC_ENVSET;
+  mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e);
 }
 
-struct RProc *
+struct RProc*
 mrb_closure_new(mrb_state *mrb, mrb_irep *irep)
 {
   struct RProc *p = mrb_proc_new(mrb, irep);
 
-  closure_setup(mrb, p, mrb->c->ci->proc->body.irep->nlocals);
+  closure_setup(mrb, p);
   return p;
 }
 
-MRB_API struct RProc *
+MRB_API struct RProc*
 mrb_proc_new_cfunc(mrb_state *mrb, mrb_func_t func)
 {
   struct RProc *p;
 
   p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
   p->body.func = func;
-  p->flags |= MRB_PROC_CFUNC;
-  p->env = 0;
+  p->flags |= MRB_PROC_CFUNC_FL;
+  p->upper = 0;
+  p->e.target_class = 0;
 
   return p;
 }
 
-MRB_API struct RProc *
+MRB_API struct RProc*
 mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const mrb_value *argv)
 {
   struct RProc *p = mrb_proc_new_cfunc(mrb, func);
   struct REnv *e;
   int i;
 
-  p->env = e = env_new(mrb, argc);
-  mrb_field_write_barrier(mrb, (struct RBasic *)p, (struct RBasic *)p->env);
+  p->e.env = e = env_new(mrb, argc);
+  p->flags |= MRB_PROC_ENVSET;
+  mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e);
   MRB_ENV_UNSHARE_STACK(e);
   e->stack = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * argc);
   if (argv) {
@@ -110,7 +131,7 @@ mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const
   return p;
 }
 
-MRB_API struct RProc *
+MRB_API struct RProc*
 mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals)
 {
   return mrb_proc_new_cfunc_with_env(mrb, func, nlocals, NULL);
@@ -120,11 +141,12 @@ MRB_API mrb_value
 mrb_proc_cfunc_env_get(mrb_state *mrb, mrb_int idx)
 {
   struct RProc *p = mrb->c->ci->proc;
-  struct REnv *e = p->env;
+  struct REnv *e;
 
-  if (!MRB_PROC_CFUNC_P(p)) {
+  if (!p || !MRB_PROC_CFUNC_P(p)) {
     mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from non-cfunc proc.");
   }
+  e = MRB_PROC_ENV(p);
   if (!e) {
     mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from cfunc Proc without REnv.");
   }
@@ -139,13 +161,18 @@ mrb_proc_cfunc_env_get(mrb_state *mrb, mrb_int idx)
 void
 mrb_proc_copy(struct RProc *a, struct RProc *b)
 {
+  if (a->body.irep) {
+    /* already initialized proc */
+    return;
+  }
   a->flags = b->flags;
   a->body = b->body;
   if (!MRB_PROC_CFUNC_P(a) && a->body.irep) {
     a->body.irep->refcnt++;
   }
-  a->target_class = b->target_class;
-  a->env = b->env;
+  a->upper = b->upper;
+  a->e.env = b->e.env;
+  /* a->e.target_class = a->e.target_class; */
 }
 
 static mrb_value
@@ -163,7 +190,11 @@ mrb_proc_s_new(mrb_state *mrb, mrb_value proc_class)
   p = (struct RProc *)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb_class_ptr(proc_class));
   mrb_proc_copy(p, mrb_proc_ptr(blk));
   proc = mrb_obj_value(p);
-  mrb_funcall_with_block(mrb, proc, mrb_intern_lit(mrb, "initialize"), 0, NULL, blk);
+  mrb_funcall_with_block(mrb, proc, mrb_intern_lit(mrb, "initialize"), 0, NULL, proc);
+  if (!MRB_PROC_STRICT_P(p) &&
+      mrb->c->ci > mrb->c->cibase && MRB_PROC_ENV(p) == mrb->c->ci[-1].env) {
+    p->flags |= MRB_PROC_ORPHAN;
+  }
   return proc;
 }
 
@@ -186,12 +217,6 @@ mrb_proc_cfunc_p(struct RProc *p)
   return MRB_PROC_CFUNC_P(p);
 }
 
-mrb_value
-mrb_proc_call_cfunc(mrb_state *mrb, struct RProc *p, mrb_value self)
-{
-  return (p->body.func)(mrb, self);
-}
-
 /* 15.2.17.4.2 */
 static mrb_value
 mrb_proc_arity(mrb_state *mrb, mrb_value self)
@@ -263,7 +288,8 @@ proc_lambda(mrb_state *mrb, mrb_value self)
 void
 mrb_init_proc(mrb_state *mrb)
 {
-  struct RProc *m;
+  struct RProc *p;
+  mrb_method_t m;
   mrb_irep *call_irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep));
   static const mrb_irep mrb_irep_zero = { 0 };
 
@@ -277,7 +303,8 @@ mrb_init_proc(mrb_state *mrb)
   mrb_define_method(mrb, mrb->proc_class, "initialize_copy", mrb_proc_init_copy, MRB_ARGS_REQ(1));
   mrb_define_method(mrb, mrb->proc_class, "arity", mrb_proc_arity, MRB_ARGS_NONE());
 
-  m = mrb_proc_new(mrb, call_irep);
+  p = mrb_proc_new(mrb, call_irep);
+  MRB_METHOD_FROM_PROC(m, p);
   mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "call"), m);
   mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "[]"), m);
 
index 2cb6f23..0360b48 100644 (file)
@@ -10,8 +10,6 @@
 #include <mruby/string.h>
 #include <mruby/array.h>
 
-#define RANGE_CLASS (mrb_class_get(mrb, "Range"))
-
 MRB_API struct RRange*
 mrb_range_ptr(mrb_state *mrb, mrb_value v)
 {
@@ -32,8 +30,12 @@ range_check(mrb_state *mrb, mrb_value a, mrb_value b)
 
   ta = mrb_type(a);
   tb = mrb_type(b);
+#ifdef MRB_WITHOUT_FLOAT
+  if (ta == MRB_TT_FIXNUM && tb == MRB_TT_FIXNUM ) {
+#else
   if ((ta == MRB_TT_FIXNUM || ta == MRB_TT_FLOAT) &&
       (tb == MRB_TT_FIXNUM || tb == MRB_TT_FLOAT)) {
+#endif
     return;
   }
 
@@ -50,7 +52,7 @@ mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
   struct RRange *r;
 
   range_check(mrb, beg, end);
-  r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, RANGE_CLASS);
+  r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, mrb->range_class);
   r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges));
   r->edges->beg = beg;
   r->edges->end = end;
@@ -133,7 +135,7 @@ mrb_range_initialize(mrb_state *mrb, mrb_value range)
 {
   mrb_value beg, end;
   mrb_bool exclusive;
-  int n;
+  mrb_int n;
 
   n = mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive);
   if (n != 3) {
@@ -353,7 +355,7 @@ range_eql(mrb_state *mrb, mrb_value range)
   mrb_get_args(mrb, "o", &obj);
 
   if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
-  if (!mrb_obj_is_kind_of(mrb, obj, RANGE_CLASS)) {
+  if (!mrb_obj_is_kind_of(mrb, obj, mrb->range_class)) {
     return mrb_false_value();
   }
   if (mrb_type(obj) != MRB_TT_RANGE) return mrb_false_value();
@@ -423,6 +425,7 @@ mrb_init_range(mrb_state *mrb)
   struct RClass *r;
 
   r = mrb_define_class(mrb, "Range", mrb->object_class);                                /* 15.2.14 */
+  mrb->range_class = r;
   MRB_SET_INSTANCE_TT(r, MRB_TT_RANGE);
 
   mrb_define_method(mrb, r, "begin",           mrb_range_beg,         MRB_ARGS_NONE()); /* 15.2.14.4.3  */
index 0be5a18..18d1045 100644 (file)
@@ -11,6 +11,7 @@
 #include <mruby/variable.h>
 #include <mruby/debug.h>
 #include <mruby/string.h>
+#include <mruby/class.h>
 
 void mrb_init_core(mrb_state*);
 void mrb_init_mrbgems(mrb_state*);
@@ -18,12 +19,6 @@ void mrb_init_mrbgems(mrb_state*);
 void mrb_gc_init(mrb_state*, mrb_gc *gc);
 void mrb_gc_destroy(mrb_state*, mrb_gc *gc);
 
-static mrb_value
-inspect_main(mrb_state *mrb, mrb_value mod)
-{
-  return mrb_str_new_lit(mrb, "main");
-}
-
 MRB_API mrb_state*
 mrb_open_core(mrb_allocf f, void *ud)
 {
@@ -63,7 +58,7 @@ mrb_default_allocf(mrb_state *mrb, void *p, size_t size, void *ud)
 
 struct alloca_header {
   struct alloca_header *next;
-  char buf[];
+  char buf[1];
 };
 
 MRB_API void*
@@ -135,9 +130,22 @@ mrb_irep_decref(mrb_state *mrb, mrb_irep *irep)
 }
 
 void
+mrb_irep_cutref(mrb_state *mrb, mrb_irep *irep)
+{
+  mrb_irep *tmp;
+  int i;
+
+  for (i=0; i<irep->rlen; i++) {
+    tmp = irep->reps[i];
+    irep->reps[i] = NULL;
+    if (tmp) mrb_irep_decref(mrb, tmp);
+  }
+}
+
+void
 mrb_irep_free(mrb_state *mrb, mrb_irep *irep)
 {
-  size_t i;
+  int i;
 
   if (!(irep->flags & MRB_ISEQ_NO_FREE))
     mrb_free(mrb, irep->iseq);
@@ -146,7 +154,7 @@ mrb_irep_free(mrb_state *mrb, mrb_irep *irep)
       mrb_gc_free_str(mrb, RSTRING(irep->pool[i]));
       mrb_free(mrb, mrb_obj_ptr(irep->pool[i]));
     }
-#ifdef MRB_WORD_BOXING
+#if defined(MRB_WORD_BOXING) && !defined(MRB_WITHOUT_FLOAT)
     else if (mrb_type(irep->pool[i]) == MRB_TT_FLOAT) {
       mrb_free(mrb, mrb_obj_ptr(irep->pool[i]));
     }
@@ -155,7 +163,8 @@ mrb_irep_free(mrb_state *mrb, mrb_irep *irep)
   mrb_free(mrb, irep->pool);
   mrb_free(mrb, irep->syms);
   for (i=0; i<irep->rlen; i++) {
-    mrb_irep_decref(mrb, irep->reps[i]);
+    if (irep->reps[i])
+      mrb_irep_decref(mrb, irep->reps[i]);
   }
   mrb_free(mrb, irep->reps);
   mrb_free(mrb, irep->lv);
@@ -214,6 +223,8 @@ mrb_str_pool(mrb_state *mrb, mrb_value str)
       ns->as.heap.ptr[len] = '\0';
     }
   }
+  RSTR_SET_POOL_FLAG(ns);
+  MRB_SET_FROZEN_FLAG(ns);
   return mrb_obj_value(ns);
 }
 
@@ -270,11 +281,6 @@ mrb_add_irep(mrb_state *mrb)
 MRB_API mrb_value
 mrb_top_self(mrb_state *mrb)
 {
-  if (!mrb->top_self) {
-    mrb->top_self = (struct RObject*)mrb_obj_alloc(mrb, MRB_TT_OBJECT, mrb->object_class);
-    mrb_define_singleton_method(mrb, mrb->top_self, "inspect", inspect_main, MRB_ARGS_NONE());
-    mrb_define_singleton_method(mrb, mrb->top_self, "to_s", inspect_main, MRB_ARGS_NONE());
-  }
   return mrb_obj_value(mrb->top_self);
 }
 
index 2cc54a8..8ebd11b 100644 (file)
@@ -8,7 +8,9 @@
 # define _CRT_NONSTDC_NO_DEPRECATE
 #endif
 
+#ifndef MRB_WITHOUT_FLOAT
 #include <float.h>
+#endif
 #include <limits.h>
 #include <stddef.h>
 #include <stdlib.h>
@@ -40,7 +42,7 @@ str_new_static(mrb_state *mrb, const char *p, size_t len)
     mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
   }
   s = mrb_obj_alloc_string(mrb);
-  s->as.heap.len = len;
+  s->as.heap.len = (mrb_int)len;
   s->as.heap.aux.capa = 0;             /* nofree */
   s->as.heap.ptr = (char *)p;
   s->flags = MRB_STR_NOFREE;
@@ -57,7 +59,7 @@ str_new(mrb_state *mrb, const char *p, size_t len)
     return str_new_static(mrb, p, len);
   }
   s = mrb_obj_alloc_string(mrb);
-  if (len < RSTRING_EMBED_LEN_MAX) {
+  if (len <= RSTRING_EMBED_LEN_MAX) {
     RSTR_SET_EMBED_FLAG(s);
     RSTR_SET_EMBED_LEN(s, len);
     if (p) {
@@ -68,9 +70,9 @@ str_new(mrb_state *mrb, const char *p, size_t len)
     if (len >= MRB_INT_MAX) {
       mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
     }
-    s->as.heap.len = len;
-    s->as.heap.aux.capa = len;
     s->as.heap.ptr = (char *)mrb_malloc(mrb, len+1);
+    s->as.heap.len = (mrb_int)len;
+    s->as.heap.aux.capa = (mrb_int)len;
     if (p) {
       memcpy(s->as.heap.ptr, p, len);
     }
@@ -94,12 +96,8 @@ mrb_str_new_empty(mrb_state *mrb, mrb_value str)
   return mrb_obj_value(s);
 }
 
-#ifndef MRB_STR_BUF_MIN_SIZE
-# define MRB_STR_BUF_MIN_SIZE 128
-#endif
-
 MRB_API mrb_value
-mrb_str_buf_new(mrb_state *mrb, size_t capa)
+mrb_str_new_capa(mrb_state *mrb, size_t capa)
 {
   struct RString *s;
 
@@ -108,17 +106,27 @@ mrb_str_buf_new(mrb_state *mrb, size_t capa)
   if (capa >= MRB_INT_MAX) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "string capacity size too big");
   }
-  if (capa < MRB_STR_BUF_MIN_SIZE) {
-    capa = MRB_STR_BUF_MIN_SIZE;
-  }
   s->as.heap.len = 0;
-  s->as.heap.aux.capa = capa;
+  s->as.heap.aux.capa = (mrb_int)capa;
   s->as.heap.ptr = (char *)mrb_malloc(mrb, capa+1);
   RSTR_PTR(s)[0] = '\0';
 
   return mrb_obj_value(s);
 }
 
+#ifndef MRB_STR_BUF_MIN_SIZE
+# define MRB_STR_BUF_MIN_SIZE 128
+#endif
+
+MRB_API mrb_value
+mrb_str_buf_new(mrb_state *mrb, size_t capa)
+{
+  if (capa < MRB_STR_BUF_MIN_SIZE) {
+    capa = MRB_STR_BUF_MIN_SIZE;
+  }
+  return mrb_str_new_capa(mrb, capa);
+}
+
 static void
 resize_capa(mrb_state *mrb, struct RString *s, size_t capacity)
 {
@@ -142,51 +150,6 @@ resize_capa(mrb_state *mrb, struct RString *s, size_t capacity)
   }
 }
 
-static void
-str_buf_cat(mrb_state *mrb, struct RString *s, const char *ptr, size_t len)
-{
-  size_t capa;
-  size_t total;
-  ptrdiff_t off = -1;
-
-  if (len == 0) return;
-  mrb_str_modify(mrb, s);
-  if (ptr >= RSTR_PTR(s) && ptr <= RSTR_PTR(s) + (size_t)RSTR_LEN(s)) {
-      off = ptr - RSTR_PTR(s);
-  }
-
-  capa = RSTR_CAPA(s);
-  if (capa <= RSTRING_EMBED_LEN_MAX)
-    capa = RSTRING_EMBED_LEN_MAX+1;
-
-  total = RSTR_LEN(s)+len;
-  if (total >= MRB_INT_MAX) {
-  size_error:
-    mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
-  }
-  if (capa <= total) {
-    while (total > capa) {
-      if (capa <= MRB_INT_MAX / 2) {
-        capa *= 2;
-      }
-      else {
-        capa = total;
-      }
-    }
-    if (capa < total || capa > MRB_INT_MAX) {
-      goto size_error;
-    }
-    resize_capa(mrb, s, capa);
-  }
-  if (off != -1) {
-      ptr = RSTR_PTR(s) + off;
-  }
-  memcpy(RSTR_PTR(s) + RSTR_LEN(s), ptr, len);
-  mrb_assert_int_fit(size_t, total, mrb_int, MRB_INT_MAX);
-  RSTR_SET_LEN(s, total);
-  RSTR_PTR(s)[total] = '\0';   /* sentinel */
-}
-
 MRB_API mrb_value
 mrb_str_new(mrb_state *mrb, const char *p, size_t len)
 {
@@ -244,7 +207,7 @@ mrb_gc_free_str(mrb_state *mrb, struct RString *str)
     /* no code */;
   else if (RSTR_SHARED_P(str))
     str_decref(mrb, str->as.heap.aux.shared);
-  else if (!RSTR_NOFREE_P(str))
+  else if (!RSTR_NOFREE_P(str) && !RSTR_FSHARED_P(str))
     mrb_free(mrb, str->as.heap.ptr);
 }
 
@@ -341,7 +304,8 @@ mrb_memsearch_qs(const unsigned char *xs, mrb_int m, const unsigned char *ys, mr
 {
   const unsigned char *x = xs, *xe = xs + m;
   const unsigned char *y = ys;
-  int i, qstable[256];
+  int i;
+  ptrdiff_t qstable[256];
 
   /* Preprocessing */
   for (i = 0; i < 256; ++i)
@@ -351,7 +315,7 @@ mrb_memsearch_qs(const unsigned char *xs, mrb_int m, const unsigned char *ys, mr
   /* Searching */
   for (; y + m <= ys + n; y += *(qstable + y[m])) {
     if (*xs == *y && memcmp(xs, y, m) == 0)
-      return y - ys;
+      return (mrb_int)(y - ys);
   }
   return -1;
 }
@@ -372,7 +336,7 @@ mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n)
     const unsigned char *ys = (const unsigned char *)memchr(y, *x, n);
 
     if (ys)
-      return ys - y;
+      return (mrb_int)(ys - y);
     else
       return -1;
   }
@@ -380,40 +344,57 @@ mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n)
 }
 
 static void
-str_make_shared(mrb_state *mrb, struct RString *s)
+str_make_shared(mrb_state *mrb, struct RString *orig, struct RString *s)
 {
-  if (!RSTR_SHARED_P(s)) {
-    mrb_shared_string *shared = (mrb_shared_string *)mrb_malloc(mrb, sizeof(mrb_shared_string));
+  mrb_shared_string *shared;
+  mrb_int len = RSTR_LEN(orig);
 
-    shared->refcnt = 1;
-    if (RSTR_EMBED_P(s)) {
-      const mrb_int len = RSTR_EMBED_LEN(s);
-      char *const tmp = (char *)mrb_malloc(mrb, len+1);
-      memcpy(tmp, s->as.ary, len);
-      tmp[len] = '\0';
-      RSTR_UNSET_EMBED_FLAG(s);
-      s->as.heap.ptr = tmp;
-      s->as.heap.len = len;
-      shared->nofree = FALSE;
-      shared->ptr = s->as.heap.ptr;
-    }
-    else if (RSTR_NOFREE_P(s)) {
-      shared->nofree = TRUE;
-      shared->ptr = s->as.heap.ptr;
-      RSTR_UNSET_NOFREE_FLAG(s);
+  mrb_assert(!RSTR_EMBED_P(orig));
+  if (RSTR_SHARED_P(orig)) {
+    shared = orig->as.heap.aux.shared;
+    shared->refcnt++;
+    s->as.heap.ptr = orig->as.heap.ptr;
+    s->as.heap.len = len;
+    s->as.heap.aux.shared = shared;
+    RSTR_SET_SHARED_FLAG(s);
+    RSTR_UNSET_EMBED_FLAG(s);
+  }
+  else if (RSTR_FSHARED_P(orig)) {
+    struct RString *fs;
+
+    fs = orig->as.heap.aux.fshared;
+    s->as.heap.ptr = orig->as.heap.ptr;
+    s->as.heap.len = len;
+    s->as.heap.aux.fshared = fs;
+    RSTR_SET_FSHARED_FLAG(s);
+    RSTR_UNSET_EMBED_FLAG(s);
+  }
+  else if (MRB_FROZEN_P(orig) && !RSTR_POOL_P(orig)) {
+    s->as.heap.ptr = orig->as.heap.ptr;
+    s->as.heap.len = len;
+    s->as.heap.aux.fshared = orig;
+    RSTR_SET_FSHARED_FLAG(s);
+    RSTR_UNSET_EMBED_FLAG(s);
+  }
+  else {
+    shared = (mrb_shared_string *)mrb_malloc(mrb, sizeof(mrb_shared_string));
+    shared->refcnt = 2;
+    shared->nofree = !!RSTR_NOFREE_P(orig);
+    if (!shared->nofree && orig->as.heap.aux.capa > orig->as.heap.len) {
+      shared->ptr = (char *)mrb_realloc(mrb, orig->as.heap.ptr, len+1);
+      orig->as.heap.ptr = shared->ptr;
     }
     else {
-      shared->nofree = FALSE;
-      if (s->as.heap.aux.capa > s->as.heap.len) {
-        s->as.heap.ptr = shared->ptr = (char *)mrb_realloc(mrb, s->as.heap.ptr, s->as.heap.len+1);
-      }
-      else {
-        shared->ptr = s->as.heap.ptr;
-      }
+      shared->ptr = orig->as.heap.ptr;
     }
-    shared->len = s->as.heap.len;
+    orig->as.heap.aux.shared = shared;
+    RSTR_SET_SHARED_FLAG(orig);
+    shared->len = len;
     s->as.heap.aux.shared = shared;
+    s->as.heap.ptr = shared->ptr;
+    s->as.heap.len = len;
     RSTR_SET_SHARED_FLAG(s);
+    RSTR_UNSET_EMBED_FLAG(s);
   }
 }
 
@@ -421,23 +402,17 @@ static mrb_value
 byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
 {
   struct RString *orig, *s;
-  mrb_shared_string *shared;
 
   orig = mrb_str_ptr(str);
-  if (RSTR_EMBED_P(orig) || RSTR_LEN(orig) == 0) {
-    s = str_new(mrb, orig->as.ary+beg, len);
+  if (RSTR_EMBED_P(orig) || RSTR_LEN(orig) == 0 || len <= RSTRING_EMBED_LEN_MAX) {
+    s = str_new(mrb, RSTR_PTR(orig)+beg, len);
   }
   else {
-    str_make_shared(mrb, orig);
-    shared = orig->as.heap.aux.shared;
     s = mrb_obj_alloc_string(mrb);
-    s->as.heap.ptr = orig->as.heap.ptr + beg;
+    str_make_shared(mrb, orig, s);
+    s->as.heap.ptr += beg;
     s->as.heap.len = len;
-    s->as.heap.aux.shared = shared;
-    RSTR_SET_SHARED_FLAG(s);
-    shared->refcnt++;
   }
-
   return mrb_obj_value(s);
 }
 #ifdef MRB_UTF8_STRING
@@ -478,15 +453,14 @@ str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
   return str_subseq(mrb, str, beg, len);
 }
 
-static mrb_int
-str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset)
+MRB_API mrb_int
+mrb_str_index(mrb_state *mrb, mrb_value str, const char *sptr, mrb_int slen, mrb_int offset)
 {
   mrb_int pos;
-  char *s, *sptr;
-  mrb_int len, slen;
+  char *s;
+  mrb_int len;
 
   len = RSTRING_LEN(str);
-  slen = RSTRING_LEN(sub);
   if (offset < 0) {
     offset += len;
     if (offset < 0) return -1;
@@ -498,26 +472,36 @@ str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset)
   }
   if (slen == 0) return offset;
   /* need proceed one character at a time */
-  sptr = RSTRING_PTR(sub);
-  slen = RSTRING_LEN(sub);
   len = RSTRING_LEN(str) - offset;
   pos = mrb_memsearch(sptr, slen, s, len);
   if (pos < 0) return pos;
   return pos + offset;
 }
 
+static mrb_int
+str_index_str(mrb_state *mrb, mrb_value str, mrb_value str2, mrb_int offset)
+{
+  const char *ptr;
+  mrb_int len;
+
+  ptr = RSTRING_PTR(str2);
+  len = RSTRING_LEN(str2);
+
+  return mrb_str_index(mrb, str, ptr, len, offset);
+}
+
 static void
 check_frozen(mrb_state *mrb, struct RString *s)
 {
   if (MRB_FROZEN_P(s)) {
-    mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen string");
+    mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen string");
   }
 }
 
 static mrb_value
 str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2)
 {
-  long len;
+  mrb_int len;
 
   check_frozen(mrb, s1);
   if (s1 == s2) return mrb_obj_value(s1);
@@ -526,33 +510,24 @@ str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2)
   len = RSTR_LEN(s2);
   if (RSTR_SHARED_P(s1)) {
     str_decref(mrb, s1->as.heap.aux.shared);
+    RSTR_UNSET_SHARED_FLAG(s1);
   }
-  else if (!RSTR_EMBED_P(s1) && !RSTR_NOFREE_P(s1)) {
+  else if (!RSTR_EMBED_P(s1) && !RSTR_NOFREE_P(s1) && !RSTR_FSHARED_P(s1)
+           && s1->as.heap.ptr) {
     mrb_free(mrb, s1->as.heap.ptr);
   }
 
+  RSTR_UNSET_FSHARED_FLAG(s1);
   RSTR_UNSET_NOFREE_FLAG(s1);
-
-  if (RSTR_SHARED_P(s2)) {
-L_SHARE:
-    RSTR_UNSET_EMBED_FLAG(s1);
-    s1->as.heap.ptr = s2->as.heap.ptr;
-    s1->as.heap.len = len;
-    s1->as.heap.aux.shared = s2->as.heap.aux.shared;
-    RSTR_SET_SHARED_FLAG(s1);
-    s1->as.heap.aux.shared->refcnt++;
+  if (len <= RSTRING_EMBED_LEN_MAX) {
+    RSTR_UNSET_SHARED_FLAG(s1);
+    RSTR_UNSET_FSHARED_FLAG(s1);
+    RSTR_SET_EMBED_FLAG(s1);
+    memcpy(s1->as.ary, RSTR_PTR(s2), len);
+    RSTR_SET_EMBED_LEN(s1, len);
   }
   else {
-    if (len <= RSTRING_EMBED_LEN_MAX) {
-      RSTR_UNSET_SHARED_FLAG(s1);
-      RSTR_SET_EMBED_FLAG(s1);
-      memcpy(s1->as.ary, RSTR_PTR(s2), len);
-      RSTR_SET_EMBED_LEN(s1, len);
-    }
-    else {
-      str_make_shared(mrb, s2);
-      goto L_SHARE;
-    }
+    str_make_shared(mrb, s2, s1);
   }
 
   return mrb_obj_value(s1);
@@ -576,7 +551,7 @@ str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos)
   if (len) {
     while (sbeg <= s) {
       if (memcmp(s, t, len) == 0) {
-        return s - RSTR_PTR(ps);
+        return (mrb_int)(s - RSTR_PTR(ps));
       }
       s--;
     }
@@ -606,16 +581,16 @@ mrb_str_strlen(mrb_state *mrb, struct RString *s)
 #include <windows.h>
 
 char*
-mrb_utf8_from_locale(const char *str, size_t len)
+mrb_utf8_from_locale(const char *str, int len)
 {
   wchar_t* wcsp;
   char* mbsp;
-  size_t mbssize, wcssize;
+  int mbssize, wcssize;
 
   if (len == 0)
     return strdup("");
   if (len == -1)
-    len = strlen(str);
+    len = (int)strlen(str);
   wcssize = MultiByteToWideChar(GetACP(), 0, str, len,  NULL, 0);
   wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t));
   if (!wcsp)
@@ -636,16 +611,16 @@ mrb_utf8_from_locale(const char *str, size_t len)
 }
 
 char*
-mrb_locale_from_utf8(const char *utf8, size_t len)
+mrb_locale_from_utf8(const char *utf8, int len)
 {
   wchar_t* wcsp;
   char* mbsp;
-  size_t mbssize, wcssize;
+  int mbssize, wcssize;
 
   if (len == 0)
     return strdup("");
   if (len == -1)
-    len = strlen(utf8);
+    len = (int)strlen(utf8);
   wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, len,  NULL, 0);
   wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t));
   if (!wcsp)
@@ -704,11 +679,13 @@ mrb_str_modify(mrb_state *mrb, struct RString *s)
     RSTR_UNSET_SHARED_FLAG(s);
     return;
   }
-  if (RSTR_NOFREE_P(s)) {
+  if (RSTR_NOFREE_P(s) || RSTR_FSHARED_P(s)) {
     char *p = s->as.heap.ptr;
     mrb_int len = s->as.heap.len;
 
+    RSTR_UNSET_FSHARED_FLAG(s);
     RSTR_UNSET_NOFREE_FLAG(s);
+    RSTR_UNSET_FSHARED_FLAG(s);
     if (len < RSTRING_EMBED_LEN_MAX) {
       RSTR_SET_EMBED_FLAG(s);
       RSTR_SET_EMBED_LEN(s, len);
@@ -768,28 +745,10 @@ mrb_str_to_cstr(mrb_state *mrb, mrb_value str0)
 MRB_API void
 mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other)
 {
-  struct RString *s1 = mrb_str_ptr(self), *s2;
-  mrb_int len;
-
-  mrb_str_modify(mrb, s1);
   if (!mrb_string_p(other)) {
     other = mrb_str_to_str(mrb, other);
   }
-  s2 = mrb_str_ptr(other);
-  if (RSTR_LEN(s2) == 0) {
-    return;
-  }
-  len = RSTR_LEN(s1) + RSTR_LEN(s2);
-
-  if (len < 0 || len >= MRB_INT_MAX) {
-    mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
-  }
-  if (RSTRING_CAPA(self) < len) {
-    resize_capa(mrb, s1, len);
-  }
-  memcpy(RSTR_PTR(s1)+RSTR_LEN(s1), RSTR_PTR(s2), RSTR_LEN(s2));
-  RSTR_SET_LEN(s1, len);
-  RSTR_PTR(s1)[len] = '\0';
+  mrb_str_cat_str(mrb, self, other);
 }
 
 /*
@@ -967,7 +926,7 @@ mrb_str_cmp_m(mrb_state *mrb, mrb_value str1)
     else {
       mrb_value tmp = mrb_funcall(mrb, str2, "<=>", 1, str1);
 
-      if (!mrb_nil_p(tmp)) return mrb_nil_value();
+      if (mrb_nil_p(tmp)) return mrb_nil_value();
       if (!mrb_fixnum_p(tmp)) {
         return mrb_funcall(mrb, mrb_fixnum_value(0), "-", 1, tmp);
       }
@@ -1096,7 +1055,7 @@ num_index:
       return str;
 
     case MRB_TT_STRING:
-      if (str_index(mrb, str, indx, 0) != -1)
+      if (str_index_str(mrb, str, indx, 0) != -1)
         return mrb_str_dup(mrb, indx);
       return mrb_nil_value();
 
@@ -1172,7 +1131,7 @@ static mrb_value
 mrb_str_aref_m(mrb_state *mrb, mrb_value str)
 {
   mrb_value a1, a2;
-  int argc;
+  mrb_int argc;
 
   argc = mrb_get_args(mrb, "o|o", &a1, &a2);
   if (argc == 2) {
@@ -1266,8 +1225,8 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str)
   mrb_int argc;
   struct RString *s = mrb_str_ptr(str);
 
-  mrb_str_modify(mrb, s);
   argc = mrb_get_args(mrb, "|S", &rs);
+  mrb_str_modify(mrb, s);
   len = RSTR_LEN(s);
   if (argc == 0) {
     if (len == 0) return mrb_nil_value();
@@ -1514,7 +1473,7 @@ mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
   return str_substr(mrb, str, beg, len);
 }
 
-mrb_int
+uint32_t
 mrb_str_hash(mrb_state *mrb, mrb_value str)
 {
   /* 1-8-7 */
@@ -1527,7 +1486,7 @@ mrb_str_hash(mrb_state *mrb, mrb_value str)
     key = key*65599 + *p;
     p++;
   }
-  return (mrb_int)(key + (key>>5));
+  return (uint32_t)(key + (key>>5));
 }
 
 /* 15.2.10.5.20 */
@@ -1563,7 +1522,7 @@ mrb_str_include(mrb_state *mrb, mrb_value self)
   mrb_value str2;
 
   mrb_get_args(mrb, "S", &str2);
-  if (str_index(mrb, self, str2, 0) < 0)
+  if (str_index_str(mrb, self, str2, 0) < 0)
     return mrb_bool_value(FALSE);
   return mrb_bool_value(TRUE);
 }
@@ -1590,7 +1549,7 @@ mrb_str_include(mrb_state *mrb, mrb_value self)
  *     "hello".index(/[aeiou]/, -3)   #=> 4
  */
 static mrb_value
-mrb_str_index(mrb_state *mrb, mrb_value str)
+mrb_str_index_m(mrb_state *mrb, mrb_value str)
 {
   mrb_value *argv;
   mrb_int argc;
@@ -1631,7 +1590,7 @@ mrb_str_index(mrb_state *mrb, mrb_value str)
     }
     /* fall through */
     case MRB_TT_STRING:
-      pos = str_index(mrb, str, sub, pos);
+      pos = str_index_str(mrb, str, sub, pos);
       break;
   }
 
@@ -1958,7 +1917,7 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str)
 static mrb_value
 mrb_str_split_m(mrb_state *mrb, mrb_value str)
 {
-  int argc;
+  mrb_int argc;
   mrb_value spat = mrb_nil_value();
   enum {awk, string, regexp} split_type = string;
   mrb_int i = 0;
@@ -2072,7 +2031,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
 }
 
 MRB_API mrb_value
-mrb_str_len_to_inum(mrb_state *mrb, const char *str, size_t len, int base, int badcheck)
+mrb_str_len_to_inum(mrb_state *mrb, const char *str, mrb_int len, mrb_int base, int badcheck)
 {
   const char *p = str;
   const char *pend = str + len;
@@ -2208,8 +2167,13 @@ mrb_str_len_to_inum(mrb_state *mrb, const char *str, size_t len, int base, int b
     n *= base;
     n += c;
     if (n > (uint64_t)MRB_INT_MAX + (sign ? 0 : 1)) {
-      mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer",
-                 mrb_str_new(mrb, str, pend-str));
+      if (base == 10) {
+        return mrb_float_value(mrb, mrb_str_to_dbl(mrb, mrb_str_new(mrb, str, len), badcheck));
+      }
+      else {
+        mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer",
+                   mrb_str_new(mrb, str, pend-str));
+      }
     }
   }
   val = (mrb_int)n;
@@ -2299,6 +2263,7 @@ mrb_str_to_i(mrb_state *mrb, mrb_value self)
   return mrb_str_to_inum(mrb, self, base, FALSE);
 }
 
+#ifndef MRB_WITHOUT_FLOAT
 MRB_API double
 mrb_cstr_to_dbl(mrb_state *mrb, const char * p, mrb_bool badcheck)
 {
@@ -2402,6 +2367,7 @@ mrb_str_to_f(mrb_state *mrb, mrb_value self)
 {
   return mrb_float_value(mrb, mrb_str_to_dbl(mrb, self, FALSE));
 }
+#endif
 
 /* 15.2.10.5.40 */
 /*
@@ -2581,10 +2547,10 @@ mrb_str_dump(mrb_state *mrb, mrb_value str)
         }
         else {
           *q++ = '\\';
-          q[2] = '0' + c % 8; c /= 8;
-          q[1] = '0' + c % 8; c /= 8;
-          q[0] = '0' + c % 8;
-          q += 3;
+          *q++ = 'x';
+          q[1] = mrb_digitmap[c % 16]; c /= 16;
+          q[0] = mrb_digitmap[c % 16];
+          q += 2;
         }
     }
   }
@@ -2595,7 +2561,45 @@ mrb_str_dump(mrb_state *mrb, mrb_value str)
 MRB_API mrb_value
 mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len)
 {
-  str_buf_cat(mrb, mrb_str_ptr(str), ptr, len);
+  struct RString *s = mrb_str_ptr(str);
+  size_t capa;
+  size_t total;
+  ptrdiff_t off = -1;
+
+  if (len == 0) return str;
+  mrb_str_modify(mrb, s);
+  if (ptr >= RSTR_PTR(s) && ptr <= RSTR_PTR(s) + (size_t)RSTR_LEN(s)) {
+      off = ptr - RSTR_PTR(s);
+  }
+
+  capa = RSTR_CAPA(s);
+  total = RSTR_LEN(s)+len;
+  if (total >= MRB_INT_MAX) {
+  size_error:
+    mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
+  }
+  if (capa <= total) {
+    if (capa == 0) capa = 1;
+    while (capa <= total) {
+      if (capa <= MRB_INT_MAX / 2) {
+        capa *= 2;
+      }
+      else {
+        capa = total+1;
+      }
+    }
+    if (capa <= total || capa > MRB_INT_MAX) {
+      goto size_error;
+    }
+    resize_capa(mrb, s, capa);
+  }
+  if (off != -1) {
+      ptr = RSTR_PTR(s) + off;
+  }
+  memcpy(RSTR_PTR(s) + RSTR_LEN(s), ptr, len);
+  mrb_assert_int_fit(size_t, total, mrb_int, MRB_INT_MAX);
+  RSTR_SET_LEN(s, total);
+  RSTR_PTR(s)[total] = '\0';   /* sentinel */
   return str;
 }
 
@@ -2686,9 +2690,9 @@ mrb_str_inspect(mrb_state *mrb, mrb_value str)
     }
     else {
       buf[0] = '\\';
-      buf[3] = '0' + c % 8; c /= 8;
-      buf[2] = '0' + c % 8; c /= 8;
-      buf[1] = '0' + c % 8;
+      buf[1] = 'x';
+      buf[3] = mrb_digitmap[c % 16]; c /= 16;
+      buf[2] = mrb_digitmap[c % 16];
       mrb_str_cat(mrb, result, buf, 4);
       continue;
     }
@@ -2752,7 +2756,7 @@ mrb_init_string(mrb_state *mrb)
 
   mrb_define_method(mrb, s, "hash",            mrb_str_hash_m,          MRB_ARGS_NONE()); /* 15.2.10.5.20 */
   mrb_define_method(mrb, s, "include?",        mrb_str_include,         MRB_ARGS_REQ(1)); /* 15.2.10.5.21 */
-  mrb_define_method(mrb, s, "index",           mrb_str_index,           MRB_ARGS_ANY());  /* 15.2.10.5.22 */
+  mrb_define_method(mrb, s, "index",           mrb_str_index_m,         MRB_ARGS_ANY());  /* 15.2.10.5.22 */
   mrb_define_method(mrb, s, "initialize",      mrb_str_init,            MRB_ARGS_REQ(1)); /* 15.2.10.5.23 */
   mrb_define_method(mrb, s, "initialize_copy", mrb_str_replace,         MRB_ARGS_REQ(1)); /* 15.2.10.5.24 */
   mrb_define_method(mrb, s, "intern",          mrb_str_intern,          MRB_ARGS_NONE()); /* 15.2.10.5.25 */
@@ -2765,7 +2769,9 @@ mrb_init_string(mrb_state *mrb)
   mrb_define_method(mrb, s, "slice",           mrb_str_aref_m,          MRB_ARGS_ANY());  /* 15.2.10.5.34 */
   mrb_define_method(mrb, s, "split",           mrb_str_split_m,         MRB_ARGS_ANY());  /* 15.2.10.5.35 */
 
+#ifndef MRB_WITHOUT_FLOAT
   mrb_define_method(mrb, s, "to_f",            mrb_str_to_f,            MRB_ARGS_NONE()); /* 15.2.10.5.38 */
+#endif
   mrb_define_method(mrb, s, "to_i",            mrb_str_to_i,            MRB_ARGS_ANY());  /* 15.2.10.5.39 */
   mrb_define_method(mrb, s, "to_s",            mrb_str_to_s,            MRB_ARGS_NONE()); /* 15.2.10.5.40 */
   mrb_define_method(mrb, s, "to_str",          mrb_str_to_s,            MRB_ARGS_NONE());
@@ -2776,8 +2782,9 @@ mrb_init_string(mrb_state *mrb)
   mrb_define_method(mrb, s, "bytes",           mrb_str_bytes,           MRB_ARGS_NONE());
 }
 
+#ifndef MRB_WITHOUT_FLOAT
 /*
- *     Source code for the "strtod" library procedure.
+ * Source code for the "strtod" library procedure.
  *
  * Copyright (c) 1988-1993 The Regents of the University of California.
  * Copyright (c) 1994 Sun Microsystems, Inc.
@@ -2803,7 +2810,7 @@ static const int maxExponent = 511; /* Largest possible base 10 exponent.  Any
                                      */
 static const double powersOf10[] = {/* Table giving binary powers of 10.  Entry */
     10.,                            /* is 10^2^i.  Used to convert decimal */
-    100.,                          /* exponents into floating-point numbers. */
+    100.,                           /* exponents into floating-point numbers. */
     1.0e4,
     1.0e8,
     1.0e16,
@@ -2815,41 +2822,41 @@ static const double powersOf10[] = {/* Table giving binary powers of 10.  Entry
 
 MRB_API double
 mrb_float_read(const char *string, char **endPtr)
-/*  const char *string;                   A decimal ASCII floating-point number,
-                                * optionally preceded by white space.
-                                * Must have form "-I.FE-X", where I is the
-                                * integer part of the mantissa, F is the
-                                * fractional part of the mantissa, and X
-                                * is the exponent.  Either of the signs
-                                * may be "+", "-", or omitted.  Either I
-                                * or F may be omitted, or both.  The decimal
-                                * point isn't necessary unless F is present.
-                                * The "E" may actually be an "e".  E and X
-                                * may both be omitted (but not just one).
-                                */
-/*  char **endPtr;                If non-NULL, store terminating character's
-                                * address here. */
+/*  const char *string;            A decimal ASCII floating-point number,
+                                 * optionally preceded by white space.
+                                 * Must have form "-I.FE-X", where I is the
+                                 * integer part of the mantissa, F is the
+                                 * fractional part of the mantissa, and X
+                                 * is the exponent.  Either of the signs
+                                 * may be "+", "-", or omitted.  Either I
+                                 * or F may be omitted, or both.  The decimal
+                                 * point isn't necessary unless F is present.
+                                 * The "E" may actually be an "e".  E and X
+                                 * may both be omitted (but not just one).
+                                 */
+/*  char **endPtr;                 If non-NULL, store terminating character's
+                                 * address here. */
 {
     int sign, expSign = FALSE;
     double fraction, dblExp;
     const double *d;
-    register const char *p;
-    register int c;
-    int exp = 0;               /* Exponent read from "EX" field. */
-    int fracExp = 0;           /* Exponent that derives from the fractional
-                                * part.  Under normal circumstatnces, it is
-                                * the negative of the number of digits in F.
-                                * However, if I is very long, the last digits
-                                * of I get dropped (otherwise a long I with a
-                                * large negative exponent could cause an
-                                * unnecessary overflow on I alone).  In this
-                                * case, fracExp is incremented one for each
-                                * dropped digit. */
-    int mantSize;              /* Number of digits in mantissa. */
-    int decPt;                 /* Number of mantissa digits BEFORE decimal
-                                * point. */
-    const char *pExp;          /* Temporarily holds location of exponent
-                                * in string. */
+    const char *p;
+    int c;
+    int exp = 0;                /* Exponent read from "EX" field. */
+    int fracExp = 0;            /* Exponent that derives from the fractional
+                                 * part.  Under normal circumstatnces, it is
+                                 * the negative of the number of digits in F.
+                                 * However, if I is very long, the last digits
+                                 * of I get dropped (otherwise a long I with a
+                                 * large negative exponent could cause an
+                                 * unnecessary overflow on I alone).  In this
+                                 * case, fracExp is incremented one for each
+                                 * dropped digit. */
+    int mantSize;               /* Number of digits in mantissa. */
+    int decPt;                  /* Number of mantissa digits BEFORE decimal
+                                 * point. */
+    const char *pExp;           /* Temporarily holds location of exponent
+                                 * in string. */
 
     /*
      * Strip off leading blanks and check for a sign.
@@ -2857,17 +2864,17 @@ mrb_float_read(const char *string, char **endPtr)
 
     p = string;
     while (isspace(*p)) {
-       p += 1;
+      p += 1;
     }
     if (*p == '-') {
-       sign = TRUE;
-       p += 1;
+      sign = TRUE;
+      p += 1;
     }
     else {
-       if (*p == '+') {
-           p += 1;
-       }
-       sign = FALSE;
+      if (*p == '+') {
+        p += 1;
+      }
+      sign = FALSE;
     }
 
     /*
@@ -2878,14 +2885,14 @@ mrb_float_read(const char *string, char **endPtr)
     decPt = -1;
     for (mantSize = 0; ; mantSize += 1)
     {
-       c = *p;
-       if (!isdigit(c)) {
-           if ((c != '.') || (decPt >= 0)) {
-               break;
-           }
-           decPt = mantSize;
-       }
-       p += 1;
+      c = *p;
+      if (!isdigit(c)) {
+        if ((c != '.') || (decPt >= 0)) {
+          break;
+        }
+        decPt = mantSize;
+      }
+      p += 1;
     }
 
     /*
@@ -2898,53 +2905,53 @@ mrb_float_read(const char *string, char **endPtr)
     pExp  = p;
     p -= mantSize;
     if (decPt < 0) {
-       decPt = mantSize;
+      decPt = mantSize;
     }
     else {
-       mantSize -= 1;                  /* One of the digits was the point. */
+      mantSize -= 1; /* One of the digits was the point. */
     }
     if (mantSize > 18) {
-       if (decPt - 18 > 29999) {
-           fracExp = 29999;
-       }
-        else {
-           fracExp = decPt - 18;
-       }
-       mantSize = 18;
+      if (decPt - 18 > 29999) {
+        fracExp = 29999;
+      }
+      else {
+        fracExp = decPt - 18;
+      }
+      mantSize = 18;
     }
     else {
-       fracExp = decPt - mantSize;
+      fracExp = decPt - mantSize;
     }
     if (mantSize == 0) {
-       fraction = 0.0;
-       p = string;
-       goto done;
+      fraction = 0.0;
+      p = string;
+      goto done;
     }
     else {
-       int frac1, frac2;
-       frac1 = 0;
-       for ( ; mantSize > 9; mantSize -= 1)
-       {
-           c = *p;
-           p += 1;
-           if (c == '.') {
-               c = *p;
-               p += 1;
-           }
-           frac1 = 10*frac1 + (c - '0');
-       }
-       frac2 = 0;
-       for (; mantSize > 0; mantSize -= 1)
-       {
-           c = *p;
-           p += 1;
-           if (c == '.') {
-               c = *p;
-               p += 1;
-           }
-           frac2 = 10*frac2 + (c - '0');
-       }
-       fraction = (1.0e9 * frac1) + frac2;
+      int frac1, frac2;
+      frac1 = 0;
+      for ( ; mantSize > 9; mantSize -= 1)
+      {
+        c = *p;
+        p += 1;
+        if (c == '.') {
+          c = *p;
+          p += 1;
+        }
+        frac1 = 10*frac1 + (c - '0');
+      }
+      frac2 = 0;
+      for (; mantSize > 0; mantSize -= 1)
+      {
+        c = *p;
+        p += 1;
+        if (c == '.') {
+          c = *p;
+          p += 1;
+        }
+        frac2 = 10*frac2 + (c - '0');
+      }
+      fraction = (1.0e9 * frac1) + frac2;
     }
 
     /*
@@ -2953,30 +2960,30 @@ mrb_float_read(const char *string, char **endPtr)
 
     p = pExp;
     if ((*p == 'E') || (*p == 'e')) {
-       p += 1;
-       if (*p == '-') {
-           expSign = TRUE;
-           p += 1;
-       }
-        else {
-           if (*p == '+') {
-               p += 1;
-           }
-           expSign = FALSE;
-       }
-       while (isdigit(*p)) {
-           exp = exp * 10 + (*p - '0');
-           if (exp > 19999) {
-               exp = 19999;
-           }
-           p += 1;
-       }
+      p += 1;
+      if (*p == '-') {
+        expSign = TRUE;
+        p += 1;
+      }
+      else {
+        if (*p == '+') {
+          p += 1;
+        }
+        expSign = FALSE;
+      }
+      while (isdigit(*p)) {
+        exp = exp * 10 + (*p - '0');
+        if (exp > 19999) {
+          exp = 19999;
+        }
+        p += 1;
+      }
     }
     if (expSign) {
-       exp = fracExp - exp;
+      exp = fracExp - exp;
     }
     else {
-       exp = fracExp + exp;
+      exp = fracExp + exp;
     }
 
     /*
@@ -2987,36 +2994,37 @@ mrb_float_read(const char *string, char **endPtr)
      */
 
     if (exp < 0) {
-       expSign = TRUE;
-       exp = -exp;
+      expSign = TRUE;
+      exp = -exp;
     }
     else {
-       expSign = FALSE;
+      expSign = FALSE;
     }
     if (exp > maxExponent) {
-       exp = maxExponent;
-       errno = ERANGE;
+      exp = maxExponent;
+      errno = ERANGE;
     }
     dblExp = 1.0;
     for (d = powersOf10; exp != 0; exp >>= 1, d += 1) {
-       if (exp & 01) {
-           dblExp *= *d;
-       }
+      if (exp & 01) {
+        dblExp *= *d;
+      }
     }
     if (expSign) {
-       fraction /= dblExp;
+      fraction /= dblExp;
     }
     else {
-       fraction *= dblExp;
+      fraction *= dblExp;
     }
 
 done:
     if (endPtr != NULL) {
-       *endPtr = (char *) p;
+      *endPtr = (char *) p;
     }
 
     if (sign) {
-       return -fraction;
+      return -fraction;
     }
     return fraction;
 }
+#endif
index a3ab05c..5e1f9f5 100644 (file)
@@ -68,7 +68,7 @@ sym_intern(mrb_state *mrb, const char *name, size_t len, mrb_bool lit)
   sym = ++mrb->symidx;
   if (mrb->symcapa < sym) {
     if (mrb->symcapa == 0) mrb->symcapa = 100;
-    else mrb->symcapa = (size_t)(mrb->symcapa * 1.2);
+    else mrb->symcapa = (size_t)(mrb->symcapa * 6 / 5);
     mrb->symtbl = (symbol_name*)mrb_realloc(mrb, mrb->symtbl, sizeof(symbol_name)*(mrb->symcapa+1));
   }
   sname = &mrb->symtbl[sym];
index 86ac32e..de36efa 100644 (file)
 
 typedef int (iv_foreach_func)(mrb_state*,mrb_sym,mrb_value,void*);
 
-#ifndef MRB_IV_SEGMENT_SIZE
-#define MRB_IV_SEGMENT_SIZE 4
+#include <mruby/khash.h>
+
+#ifndef MRB_IVHASH_INIT_SIZE
+#define MRB_IVHASH_INIT_SIZE KHASH_MIN_SIZE
 #endif
 
-typedef struct segment {
-  mrb_sym key[MRB_IV_SEGMENT_SIZE];
-  mrb_value val[MRB_IV_SEGMENT_SIZE];
-  struct segment *next;
-} segment;
+KHASH_DECLARE(iv, mrb_sym, mrb_value, TRUE)
+KHASH_DEFINE(iv, mrb_sym, mrb_value, TRUE, kh_int_hash_func, kh_int_hash_equal)
 
 /* Instance variable table structure */
 typedef struct iv_tbl {
-  segment *rootseg;
-  size_t size;
-  size_t last_len;
+  khash_t(iv) h;
 } iv_tbl;
 
 /*
@@ -40,14 +37,7 @@ typedef struct iv_tbl {
 static iv_tbl*
 iv_new(mrb_state *mrb)
 {
-  iv_tbl *t;
-
-  t = (iv_tbl*)mrb_malloc(mrb, sizeof(iv_tbl));
-  t->size = 0;
-  t->rootseg =  NULL;
-  t->last_len = 0;
-
-  return t;
+  return (iv_tbl*)kh_init_size(iv, mrb, MRB_IVHASH_INIT_SIZE);
 }
 
 /*
@@ -62,56 +52,11 @@ iv_new(mrb_state *mrb)
 static void
 iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val)
 {
-  segment *seg = t->rootseg;
-  segment *prev = NULL;
-  segment *matched_seg = NULL;
-  size_t matched_idx = 0;
-  size_t i;
-
-  while (seg) {
-    for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
-      mrb_sym key = seg->key[i];
-      /* Found room in last segment after last_len */
-      if (!seg->next && i >= t->last_len) {
-        seg->key[i] = sym;
-        seg->val[i] = val;
-        t->last_len = i+1;
-        t->size++;
-        return;
-      }
-      if (!matched_seg && key == 0) {
-        matched_seg = seg;
-        matched_idx = i;
-      }
-      else if (key == sym) {
-        seg->val[i] = val;
-        return;
-      }
-    }
-    prev = seg;
-    seg = seg->next;
-  }
+  khash_t(iv) *h = &t->h;
+  khiter_t k;
 
-  /* Not found */
-  t->size++;
-  if (matched_seg) {
-    matched_seg->key[matched_idx] = sym;
-    matched_seg->val[matched_idx] = val;
-    return;
-  }
-
-  seg = (segment*)mrb_malloc(mrb, sizeof(segment));
-  if (!seg) return;
-  seg->next = NULL;
-  seg->key[0] = sym;
-  seg->val[0] = val;
-  t->last_len = 1;
-  if (prev) {
-    prev->next = seg;
-  }
-  else {
-    t->rootseg = seg;
-  }
+  k = kh_put(iv, mrb, h, sym);
+  kh_value(h, k) = val;
 }
 
 /*
@@ -129,23 +74,13 @@ iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val)
 static mrb_bool
 iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
 {
-  segment *seg;
-  size_t i;
+  khash_t(iv) *h = &t->h;
+  khiter_t k;
 
-  seg = t->rootseg;
-  while (seg) {
-    for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
-      mrb_sym key = seg->key[i];
-
-      if (!seg->next && i >= t->last_len) {
-        return FALSE;
-      }
-      if (key == sym) {
-        if (vp) *vp = seg->val[i];
-        return TRUE;
-      }
-    }
-    seg = seg->next;
+  k = kh_get(iv, mrb, h, sym);
+  if (k != kh_end(h)) {
+    if (vp) *vp = kh_value(h, k);
+    return TRUE;
   }
   return FALSE;
 }
@@ -164,25 +99,18 @@ iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
 static mrb_bool
 iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
 {
-  segment *seg;
-  size_t i;
-
-  seg = t->rootseg;
-  while (seg) {
-    for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
-      mrb_sym key = seg->key[i];
-
-      if (!seg->next && i >= t->last_len) {
-        return FALSE;
-      }
-      if (key == sym) {
-        t->size--;
-        seg->key[i] = 0;
-        if (vp) *vp = seg->val[i];
-        return TRUE;
-      }
+  if (t == NULL) return FALSE;
+  else {
+    khash_t(iv) *h = &t->h;
+    khiter_t k;
+
+    k = kh_get(iv, mrb, h, sym);
+    if (k != kh_end(h)) {
+      mrb_value val = kh_value(h, k);
+      kh_del(iv, mrb, h, k);
+      if (vp) *vp = val;
+      return TRUE;
     }
-    seg = seg->next;
   }
   return FALSE;
 }
@@ -190,29 +118,23 @@ iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
 static mrb_bool
 iv_foreach(mrb_state *mrb, iv_tbl *t, iv_foreach_func *func, void *p)
 {
-  segment *seg;
-  size_t i;
-  int n;
-
-  seg = t->rootseg;
-  while (seg) {
-    for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
-      mrb_sym key = seg->key[i];
+  if (t == NULL) {
+    return TRUE;
+  }
+  else {
+    khash_t(iv) *h = &t->h;
+    khiter_t k;
+    int n;
 
-      /* no value in last segment after last_len */
-      if (!seg->next && i >= t->last_len) {
-        return FALSE;
-      }
-      if (key != 0) {
-        n =(*func)(mrb, key, seg->val[i], p);
+    for (k = kh_begin(h); k != kh_end(h); k++) {
+      if (kh_exist(h, k)) {
+        n = (*func)(mrb, kh_key(h, k), kh_value(h, k), p);
         if (n > 0) return FALSE;
         if (n < 0) {
-          t->size--;
-          seg->key[i] = 0;
+          kh_del(iv, mrb, h, k);
         }
       }
     }
-    seg = seg->next;
   }
   return TRUE;
 }
@@ -220,62 +142,22 @@ iv_foreach(mrb_state *mrb, iv_tbl *t, iv_foreach_func *func, void *p)
 static size_t
 iv_size(mrb_state *mrb, iv_tbl *t)
 {
-  segment *seg;
-  size_t size = 0;
-
-  if (!t) return 0;
-  if (t->size > 0) return t->size;
-  seg = t->rootseg;
-  while (seg) {
-    if (seg->next == NULL) {
-      size += t->last_len;
-      return size;
-    }
-    seg = seg->next;
-    size += MRB_IV_SEGMENT_SIZE;
+  if (t) {
+    return kh_size(&t->h);
   }
-  /* empty iv_tbl */
   return 0;
 }
 
 static iv_tbl*
 iv_copy(mrb_state *mrb, iv_tbl *t)
 {
-  segment *seg;
-  iv_tbl *t2;
-
-  size_t i;
-
-  seg = t->rootseg;
-  t2 = iv_new(mrb);
-
-  while (seg != NULL) {
-    for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
-      mrb_sym key = seg->key[i];
-      mrb_value val = seg->val[i];
-
-      if ((seg->next == NULL) && (i >= t->last_len)) {
-        return t2;
-      }
-      iv_put(mrb, t2, key, val);
-    }
-    seg = seg->next;
-  }
-  return t2;
+  return (iv_tbl*)kh_copy(iv, mrb, &t->h);
 }
 
 static void
 iv_free(mrb_state *mrb, iv_tbl *t)
 {
-  segment *seg;
-
-  seg = t->rootseg;
-  while (seg) {
-    segment *p = seg;
-    seg = seg->next;
-    mrb_free(mrb, p);
-  }
-  mrb_free(mrb, t);
+  kh_destroy(iv, mrb, &t->h);
 }
 
 static int
@@ -379,7 +261,7 @@ mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
   iv_tbl *t = obj->iv;
 
   if (MRB_FROZEN_P(obj)) {
-    mrb_raisef(mrb, E_RUNTIME_ERROR, "can't modify frozen %S", mrb_obj_value(obj));
+    mrb_raisef(mrb, E_FROZEN_ERROR, "can't modify frozen %S", mrb_obj_value(obj));
   }
   if (!t) {
     t = obj->iv = iv_new(mrb);
@@ -389,21 +271,6 @@ mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
 }
 
 MRB_API void
-mrb_obj_iv_ifnone(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
-{
-  iv_tbl *t = obj->iv;
-
-  if (!t) {
-    t = obj->iv = iv_new(mrb);
-  }
-  else if (iv_get(mrb, t, sym, &v)) {
-    return;
-  }
-  mrb_write_barrier(mrb, (struct RBasic*)obj);
-  iv_put(mrb, t, sym, v);
-}
-
-MRB_API void
 mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v)
 {
   if (obj_iv_p(obj)) {
@@ -513,7 +380,7 @@ mrb_obj_iv_inspect(mrb_state *mrb, struct RObject *obj)
 
   if (len > 0) {
     const char *cn = mrb_obj_classname(mrb, mrb_obj_value(obj));
-    mrb_value str = mrb_str_buf_new(mrb, 30);
+    mrb_value str = mrb_str_new_capa(mrb, 30);
 
     mrb_str_cat_lit(mrb, str, "-<");
     mrb_str_cat_cstr(mrb, str, cn);
@@ -765,19 +632,18 @@ mrb_cv_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym)
 mrb_value
 mrb_vm_cv_get(mrb_state *mrb, mrb_sym sym)
 {
-  struct RClass *c = mrb->c->ci->proc->target_class;
-
-  if (!c) c = mrb->c->ci->target_class;
+  struct RClass *c;
 
+  c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
   return mrb_mod_cv_get(mrb, c, sym);
 }
 
 void
 mrb_vm_cv_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
 {
-  struct RClass *c = mrb->c->ci->proc->target_class;
+  struct RClass *c;
 
-  if (!c) c = mrb->c->ci->target_class;
+  c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
   mrb_mod_cv_set(mrb, c, sym, v);
 }
 
@@ -796,25 +662,24 @@ mod_const_check(mrb_state *mrb, mrb_value mod)
 }
 
 static mrb_value
-const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym)
+const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym, mrb_bool top)
 {
   struct RClass *c = base;
   mrb_value v;
-  iv_tbl *t;
   mrb_bool retry = FALSE;
   mrb_value name;
+  struct RClass *oclass = mrb->object_class;
 
 L_RETRY:
   while (c) {
-    if (c->iv) {
-      t = c->iv;
-      if (iv_get(mrb, t, sym, &v))
+    if (c->iv && (top || c != oclass || base == oclass)) {
+      if (iv_get(mrb, c->iv, sym, &v))
         return v;
     }
     c = c->super;
   }
-  if (!retry && base && base->tt == MRB_TT_MODULE) {
-    c = mrb->object_class;
+  if (!retry && base->tt == MRB_TT_MODULE) {
+    c = oclass;
     retry = TRUE;
     goto L_RETRY;
   }
@@ -826,55 +691,57 @@ MRB_API mrb_value
 mrb_const_get(mrb_state *mrb, mrb_value mod, mrb_sym sym)
 {
   mod_const_check(mrb, mod);
-  return const_get(mrb, mrb_class_ptr(mod), sym);
+  return const_get(mrb, mrb_class_ptr(mod), sym, FALSE);
 }
 
 mrb_value
 mrb_vm_const_get(mrb_state *mrb, mrb_sym sym)
 {
-  struct RClass *c = mrb->c->ci->proc->target_class;
-
-  if (!c) c = mrb->c->ci->target_class;
-  if (c) {
-    struct RClass *c2;
-    mrb_value v;
+  struct RClass *c;
+  struct RClass *c2;
+  mrb_value v;
+  struct RProc *proc;
 
-    if (c->iv && iv_get(mrb, c->iv, sym, &v)) {
+  c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
+  if (c->iv && iv_get(mrb, c->iv, sym, &v)) {
+    return v;
+  }
+  c2 = c;
+  while (c2 && c2->tt == MRB_TT_SCLASS) {
+    mrb_value klass;
+    klass = mrb_obj_iv_get(mrb, (struct RObject *)c2,
+                           mrb_intern_lit(mrb, "__attached__"));
+    c2 = mrb_class_ptr(klass);
+  }
+  if (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE) c = c2;
+  mrb_assert(!MRB_PROC_CFUNC_P(mrb->c->ci->proc));
+  proc = mrb->c->ci->proc;
+  while (proc) {
+    c2 = MRB_PROC_TARGET_CLASS(proc);
+    if (c2 && c2->iv && iv_get(mrb, c2->iv, sym, &v)) { 
       return v;
     }
-    c2 = c;
-    while (c2 && c2->tt == MRB_TT_SCLASS) {
-      mrb_value klass;
-      klass = mrb_obj_iv_get(mrb, (struct RObject *)c2,
-                             mrb_intern_lit(mrb, "__attached__"));
-      c2 = mrb_class_ptr(klass);
-    }
-    if (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE) c = c2;
-    c2 = c;
-    for (;;) {
-      c2 = mrb_class_outer_module(mrb, c2);
-      if (!c2) break;
-      if (c2->iv && iv_get(mrb, c2->iv, sym, &v)) {
-        return v;
-      }
-    }
+    proc = proc->upper;
   }
-  return const_get(mrb, c, sym);
+  return const_get(mrb, c, sym, TRUE);
 }
 
 MRB_API void
 mrb_const_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v)
 {
   mod_const_check(mrb, mod);
+  if (mrb_type(v) == MRB_TT_CLASS || mrb_type(v) == MRB_TT_MODULE) {
+    mrb_class_name_class(mrb, mrb_class_ptr(mod), mrb_class_ptr(v), sym);
+  }
   mrb_iv_set(mrb, mod, sym, v);
 }
 
 void
 mrb_vm_const_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
 {
-  struct RClass *c = mrb->c->ci->proc->target_class;
+  struct RClass *c;
 
-  if (!c) c = mrb->c->ci->target_class;
+  c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
   mrb_obj_iv_set(mrb, (struct RObject*)c, sym, v);
 }
 
@@ -1020,7 +887,7 @@ mrb_const_defined_0(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool exclude,
 {
   struct RClass *klass = mrb_class_ptr(mod);
   struct RClass *tmp;
-  mrb_bool mod_retry = 0;
+  mrb_bool mod_retry = FALSE;
 
   tmp = klass;
 retry:
@@ -1032,7 +899,7 @@ retry:
     tmp = tmp->super;
   }
   if (!exclude && !mod_retry && (klass->tt == MRB_TT_MODULE)) {
-    mod_retry = 1;
+    mod_retry = TRUE;
     tmp = mrb->object_class;
     goto retry;
   }
@@ -1061,37 +928,89 @@ struct csym_arg {
   struct RClass *c;
   mrb_sym sym;
 };
-
 static int
 csym_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
 {
   struct csym_arg *a = (struct csym_arg*)p;
   struct RClass *c = a->c;
-
   if (mrb_type(v) == c->tt && mrb_class_ptr(v) == c) {
     a->sym = sym;
     return 1;     /* stop iteration */
   }
   return 0;
 }
+static mrb_sym
+find_class_sym(mrb_state *mrb, struct RClass *outer, struct RClass *c)
+{
+  struct csym_arg arg;
+  if (!outer) return 0;
+  if (outer == c) return 0;
+  arg.c = c;
+  arg.sym = 0;
+  iv_foreach(mrb, outer->iv, csym_i, &arg);
+  return arg.sym;
+}
 
-mrb_sym
-mrb_class_sym(mrb_state *mrb, struct RClass *c, struct RClass *outer)
+static struct RClass*
+outer_class(mrb_state *mrb, struct RClass *c)
 {
-  mrb_value name;
+  mrb_value ov;
 
-  name = mrb_obj_iv_get(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__classid__"));
-  if (mrb_nil_p(name)) {
+  ov = mrb_obj_iv_get(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"));
+  if (mrb_nil_p(ov)) return NULL;
+  switch (mrb_type(ov)) {
+  case MRB_TT_CLASS:
+  case MRB_TT_MODULE:
+    return mrb_class_ptr(ov);
+  default:
+    break;
+  }
+  return NULL;
+}
 
-    if (!outer) return 0;
-    else {
-      struct csym_arg arg;
+static mrb_bool
+detect_outer_loop(mrb_state *mrb, struct RClass *c)
+{
+  struct RClass *t = c;         /* tortoise */
+  struct RClass *h = c;         /* hare */
 
-      arg.c = c;
-      arg.sym = 0;
-      iv_foreach(mrb, outer->iv, csym_i, &arg);
-      return arg.sym;
-    }
+  for (;;) {
+    if (h == NULL) return FALSE;
+    h = outer_class(mrb, h);
+    if (h == NULL) return FALSE;
+    h = outer_class(mrb, h);
+    t = outer_class(mrb, t);
+    if (t == h) return TRUE;
   }
-  return mrb_symbol(name);
+}
+
+mrb_value
+mrb_class_find_path(mrb_state *mrb, struct RClass *c)
+{
+  struct RClass *outer;
+  mrb_value path;
+  mrb_sym name;
+  const char *str;
+  mrb_int len;
+
+  if (detect_outer_loop(mrb, c)) return mrb_nil_value();
+  outer = outer_class(mrb, c);
+  if (outer == NULL) return mrb_nil_value();
+  name = find_class_sym(mrb, outer, c);
+  if (name == 0) return mrb_nil_value();
+  str = mrb_class_name(mrb, outer);
+  path = mrb_str_new_capa(mrb, 40);
+  mrb_str_cat_cstr(mrb, path, str);
+  mrb_str_cat_cstr(mrb, path, "::");
+
+  str = mrb_sym2name_len(mrb, name, &len);
+  mrb_str_cat(mrb, path, str, len);
+  iv_del(mrb, c->iv, mrb_intern_lit(mrb, "__outer__"), NULL);
+  iv_put(mrb, c->iv, mrb_intern_lit(mrb, "__classname__"), path);
+  mrb_field_write_barrier_value(mrb, (struct RBasic*)c, path);
+  return path;
 }
index 45f9bf9..9e43d0a 100644 (file)
@@ -53,6 +53,11 @@ void abort(void);
 #define MRB_FUNCALL_DEPTH_MAX 512
 #endif
 
+/* Maximum depth of ecall() recursion. */
+#ifndef MRB_ECALL_DEPTH_MAX
+#define MRB_ECALL_DEPTH_MAX 32
+#endif
+
 /* Maximum stack depth. Should be set lower on memory constrained systems.
 The value below allows about 60000 recursive calls in the simplest case. */
 #ifndef MRB_STACK_MAX
@@ -96,7 +101,7 @@ static inline void
 stack_clear(mrb_value *from, size_t count)
 {
 #ifndef MRB_NAN_BOXING
-  const mrb_value mrb_value_zero = { { 0 } };
+  const mrb_value mrb_value_zero = { 0 };
 
   while (count-- > 0) {
     *from++ = mrb_value_zero;
@@ -151,6 +156,18 @@ envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t size)
 
       e->stack = newbase + off;
     }
+
+    if (ci->proc && MRB_PROC_ENV_P(ci->proc) && ci->env != MRB_PROC_ENV(ci->proc)) {
+      e = MRB_PROC_ENV(ci->proc);
+
+      if (e && MRB_ENV_STACK_SHARED_P(e) &&
+          (st = e->stack) && oldbase <= st && st < oldbase+size) {
+        ptrdiff_t off = e->stack - oldbase;
+
+        e->stack = newbase + off;
+      }
+    }
+
     ci->stackent = newbase + (ci->stackent - oldbase);
     ci++;
   }
@@ -211,38 +228,38 @@ stack_extend(mrb_state *mrb, int room)
 static inline struct REnv*
 uvenv(mrb_state *mrb, int up)
 {
-  struct REnv *e = mrb->c->ci->proc->env;
+  struct RProc *proc = mrb->c->ci->proc;
+  struct REnv *e;
 
   while (up--) {
-    if (!e) return NULL;
-    e = (struct REnv*)e->c;
+    proc = proc->upper;
+    if (!proc) return NULL;
   }
-  return e;
-}
-
-static inline mrb_bool
-is_strict(mrb_state *mrb, struct REnv *e)
-{
-  int cioff = e->cioff;
+  e = MRB_PROC_ENV(proc);
+  if (e) return e;              /* proc has enclosed env */
+  else {
+    mrb_callinfo *ci = mrb->c->ci;
+    mrb_callinfo *cb = mrb->c->cibase;
 
-  if (MRB_ENV_STACK_SHARED_P(e) && e->cxt.c->cibase[cioff].proc &&
-      MRB_PROC_STRICT_P(e->cxt.c->cibase[cioff].proc)) {
-    return TRUE;
+    while (cb <= ci) {
+      if (ci->proc == proc) {
+        return ci->env;
+      }
+      ci--;
+    }
   }
-  return FALSE;
+  return NULL;
 }
 
-static inline struct REnv*
-top_env(mrb_state *mrb, struct RProc *proc)
+static inline struct RProc*
+top_proc(mrb_state *mrb, struct RProc *proc)
 {
-  struct REnv *e = proc->env;
-
-  if (is_strict(mrb, e)) return e;
-  while (e->c) {
-    e = (struct REnv*)e->c;
-    if (is_strict(mrb, e)) return e;
+  while (proc->upper) {
+    if (MRB_PROC_SCOPE_P(proc) || MRB_PROC_STRICT_P(proc))
+      return proc;
+    proc = proc->upper;
   }
-  return e;
+  return proc;
 }
 
 #define CI_ACC_SKIP    -1
@@ -273,29 +290,22 @@ cipush(mrb_state *mrb)
   return ci;
 }
 
-MRB_API void
+void
 mrb_env_unshare(mrb_state *mrb, struct REnv *e)
 {
   if (e == NULL) return;
   else {
     size_t len = (size_t)MRB_ENV_STACK_LEN(e);
-    ptrdiff_t cioff = e->cioff;
     mrb_value *p;
 
     if (!MRB_ENV_STACK_SHARED_P(e)) return;
-    if (e->cxt.c != mrb->c) return;
-    if (e->cioff == 0 && e->cxt.c == mrb->root_c) return;
-    MRB_ENV_UNSHARE_STACK(e);
-    if (!e->c) {
-      /* save block argument position (negated) */
-      e->cioff = -e->cxt.c->cibase[cioff].argc-1;
-    }
-    e->cxt.mid = e->cxt.c->cibase[cioff].mid;
+    if (e->cxt != mrb->c) return;
     p = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*len);
     if (len > 0) {
       stack_copy(p, e->stack, len);
     }
     e->stack = p;
+    MRB_ENV_UNSHARE_STACK(e);
     mrb_write_barrier(mrb, (struct RBasic *)e);
   }
 }
@@ -307,29 +317,38 @@ cipop(mrb_state *mrb)
   struct REnv *env = c->ci->env;
 
   c->ci--;
-  mrb_env_unshare(mrb, env);
+  if (env) mrb_env_unshare(mrb, env);
 }
 
 void mrb_exc_set(mrb_state *mrb, mrb_value exc);
 
 static void
-ecall(mrb_state *mrb, int i)
+ecall(mrb_state *mrb)
 {
   struct RProc *p;
-  mrb_callinfo *ci = mrb->c->ci;
-  mrb_value *self = mrb->c->stack;
+  struct mrb_context *c = mrb->c;
+  mrb_callinfo *ci = c->ci;
   struct RObject *exc;
+  struct REnv *env;
   ptrdiff_t cioff;
   int ai = mrb_gc_arena_save(mrb);
+  int i = --c->eidx;
+  int nregs;
 
   if (i<0) return;
-  if (ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) {
+  if (ci - c->cibase > MRB_ECALL_DEPTH_MAX) {
     mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
   }
-  p = mrb->c->ensure[i];
+  p = c->ensure[i];
   if (!p) return;
-  mrb->c->ensure[i] = NULL;
-  cioff = ci - mrb->c->cibase;
+  mrb_assert(!MRB_PROC_CFUNC_P(p));
+  c->ensure[i] = NULL;
+  nregs = p->upper->body.irep->nregs;
+  if (ci->proc && !MRB_PROC_CFUNC_P(ci->proc) &&
+      ci->proc->body.irep->nregs > nregs) {
+    nregs = ci->proc->body.irep->nregs;
+  }
+  cioff = ci - c->cibase;
   ci = cipush(mrb);
   ci->stackent = mrb->c->stack;
   ci->mid = ci[-1].mid;
@@ -337,14 +356,17 @@ ecall(mrb_state *mrb, int i)
   ci->argc = 0;
   ci->proc = p;
   ci->nregs = p->body.irep->nregs;
-  ci->target_class = p->target_class;
-  mrb->c->stack = mrb->c->stack + ci[-1].nregs;
+  ci->target_class = MRB_PROC_TARGET_CLASS(p);
+  env = MRB_PROC_ENV(p);
+  mrb_assert(env);
+  c->stack += nregs;
   exc = mrb->exc; mrb->exc = 0;
   if (exc) {
     mrb_gc_protect(mrb, mrb_obj_value(exc));
   }
-  mrb_run(mrb, p, *self);
-  mrb->c->ci = mrb->c->cibase + cioff;
+  mrb_run(mrb, p, env->stack[0]);
+  mrb->c = c;
+  c->ci = c->cibase + cioff;
   if (!mrb->exc) mrb->exc = exc;
   mrb_gc_arena_restore(mrb, ai);
 }
@@ -400,7 +422,7 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
     mrb->jmp = 0;
   }
   else {
-    struct RProc *p;
+    mrb_method_t m;
     struct RClass *c;
     mrb_callinfo *ci;
     int n;
@@ -414,12 +436,12 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
       mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative argc for funcall (%S)", mrb_fixnum_value(argc));
     }
     c = mrb_class(mrb, self);
-    p = mrb_method_search_vm(mrb, &c, mid);
-    if (!p) {
+    m = mrb_method_search_vm(mrb, &c, mid);
+    if (MRB_METHOD_UNDEF_P(m)) {
       mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
       mrb_value args = mrb_ary_new_from_values(mrb, argc, argv);
-      p = mrb_method_search_vm(mrb, &c, missing);
-      if (!p) {
+      m = mrb_method_search_vm(mrb, &c, missing);
+      if (MRB_METHOD_UNDEF_P(m)) {
         mrb_method_missing(mrb, mid, self, args);
       }
       mrb_ary_unshift(mrb, args, mrb_symbol_value(mid));
@@ -432,28 +454,29 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
     }
     ci = cipush(mrb);
     ci->mid = mid;
-    ci->proc = p;
     ci->stackent = mrb->c->stack;
-    ci->argc = argc;
+    ci->argc = (int)argc;
     ci->target_class = c;
     mrb->c->stack = mrb->c->stack + n;
     if (mrb->c->stbase <= argv && argv < mrb->c->stend) {
       voff = argv - mrb->c->stbase;
     }
-    if (MRB_PROC_CFUNC_P(p)) {
-      ci->nregs = argc + 2;
+    if (MRB_METHOD_CFUNC_P(m)) {
+      ci->nregs = (int)(argc + 2);
       stack_extend(mrb, ci->nregs);
     }
     else if (argc >= CALL_MAXARGS) {
       mrb_value args = mrb_ary_new_from_values(mrb, argc, argv);
-      stack_extend(mrb, ci->nregs);
+      stack_extend(mrb, ci->nregs+2);
       mrb->c->stack[1] = args;
       ci->argc = -1;
       argc = 1;
     }
     else {
+      struct RProc *p = MRB_METHOD_PROC(m);
+      ci->proc = p;
       if (argc < 0) argc = 1;
-      ci->nregs = p->body.irep->nregs + argc;
+      ci->nregs = (int)(p->body.irep->nregs + argc);
       stack_extend(mrb, ci->nregs);
     }
     if (voff >= 0) {
@@ -465,18 +488,21 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
     }
     mrb->c->stack[argc+1] = blk;
 
-    if (MRB_PROC_CFUNC_P(p)) {
+    if (MRB_METHOD_CFUNC_P(m)) {
       int ai = mrb_gc_arena_save(mrb);
 
       ci->acc = CI_ACC_DIRECT;
-      val = p->body.func(mrb, self);
+      if (MRB_METHOD_PROC_P(m)) {
+        ci->proc = MRB_METHOD_PROC(m);
+      }
+      val = MRB_METHOD_CFUNC(m)(mrb, self);
       mrb->c->stack = mrb->c->ci->stackent;
       cipop(mrb);
       mrb_gc_arena_restore(mrb, ai);
     }
     else {
       ci->acc = CI_ACC_SKIP;
-      val = mrb_run(mrb, p, self);
+      val = mrb_run(mrb, MRB_METHOD_PROC(m), self);
     }
   }
   mrb_gc_protect(mrb, val);
@@ -493,21 +519,24 @@ mrb_value
 mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
 {
   mrb_callinfo *ci = mrb->c->ci;
+  int keep;
 
   mrb->c->stack[0] = self;
   ci->proc = p;
-  ci->target_class = p->target_class;
   if (MRB_PROC_CFUNC_P(p)) {
-    return p->body.func(mrb, self);
+    return MRB_PROC_CFUNC(p)(mrb, self);
   }
-  if (ci->argc < 0) {
-    stack_extend(mrb, (p->body.irep->nregs < 3) ? 3 : p->body.irep->nregs);
+  ci->nregs = p->body.irep->nregs;
+  if (ci->argc < 0) keep = 3;
+  else keep = ci->argc + 2;
+  if (ci->nregs < keep) {
+    stack_extend(mrb, keep);
   }
   else {
-    stack_extend(mrb, p->body.irep->nregs);
+    stack_extend(mrb, ci->nregs);
+    stack_clear(mrb->c->stack+keep, ci->nregs-keep);
   }
 
-  ci->nregs = p->body.irep->nregs;
   ci = cipush(mrb);
   ci->nregs = 0;
   ci->target_class = 0;
@@ -543,7 +572,7 @@ mrb_f_send(mrb_state *mrb, mrb_value self)
   mrb_sym name;
   mrb_value block, *argv, *regs;
   mrb_int argc, i, len;
-  struct RProc *p;
+  mrb_method_t m;
   struct RClass *c;
   mrb_callinfo *ci;
 
@@ -555,9 +584,8 @@ mrb_f_send(mrb_state *mrb, mrb_value self)
   }
 
   c = mrb_class(mrb, self);
-  p = mrb_method_search_vm(mrb, &c, name);
-
-  if (!p) {                     /* call method_mising */
+  m = mrb_method_search_vm(mrb, &c, name);
+  if (MRB_METHOD_UNDEF_P(m)) {            /* call method_mising */
     goto funcall;
   }
 
@@ -575,7 +603,13 @@ mrb_f_send(mrb_state *mrb, mrb_value self)
     mrb_ary_shift(mrb, regs[0]);
   }
 
-  return mrb_exec_irep(mrb, self, p);
+  if (MRB_METHOD_CFUNC_P(m)) {
+    if (MRB_METHOD_PROC_P(m)) {
+      ci->proc = MRB_METHOD_PROC(m);
+    }
+    return MRB_METHOD_CFUNC(m)(mrb, self);
+  }
+  return mrb_exec_irep(mrb, self, MRB_METHOD_PROC(m));
 }
 
 static mrb_value
@@ -583,7 +617,6 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c)
 {
   struct RProc *p;
   mrb_callinfo *ci;
-  mrb_int max = 3;
 
   if (mrb_nil_p(blk)) {
     mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
@@ -603,11 +636,10 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c)
     mrb->c->stack[0] = self;
     mrb->c->stack[1] = self;
     mrb->c->stack[2] = mrb_nil_value();
-    return p->body.func(mrb, self);
+    return MRB_PROC_CFUNC(p)(mrb, self);
   }
   ci->nregs = p->body.irep->nregs;
-  if (max < ci->nregs) max = ci->nregs;
-  stack_extend(mrb, max);
+  stack_extend(mrb, (ci->nregs < 3) ? 3 : ci->nregs);
   mrb->c->stack[0] = self;
   mrb->c->stack[1] = self;
   mrb->c->stack[2] = mrb_nil_value();
@@ -676,7 +708,9 @@ mrb_obj_instance_eval(mrb_state *mrb, mrb_value self)
   switch (mrb_type(self)) {
   case MRB_TT_SYMBOL:
   case MRB_TT_FIXNUM:
+#ifndef MRB_WITHOUT_FLOAT
   case MRB_TT_FLOAT:
+#endif
     c = 0;
     break;
   default:
@@ -707,18 +741,12 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value
   ci->mid = mid;
   ci->proc = p;
   ci->stackent = mrb->c->stack;
-  ci->argc = argc;
+  ci->argc = (int)argc;
   ci->target_class = c;
   ci->acc = CI_ACC_SKIP;
   mrb->c->stack = mrb->c->stack + n;
-  if (MRB_PROC_CFUNC_P(p)) {
-    ci->nregs = argc + 2;
-    stack_extend(mrb, ci->nregs);
-  }
-  else {
-    ci->nregs = p->body.irep->nregs;
-    stack_extend(mrb, ci->nregs);
-  }
+  ci->nregs = MRB_PROC_CFUNC_P(p) ? (int)(argc+2) : p->body.irep->nregs;
+  stack_extend(mrb, ci->nregs);
 
   mrb->c->stack[0] = self;
   if (argc > 0) {
@@ -727,15 +755,13 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value
   mrb->c->stack[argc+1] = mrb_nil_value();
 
   if (MRB_PROC_CFUNC_P(p)) {
-    val = p->body.func(mrb, self);
+    val = MRB_PROC_CFUNC(p)(mrb, self);
     mrb->c->stack = mrb->c->ci->stackent;
+    cipop(mrb);
   }
   else {
-    int cioff = mrb->c->ci - mrb->c->cibase;
     val = mrb_run(mrb, p, self);
-    mrb->c->ci = mrb->c->cibase + cioff;
   }
-  cipop(mrb);
   return val;
 }
 
@@ -744,7 +770,7 @@ mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv)
 {
   struct RProc *p = mrb_proc_ptr(b);
 
-  return mrb_yield_with_class(mrb, b, argc, argv, p->env->stack[0], p->target_class);
+  return mrb_yield_with_class(mrb, b, argc, argv, MRB_PROC_ENV(p)->stack[0], MRB_PROC_TARGET_CLASS(p));
 }
 
 MRB_API mrb_value
@@ -752,7 +778,7 @@ mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg)
 {
   struct RProc *p = mrb_proc_ptr(b);
 
-  return mrb_yield_with_class(mrb, b, 1, &arg, p->env->stack[0], p->target_class);
+  return mrb_yield_with_class(mrb, b, 1, &arg, MRB_PROC_ENV(p)->stack[0], MRB_PROC_TARGET_CLASS(p));
 }
 
 mrb_value
@@ -778,13 +804,37 @@ mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const
   return mrb_exec_irep(mrb, self, p);
 }
 
+mrb_value
+mrb_mod_s_nesting(mrb_state *mrb, mrb_value mod)
+{
+  struct RProc *proc;
+  mrb_value ary;
+  struct RClass *c = NULL;
+
+  mrb_get_args(mrb, "");
+  ary = mrb_ary_new(mrb);
+  proc = mrb->c->ci[-1].proc;   /* callee proc */
+  mrb_assert(!MRB_PROC_CFUNC_P(proc));
+  while (proc) {
+    if (MRB_PROC_SCOPE_P(proc)) {
+      struct RClass *c2 = MRB_PROC_TARGET_CLASS(proc);
+
+      if (c2 != c) {
+        c = c2;
+        mrb_ary_push(mrb, ary, mrb_obj_value(c));
+      }
+    }
+    proc = proc->upper;
+  }
+  return ary;
+}
+
 static struct RBreak*
 break_new(mrb_state *mrb, struct RProc *p, mrb_value val)
 {
   struct RBreak *brk;
 
   brk = (struct RBreak*)mrb_obj_alloc(mrb, MRB_TT_BREAK, NULL);
-  brk->iv = NULL;
   brk->proc = p;
   brk->val = val;
 
@@ -806,7 +856,7 @@ localjump_error(mrb_state *mrb, localjump_error_kind kind)
   mrb_value msg;
   mrb_value exc;
 
-  msg = mrb_str_buf_new(mrb, sizeof(lead) + 7);
+  msg = mrb_str_new_capa(mrb, sizeof(lead) + 7);
   mrb_str_cat(mrb, msg, lead, sizeof(lead) - 1);
   mrb_str_cat(mrb, msg, kind_str[kind], kind_str_len[kind]);
   exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg);
@@ -883,7 +933,7 @@ mrb_vm_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stac
   mrb_irep *irep = proc->body.irep;
   mrb_value result;
   struct mrb_context *c = mrb->c;
-  int cioff = c->ci - c->cibase;
+  ptrdiff_t cioff = c->ci - c->cibase;
   unsigned int nregs = irep->nregs;
 
   if (!c->stack) {
@@ -898,12 +948,7 @@ mrb_vm_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stac
   if (c->ci - c->cibase > cioff) {
     c->ci = c->cibase + cioff;
   }
-  if (mrb->c != c) {
-    if (mrb->c->fib) {
-      mrb_write_barrier(mrb, (struct RBasic*)mrb->c->fib);
-    }
-    mrb->c = c;
-  }
+  mrb->c = c;
   return result;
 }
 
@@ -980,9 +1025,11 @@ RETRY_TRY_BLOCK:
       int bx = GETARG_Bx(i);
 #ifdef MRB_WORD_BOXING
       mrb_value val = pool[bx];
+#ifndef MRB_WITHOUT_FLOAT
       if (mrb_float_p(val)) {
         val = mrb_float_value(mrb, mrb_float(val));
       }
+#endif
       regs[a] = val;
 #else
       regs[a] = pool[bx];
@@ -992,7 +1039,9 @@ RETRY_TRY_BLOCK:
 
     CASE(OP_LOADI) {
       /* A sBx  R(A) := sBx */
-      SET_INT_VALUE(regs[GETARG_A(i)], GETARG_sBx(i));
+      int a = GETARG_A(i);
+      mrb_int bx = GETARG_sBx(i);
+      SET_INT_VALUE(regs[a], bx);
       NEXT;
     }
 
@@ -1147,11 +1196,11 @@ RETRY_TRY_BLOCK:
       mrb_value *regs_a = regs + a;
       struct REnv *e = uvenv(mrb, c);
 
-      if (!e) {
-        *regs_a = mrb_nil_value();
+      if (e && b < MRB_ENV_STACK_LEN(e)) {
+        *regs_a = e->stack[b];
       }
       else {
-        *regs_a = e->stack[b];
+        *regs_a = mrb_nil_value();
       }
       NEXT;
     }
@@ -1296,12 +1345,39 @@ RETRY_TRY_BLOCK:
       int a = GETARG_A(i);
       mrb_callinfo *ci = mrb->c->ci;
       int n, epos = ci->epos;
+      mrb_value self = regs[0];
+      struct RClass *target_class = ci->target_class;
 
-      for (n=0; n<a && mrb->c->eidx > epos; n++) {
-        ecall(mrb, --mrb->c->eidx);
-        mrb_gc_arena_restore(mrb, ai);
+      if (mrb->c->eidx <= epos) {
+        NEXT;
       }
-      NEXT;
+
+      if (a > mrb->c->eidx - epos)
+        a = mrb->c->eidx - epos;
+      pc = pc + 1;
+      for (n=0; n<a; n++) {
+        proc = mrb->c->ensure[epos+n];
+        mrb->c->ensure[epos+n] = NULL;
+        if (proc == NULL) continue;
+        irep = proc->body.irep;
+        ci = cipush(mrb);
+        ci->mid = ci[-1].mid;
+        ci->argc = 0;
+        ci->proc = proc;
+        ci->stackent = mrb->c->stack;
+        ci->nregs = irep->nregs;
+        ci->target_class = target_class;
+        ci->pc = pc;
+        ci->acc = ci[-1].nregs;
+        mrb->c->stack += ci->acc;
+        stack_extend(mrb, ci->nregs);
+        regs[0] = self;
+        pc = irep->iseq;
+      }
+      pool = irep->pool;
+      syms = irep->syms;
+      mrb->c->eidx = epos;
+      JUMP;
     }
 
     CASE(OP_LOADNIL) {
@@ -1322,77 +1398,58 @@ RETRY_TRY_BLOCK:
       /* A B C  R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C)) */
       int a = GETARG_A(i);
       int n = GETARG_C(i);
-      struct RProc *m;
+      int argc = (n == CALL_MAXARGS) ? -1 : n;
+      int bidx = (argc < 0) ? a+2 : a+n+1;
+      mrb_method_t m;
       struct RClass *c;
       mrb_callinfo *ci = mrb->c->ci;
-      mrb_value recv, result;
+      mrb_value recv, blk;
       mrb_sym mid = syms[GETARG_B(i)];
-      int bidx;
-      mrb_value blk;
+
+      mrb_assert(bidx < ci->nregs);
 
       recv = regs[a];
-      if (n == CALL_MAXARGS) {
-        bidx = a+2;
-      }
-      else {
-        bidx = a+n+1;
-      }
       if (GET_OPCODE(i) != OP_SENDB) {
-        if (bidx >= ci->nregs) {
-          stack_extend(mrb, bidx+1);
-          ci->nregs = bidx+1;
-        }
         SET_NIL_VALUE(regs[bidx]);
-        blk = mrb_nil_value();
+        blk = regs[bidx];
       }
       else {
         blk = regs[bidx];
         if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) {
-          if (bidx >= ci->nregs) {
-            stack_extend(mrb, bidx+1);
-            ci->nregs = bidx+1;
-          }
-          result = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc");
-          blk = regs[bidx] = result;
+          blk = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc");
+          /* The stack might have been reallocated during mrb_convert_type(),
+             see #3622 */
+          regs[bidx] = blk;
         }
       }
       c = mrb_class(mrb, recv);
       m = mrb_method_search_vm(mrb, &c, mid);
-      if (!m) {
-        mrb_value sym = mrb_symbol_value(mid);
+      if (MRB_METHOD_UNDEF_P(m)) {
         mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
-
         m = mrb_method_search_vm(mrb, &c, missing);
-        if (!m) {
-          mrb_value args;
-
-          if (n == CALL_MAXARGS) {
-            args = regs[a+1];
-          }
-          else {
-            args = mrb_ary_new_from_values(mrb, n, regs+a+1);
-          }
+        if (MRB_METHOD_UNDEF_P(m) || (missing == mrb->c->ci->mid && mrb_obj_eq(mrb, regs[0], recv))) {
+          mrb_value args = (argc < 0) ? regs[a+1] : mrb_ary_new_from_values(mrb, n, regs+a+1);
           ERR_PC_SET(mrb, pc);
           mrb_method_missing(mrb, mid, recv, args);
         }
-        mid = missing;
-        if (n != CALL_MAXARGS) {
+        if (argc >= 0) {
           if (a+2 >= irep->nregs) {
             stack_extend(mrb, a+3);
           }
           regs[a+1] = mrb_ary_new_from_values(mrb, n, regs+a+1);
           regs[a+2] = blk;
-          n = CALL_MAXARGS;
+          argc = -1;
         }
-        mrb_ary_unshift(mrb, regs[a+1], sym);
+        mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(mid));
+        mid = missing;
       }
 
       /* push callinfo */
       ci = cipush(mrb);
       ci->mid = mid;
-      ci->proc = m;
       ci->stackent = mrb->c->stack;
       ci->target_class = c;
+      ci->argc = argc;
 
       ci->pc = pc + 1;
       ci->acc = a;
@@ -1400,16 +1457,17 @@ RETRY_TRY_BLOCK:
       /* prepare stack */
       mrb->c->stack += a;
 
-      if (MRB_PROC_CFUNC_P(m)) {
-        if (n == CALL_MAXARGS) {
-          ci->argc = -1;
-          ci->nregs = 3;
+      if (MRB_METHOD_CFUNC_P(m)) {
+        ci->nregs = (argc < 0) ? 3 : n+2;
+        if (MRB_METHOD_PROC_P(m)) {
+          struct RProc *p = MRB_METHOD_PROC(m);
+
+          ci->proc = p;
+          recv = p->body.func(mrb, recv);
         }
         else {
-          ci->argc = n;
-          ci->nregs = n + 2;
+          recv = MRB_METHOD_FUNC(m)(mrb, recv);
         }
-        result = m->body.func(mrb, recv);
         mrb_gc_arena_restore(mrb, ai);
         mrb_gc_arena_shrink(mrb, ai);
         if (mrb->exc) goto L_RAISE;
@@ -1417,8 +1475,7 @@ RETRY_TRY_BLOCK:
         if (GET_OPCODE(i) == OP_SENDB) {
           if (mrb_type(blk) == MRB_TT_PROC) {
             struct RProc *p = mrb_proc_ptr(blk);
-
-            if (p && !MRB_PROC_STRICT_P(p) && p->env == ci[-1].env) {
+            if (p && !MRB_PROC_STRICT_P(p) && MRB_PROC_ENV(p) == ci[-1].env) {
               p->flags |= MRB_PROC_ORPHAN;
             }
           }
@@ -1426,7 +1483,7 @@ RETRY_TRY_BLOCK:
         if (!ci->target_class) { /* return from context modifying method (resume/yield) */
           if (ci->acc == CI_ACC_RESUMED) {
             mrb->jmp = prev_jmp;
-            return result;
+            return recv;
           }
           else {
             mrb_assert(!MRB_PROC_CFUNC_P(ci[-1].proc));
@@ -1436,7 +1493,7 @@ RETRY_TRY_BLOCK:
             syms = irep->syms;
           }
         }
-        mrb->c->stack[0] = result;
+        mrb->c->stack[0] = recv;
         /* pop stackpos */
         mrb->c->stack = ci->stackent;
         pc = ci->pc;
@@ -1445,19 +1502,12 @@ RETRY_TRY_BLOCK:
       }
       else {
         /* setup environment for calling method */
-        proc = mrb->c->ci->proc = m;
-        irep = m->body.irep;
+        proc = ci->proc = MRB_METHOD_PROC(m);
+        irep = proc->body.irep;
         pool = irep->pool;
         syms = irep->syms;
         ci->nregs = irep->nregs;
-        if (n == CALL_MAXARGS) {
-          ci->argc = -1;
-          stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs);
-        }
-        else {
-          ci->argc = n;
-          stack_extend(mrb, irep->nregs);
-        }
+        stack_extend(mrb, (argc < 0 && ci->nregs < 3) ? 3 : ci->nregs);
         pc = irep->iseq;
         JUMP;
       }
@@ -1477,26 +1527,22 @@ RETRY_TRY_BLOCK:
 
       /* replace callinfo */
       ci = mrb->c->ci;
-      ci->target_class = m->target_class;
+      ci->target_class = MRB_PROC_TARGET_CLASS(m);
       ci->proc = m;
-      if (m->env) {
+      if (MRB_PROC_ENV_P(m)) {
         mrb_sym mid;
+        struct REnv *e = MRB_PROC_ENV(m);
 
-        if (MRB_ENV_STACK_SHARED_P(m->env)) {
-          mid = m->env->cxt.c->cibase[m->env->cioff].mid;
-        }
-        else {
-          mid = m->env->cxt.mid;
-        }
+        mid = e->mid;
         if (mid) ci->mid = mid;
-        if (!m->env->stack) {
-          m->env->stack = mrb->c->stack;
+        if (!e->stack) {
+          e->stack = mrb->c->stack;
         }
       }
 
       /* prepare stack */
       if (MRB_PROC_CFUNC_P(m)) {
-        recv = m->body.func(mrb, recv);
+        recv = MRB_PROC_CFUNC(m)(mrb, recv);
         mrb_gc_arena_restore(mrb, ai);
         mrb_gc_arena_shrink(mrb, ai);
         if (mrb->exc) goto L_RAISE;
@@ -1522,7 +1568,7 @@ RETRY_TRY_BLOCK:
         pool = irep->pool;
         syms = irep->syms;
         ci->nregs = irep->nregs;
-        stack_extend(mrb, irep->nregs);
+        stack_extend(mrb, ci->nregs);
         if (ci->argc < 0) {
           if (irep->nregs > 3) {
             stack_clear(regs+3, irep->nregs-3);
@@ -1531,8 +1577,8 @@ RETRY_TRY_BLOCK:
         else if (ci->argc+2 < irep->nregs) {
           stack_clear(regs+ci->argc+2, irep->nregs-ci->argc-2);
         }
-        if (m->env) {
-          regs[0] = m->env->stack[0];
+        if (MRB_PROC_ENV_P(m)) {
+          regs[0] = MRB_PROC_ENV(m)->stack[0];
         }
         pc = irep->iseq;
         JUMP;
@@ -1541,101 +1587,92 @@ RETRY_TRY_BLOCK:
 
     CASE(OP_SUPER) {
       /* A C  R(A) := super(R(A+1),... ,R(A+C+1)) */
-      mrb_value recv;
-      mrb_callinfo *ci = mrb->c->ci;
-      struct RProc *m;
-      struct RClass *c;
-      mrb_sym mid = ci->mid;
       int a = GETARG_A(i);
       int n = GETARG_C(i);
-      mrb_value blk;
-      int bidx;
+      int argc = (n == CALL_MAXARGS) ? -1 : n;
+      int bidx = (argc < 0) ? a+2 : a+n+1;
+      mrb_method_t m;
+      struct RClass *c;
+      mrb_callinfo *ci = mrb->c->ci;
+      mrb_value recv, blk;
+      mrb_sym mid = ci->mid;
+      struct RClass* target_class = MRB_PROC_TARGET_CLASS(ci->proc);
 
-      if (mid == 0 || !ci->target_class) {
-        mrb_value exc;
+      mrb_assert(bidx < ci->nregs);
 
-        exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method");
+      if (mid == 0 || !target_class) {
+        mrb_value exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method");
         mrb_exc_set(mrb, exc);
         goto L_RAISE;
       }
+      if (target_class->tt == MRB_TT_MODULE) {
+        target_class = ci->target_class;
+        if (target_class->tt != MRB_TT_ICLASS) {
+          mrb_value exc = mrb_exc_new_str_lit(mrb, E_RUNTIME_ERROR, "superclass info lost [mruby limitations]");
+          mrb_exc_set(mrb, exc);
+          goto L_RAISE;
+        }
+      }
       recv = regs[0];
-      c = mrb->c->ci->target_class->super;
+      if (!mrb_obj_is_kind_of(mrb, recv, target_class)) {
+        mrb_value exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR,
+                                            "self has wrong type to call super in this context");
+        mrb_exc_set(mrb, exc);
+        goto L_RAISE;
+      }
+      blk = regs[bidx];
+      if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) {
+        blk = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc");
+        /* The stack or ci stack might have been reallocated during
+           mrb_convert_type(), see #3622 and #3784 */
+        regs[bidx] = blk;
+        ci = mrb->c->ci;
+      }
+      c = target_class->super;
       m = mrb_method_search_vm(mrb, &c, mid);
-      if (!m) {
+      if (MRB_METHOD_UNDEF_P(m)) {
         mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
-        m = mrb_method_search_vm(mrb, &c, missing);
-        if (!m) {
-          mrb_value args;
 
-          if (n == CALL_MAXARGS) {
-            args = regs[a+1];
-          }
-          else {
-            args = mrb_ary_new_from_values(mrb, n, regs+a+1);
-          }
+        if (mid != missing) {
+          c = mrb_class(mrb, recv);
+        }
+        m = mrb_method_search_vm(mrb, &c, missing);
+        if (MRB_METHOD_UNDEF_P(m)) {
+          mrb_value args = (argc < 0) ? regs[a+1] : mrb_ary_new_from_values(mrb, n, regs+a+1);
           ERR_PC_SET(mrb, pc);
           mrb_method_missing(mrb, mid, recv, args);
         }
         mid = missing;
-        if (n == CALL_MAXARGS-1) {
+        if (argc >= 0) {
+          if (a+2 >= ci->nregs) {
+            stack_extend(mrb, a+3);
+          }
           regs[a+1] = mrb_ary_new_from_values(mrb, n, regs+a+1);
-          n++;
-        }
-        if (n == CALL_MAXARGS) {
-          mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(ci->mid));
-        }
-        else {
-          value_move(regs+a+2, regs+a+1, ++n);
-          SET_SYM_VALUE(regs[a+1], ci->mid);
-        }
-      }
-
-      if (n == CALL_MAXARGS) {
-        bidx = a+2;
-      }
-      else {
-        bidx = a+n+1;
-      }
-      blk = regs[bidx];
-      if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) {
-        mrb_value result;
-
-        if (bidx >= ci->nregs) {
-          stack_extend(mrb, bidx+1);
-          ci->nregs = bidx+1;
+          regs[a+2] = blk;
+          argc = -1;
         }
-        result = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc");
-        regs[bidx] = result;
+        mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(ci->mid));
       }
 
       /* push callinfo */
       ci = cipush(mrb);
       ci->mid = mid;
-      ci->proc = m;
       ci->stackent = mrb->c->stack;
       ci->target_class = c;
       ci->pc = pc + 1;
-      if (n == CALL_MAXARGS) {
-        ci->argc = -1;
-      }
-      else {
-        ci->argc = n;
-      }
+      ci->argc = argc;
 
       /* prepare stack */
       mrb->c->stack += a;
       mrb->c->stack[0] = recv;
 
-      if (MRB_PROC_CFUNC_P(m)) {
+      if (MRB_METHOD_CFUNC_P(m)) {
         mrb_value v;
-
-        if (n == CALL_MAXARGS) {
-          ci->nregs = 3;
+        ci->nregs = (argc < 0) ? 3 : n+2;
+        if (MRB_METHOD_PROC_P(m)) {
+          ci->proc = MRB_METHOD_PROC(m);
         }
-        else {
-          ci->nregs = n + 2;
-        }
-        v = m->body.func(mrb, recv);
+        v = MRB_METHOD_CFUNC(m)(mrb, recv);
         mrb_gc_arena_restore(mrb, ai);
         if (mrb->exc) goto L_RAISE;
         ci = mrb->c->ci;
@@ -1664,17 +1701,12 @@ RETRY_TRY_BLOCK:
         ci->acc = a;
 
         /* setup environment for calling method */
-        ci->proc = m;
-        irep = m->body.irep;
+        proc = ci->proc = MRB_METHOD_PROC(m);
+        irep = proc->body.irep;
         pool = irep->pool;
         syms = irep->syms;
         ci->nregs = irep->nregs;
-        if (n == CALL_MAXARGS) {
-          stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs);
-        }
-        else {
-          stack_extend(mrb, irep->nregs);
-        }
+        stack_extend(mrb, (argc < 0 && ci->nregs < 3) ? 3 : ci->nregs);
         pc = irep->iseq;
         JUMP;
       }
@@ -1718,7 +1750,7 @@ RETRY_TRY_BLOCK:
           struct RArray *ary = mrb_ary_ptr(stack[m1]);
 
           pp = ARY_PTR(ary);
-          len = ARY_LEN(ary);
+          len = (int)ARY_LEN(ary);
         }
         regs[a] = mrb_ary_new_capa(mrb, m1+len+m2);
         rest = mrb_ary_ptr(regs[a]);
@@ -1760,7 +1792,7 @@ RETRY_TRY_BLOCK:
       if (argc < 0) {
         struct RArray *ary = mrb_ary_ptr(regs[1]);
         argv = ARY_PTR(ary);
-        argc = ARY_LEN(ary);
+        argc = (int)ARY_LEN(ary);
         mrb_gc_protect(mrb, regs[1]);
       }
       if (mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc)) {
@@ -1773,7 +1805,7 @@ RETRY_TRY_BLOCK:
       }
       else if (len > 1 && argc == 1 && mrb_array_p(argv[0])) {
         mrb_gc_protect(mrb, argv[0]);
-        argc = RARRAY_LEN(argv[0]);
+        argc = (int)RARRAY_LEN(argv[0]);
         argv = RARRAY_PTR(argv[0]);
       }
       if (argc < len) {
@@ -1852,6 +1884,12 @@ RETRY_TRY_BLOCK:
       /* A B     return R(A) (B=normal,in-block return/break) */
       mrb_callinfo *ci;
 
+#define ecall_adjust() do {\
+  ptrdiff_t cioff = ci - mrb->c->cibase;\
+  ecall(mrb);\
+  ci = mrb->c->cibase + cioff;\
+} while (0)
+
       ci = mrb->c->ci;
       if (ci->mid) {
         mrb_value blk;
@@ -1865,8 +1903,8 @@ RETRY_TRY_BLOCK:
         if (mrb_type(blk) == MRB_TT_PROC) {
           struct RProc *p = mrb_proc_ptr(blk);
 
-          if (!MRB_PROC_STRICT_P(proc) &&
-              ci > mrb->c->cibase && p->env == ci[-1].env) {
+          if (!MRB_PROC_STRICT_P(p) &&
+              ci > mrb->c->cibase && MRB_PROC_ENV(p) == ci[-1].env) {
             p->flags |= MRB_PROC_ORPHAN;
           }
         }
@@ -1874,7 +1912,6 @@ RETRY_TRY_BLOCK:
 
       if (mrb->exc) {
         mrb_callinfo *ci0;
-        mrb_value *stk;
 
       L_RAISE:
         ci0 = ci = mrb->c->ci;
@@ -1882,7 +1919,6 @@ RETRY_TRY_BLOCK:
           if (ci->ridx == 0) goto L_FTOP;
           goto L_RESCUE;
         }
-        stk = mrb->c->stack;
         while (ci[0].ridx == ci[-1].ridx) {
           cipop(mrb);
           mrb->c->stack = ci->stackent;
@@ -1892,7 +1928,6 @@ RETRY_TRY_BLOCK:
           }
           ci = mrb->c->ci;
           if (ci == mrb->c->cibase) {
-            mrb->c->stack = stk;
             if (ci->ridx == 0) {
             L_FTOP:             /* fiber top */
               if (mrb->c == mrb->root_c) {
@@ -1902,9 +1937,10 @@ RETRY_TRY_BLOCK:
               else {
                 struct mrb_context *c = mrb->c;
 
-                if (c->fib) {
-                  mrb_write_barrier(mrb, (struct RBasic*)c->fib);
+                while (c->eidx > ci->epos) {
+                  ecall_adjust();
                 }
+                mrb->c->status = MRB_FIBER_TERMINATED;
                 mrb->c = c->prev;
                 c->prev = NULL;
                 goto L_RAISE;
@@ -1914,13 +1950,8 @@ RETRY_TRY_BLOCK:
           }
           /* call ensure only when we skip this callinfo */
           if (ci[0].ridx == ci[-1].ridx) {
-           mrb_value *org_stbase = mrb->c->stbase;
             while (mrb->c->eidx > ci->epos) {
-              ecall(mrb, --mrb->c->eidx);
-              ci = mrb->c->ci;
-             if (org_stbase != mrb->c->stbase) {
-               stk = mrb->c->stack;
-             }
+              ecall_adjust();
             }
           }
         }
@@ -1930,7 +1961,7 @@ RETRY_TRY_BLOCK:
         irep = proc->body.irep;
         pool = irep->pool;
         syms = irep->syms;
-        if (ci != ci0) {
+        if (ci < ci0) {
           mrb->c->stack = ci[1].stackent;
         }
         stack_extend(mrb, irep->nregs);
@@ -1939,42 +1970,45 @@ RETRY_TRY_BLOCK:
       else {
         int acc;
         mrb_value v;
+        struct RProc *dst;
 
+        ci = mrb->c->ci;
         v = regs[GETARG_A(i)];
         mrb_gc_protect(mrb, v);
         switch (GETARG_B(i)) {
         case OP_R_RETURN:
           /* Fall through to OP_R_NORMAL otherwise */
-          if (ci->acc >=0 && proc->env && !MRB_PROC_STRICT_P(proc)) {
-            struct REnv *e = top_env(mrb, proc);
-            mrb_callinfo *ce;
+          if (ci->acc >=0 && MRB_PROC_ENV_P(proc) && !MRB_PROC_STRICT_P(proc)) {
+            mrb_callinfo *cibase = mrb->c->cibase;
+            dst = top_proc(mrb, proc);
 
-            if (!MRB_ENV_STACK_SHARED_P(e) || e->cxt.c != mrb->c) {
-              localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
-              goto L_RAISE;
+            if (MRB_PROC_ENV_P(dst)) {
+              struct REnv *e = MRB_PROC_ENV(dst);
+
+              if (!MRB_ENV_STACK_SHARED_P(e) || e->cxt != mrb->c) {
+                localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
+                goto L_RAISE;
+              }
             }
-            
-            ce = mrb->c->cibase + e->cioff;
-            while (ci > ce) {
-              mrb_env_unshare(mrb, ci->env);
+            while (cibase <= ci && ci->proc != dst) {
               if (ci->acc < 0) {
                 localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
                 goto L_RAISE;
               }
               ci--;
             }
-            mrb_env_unshare(mrb, ci->env);
-            if (ce == mrb->c->cibase) {
+            if (ci <= cibase) {
               localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
               goto L_RAISE;
             }
-            mrb->c->stack = mrb->c->ci->stackent;
-            mrb->c->ci = ce;
             break;
           }
+          /* fallthrough */
         case OP_R_NORMAL:
         NORMAL_RETURN:
           if (ci == mrb->c->cibase) {
+            struct mrb_context *c;
+
             if (!mrb->c->prev) { /* toplevel return */
               localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
               goto L_RAISE;
@@ -1985,14 +2019,16 @@ RETRY_TRY_BLOCK:
               goto L_RAISE;
             }
             while (mrb->c->eidx > 0) {
-              ecall(mrb, --mrb->c->eidx);
+              ecall(mrb);
             }
             /* automatic yield at the end */
-            mrb->c->status = MRB_FIBER_TERMINATED;
-            mrb->c = mrb->c->prev;
+            c = mrb->c;
+            c->status = MRB_FIBER_TERMINATED;
+            mrb->c = c->prev;
+            c->prev = NULL;
             mrb->c->status = MRB_FIBER_RUNNING;
+            ci = mrb->c->ci;
           }
-          ci = mrb->c->ci;
           break;
         case OP_R_BREAK:
           if (MRB_PROC_STRICT_P(proc)) goto NORMAL_RETURN;
@@ -2005,17 +2041,21 @@ RETRY_TRY_BLOCK:
             mrb_exc_set(mrb, exc);
             goto L_RAISE;
           }
-          if (!proc->env || !MRB_ENV_STACK_SHARED_P(proc->env)) {
+          if (!MRB_PROC_ENV_P(proc) || !MRB_ENV_STACK_SHARED_P(MRB_PROC_ENV(proc))) {
             goto L_BREAK_ERROR;
           }
-          if (proc->env->cxt.c != mrb->c) {
-            goto L_BREAK_ERROR;
+          else {
+            struct REnv *e = MRB_PROC_ENV(proc);
+
+            if (e->cxt != mrb->c) {
+              goto L_BREAK_ERROR;
+            }
           }
           while (mrb->c->eidx > mrb->c->ci->epos) {
-            ecall(mrb, --mrb->c->eidx);
+            ecall_adjust();
           }
           /* break from fiber block */
-          if (mrb->c->ci == mrb->c->cibase && mrb->c->ci->pc) {
+          if (ci == mrb->c->cibase && ci->pc) {
             struct mrb_context *c = mrb->c;
 
             mrb->c = c->prev;
@@ -2037,31 +2077,37 @@ RETRY_TRY_BLOCK:
             ci = mrb->c->ci;
           }
           mrb->c->stack = ci->stackent;
-          mrb->c->ci = mrb->c->cibase + proc->env->cioff + 1;
-          while (ci > mrb->c->ci) {
-            mrb_env_unshare(mrb, ci->env);
+          proc = proc->upper;
+          while (mrb->c->cibase < ci &&  ci[-1].proc != proc) {
             if (ci[-1].acc == CI_ACC_SKIP) {
-              mrb->c->ci = ci;
+              while (ci < mrb->c->ci) {
+                cipop(mrb);
+              }
               goto L_BREAK_ERROR;
             }
             ci--;
           }
-          mrb_env_unshare(mrb, ci->env);
+          if (ci == mrb->c->cibase) {
+            goto L_BREAK_ERROR;
+          }
           break;
         default:
           /* cannot happen */
           break;
         }
-        while (mrb->c->eidx > mrb->c->ci->epos) {
-          ecall(mrb, --mrb->c->eidx);
+        while (ci < mrb->c->ci) {
+          cipop(mrb);
+        }
+        ci[0].ridx = ci[-1].ridx;
+        while (mrb->c->eidx > ci->epos) {
+          ecall_adjust();
         }
-        if (mrb->c->vmexec && !mrb->c->ci->target_class) {
+        if (mrb->c->vmexec && !ci->target_class) {
           mrb_gc_arena_restore(mrb, ai);
           mrb->c->vmexec = FALSE;
           mrb->jmp = prev_jmp;
           return v;
         }
-        ci = mrb->c->ci;
         acc = ci->acc;
         mrb->c->stack = ci->stackent;
         cipop(mrb);
@@ -2071,6 +2117,7 @@ RETRY_TRY_BLOCK:
           return v;
         }
         pc = ci->pc;
+        ci = mrb->c->ci;
         DEBUG(fprintf(stderr, "from :%s\n", mrb_sym2name(mrb, ci->mid)));
         proc = mrb->c->ci->proc;
         irep = proc->body.irep;
@@ -2086,21 +2133,22 @@ RETRY_TRY_BLOCK:
     CASE(OP_TAILCALL) {
       /* A B C  return call(R(A),Syms(B),R(A+1),... ,R(A+C+1)) */
       int a = GETARG_A(i);
+      int b = GETARG_B(i);
       int n = GETARG_C(i);
-      struct RProc *m;
+      mrb_method_t m;
       struct RClass *c;
       mrb_callinfo *ci;
       mrb_value recv;
-      mrb_sym mid = syms[GETARG_B(i)];
+      mrb_sym mid = syms[b];
 
       recv = regs[a];
       c = mrb_class(mrb, recv);
       m = mrb_method_search_vm(mrb, &c, mid);
-      if (!m) {
+      if (MRB_METHOD_UNDEF_P(m)) {
         mrb_value sym = mrb_symbol_value(mid);
         mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
         m = mrb_method_search_vm(mrb, &c, missing);
-        if (!m) {
+        if (MRB_METHOD_UNDEF_P(m)) {
           mrb_value args;
 
           if (n == CALL_MAXARGS) {
@@ -2136,15 +2184,16 @@ RETRY_TRY_BLOCK:
       /* move stack */
       value_move(mrb->c->stack, &regs[a], ci->argc+1);
 
-      if (MRB_PROC_CFUNC_P(m)) {
-        mrb_value v = m->body.func(mrb, recv);
+      if (MRB_METHOD_CFUNC_P(m)) {
+        mrb_value v = MRB_METHOD_CFUNC(m)(mrb, recv);
         mrb->c->stack[0] = v;
         mrb_gc_arena_restore(mrb, ai);
         goto L_RETURN;
       }
       else {
         /* setup environment for calling method */
-        irep = m->body.irep;
+        struct RProc *p = MRB_METHOD_PROC(m);
+        irep = p->body.irep;
         pool = irep->pool;
         syms = irep->syms;
         if (ci->argc < 0) {
@@ -2171,8 +2220,7 @@ RETRY_TRY_BLOCK:
       if (lv == 0) stack = regs + 1;
       else {
         struct REnv *e = uvenv(mrb, lv-1);
-        if (!e || e->cioff == 0 ||
-            (!MRB_ENV_STACK_SHARED_P(e) && e->cxt.mid == 0) ||
+        if (!e || (!MRB_ENV_STACK_SHARED_P(e) && e->mid == 0) ||
             MRB_ENV_STACK_LEN(e) <= m1+r+m2+1) {
           localjump_error(mrb, LOCALJUMP_ERROR_YIELD);
           goto L_RAISE;
@@ -2206,12 +2254,15 @@ RETRY_TRY_BLOCK:
           x = mrb_fixnum(regs_a[0]);
           y = mrb_fixnum(regs_a[1]);
           if (mrb_int_add_overflow(x, y, &z)) {
+#ifndef MRB_WITHOUT_FLOAT
             SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x + (mrb_float)y);
             break;
+#endif
           }
           SET_INT_VALUE(regs[a], z);
         }
         break;
+#ifndef MRB_WITHOUT_FLOAT
       case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
         {
           mrb_int x = mrb_fixnum(regs[a]);
@@ -2241,6 +2292,7 @@ RETRY_TRY_BLOCK:
         OP_MATH_BODY(+,mrb_float,mrb_float);
 #endif
         break;
+#endif
       case TYPES2(MRB_TT_STRING,MRB_TT_STRING):
         regs[a] = mrb_str_plus(mrb, regs[a], regs[a+1]);
         break;
@@ -2264,12 +2316,15 @@ RETRY_TRY_BLOCK:
           x = mrb_fixnum(regs[a]);
           y = mrb_fixnum(regs[a+1]);
           if (mrb_int_sub_overflow(x, y, &z)) {
+#ifndef MRB_WITHOUT_FLOAT
             SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - (mrb_float)y);
             break;
+#endif
           }
           SET_INT_VALUE(regs[a], z);
         }
         break;
+#ifndef MRB_WITHOUT_FLOAT
       case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
         {
           mrb_int x = mrb_fixnum(regs[a]);
@@ -2299,6 +2354,7 @@ RETRY_TRY_BLOCK:
         OP_MATH_BODY(-,mrb_float,mrb_float);
 #endif
         break;
+#endif
       default:
         goto L_SEND;
       }
@@ -2318,12 +2374,15 @@ RETRY_TRY_BLOCK:
           x = mrb_fixnum(regs[a]);
           y = mrb_fixnum(regs[a+1]);
           if (mrb_int_mul_overflow(x, y, &z)) {
+#ifndef MRB_WITHOUT_FLOAT
             SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x * (mrb_float)y);
             break;
+#endif
           }
           SET_INT_VALUE(regs[a], z);
         }
         break;
+#ifndef MRB_WITHOUT_FLOAT
       case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
         {
           mrb_int x = mrb_fixnum(regs[a]);
@@ -2353,6 +2412,7 @@ RETRY_TRY_BLOCK:
         OP_MATH_BODY(*,mrb_float,mrb_float);
 #endif
         break;
+#endif
       default:
         goto L_SEND;
       }
@@ -2362,67 +2422,51 @@ RETRY_TRY_BLOCK:
     CASE(OP_DIV) {
       /* A B C  R(A) := R(A)/R(A+1) (Syms[B]=:/,C=1)*/
       int a = GETARG_A(i);
+#ifndef MRB_WITHOUT_FLOAT
+      double x, y, f;
+#endif
 
       /* need to check if op is overridden */
       switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {
       case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):
+#ifdef MRB_WITHOUT_FLOAT
         {
           mrb_int x = mrb_fixnum(regs[a]);
           mrb_int y = mrb_fixnum(regs[a+1]);
-          double f;
-          if (y == 0 && x != 0) {
-            f = INFINITY;
-          }
-          else {
-            f = (mrb_float)x / (mrb_float)y;
-          }
-          SET_FLOAT_VALUE(mrb, regs[a], f);
+          SET_INT_VALUE(regs[a], y ? x / y : 0);
         }
         break;
+#else
+        x = (mrb_float)mrb_fixnum(regs[a]);
+        y = (mrb_float)mrb_fixnum(regs[a+1]);
+        break;
       case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
-        {
-          mrb_int x = mrb_fixnum(regs[a]);
-          mrb_float y = mrb_float(regs[a+1]);
-          SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x / y);
-        }
+        x = (mrb_float)mrb_fixnum(regs[a]);
+        y = mrb_float(regs[a+1]);
         break;
       case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
-#ifdef MRB_WORD_BOXING
-        {
-          mrb_float x = mrb_float(regs[a]);
-          mrb_int y = mrb_fixnum(regs[a+1]);
-          double f;
-          if (y == 0) {
-            f = INFINITY;
-          }
-          else {
-            f = x / y;
-          }
-          SET_FLOAT_VALUE(mrb, regs[a], f);
-        }
-#else
-        OP_MATH_BODY(/,mrb_float,mrb_fixnum);
-#endif
+        x = mrb_float(regs[a]);
+        y = (mrb_float)mrb_fixnum(regs[a+1]);
         break;
       case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
-#ifdef MRB_WORD_BOXING
-        {
-          mrb_float x = mrb_float(regs[a]);
-          mrb_float y = mrb_float(regs[a+1]);
-          SET_FLOAT_VALUE(mrb, regs[a], x / y);
-        }
-#else
-        OP_MATH_BODY(/,mrb_float,mrb_float);
-#endif
+        x = mrb_float(regs[a]);
+        y = mrb_float(regs[a+1]);
         break;
+#endif
       default:
         goto L_SEND;
       }
-#ifdef MRB_NAN_BOXING
-      if (isnan(mrb_float(regs[a]))) {
-        mrb_value v = mrb_float_value(mrb, mrb_float(regs[a]));
-        regs[a] = v;
+
+#ifndef MRB_WITHOUT_FLOAT
+      if (y == 0) {
+        if (x > 0) f = INFINITY;
+        else if (x < 0) f = -INFINITY;
+        else /* if (x == 0) */ f = NAN;
+      }
+      else {
+        f = x / y;
       }
+      SET_FLOAT_VALUE(mrb, regs[a], f);
 #endif
       NEXT;
     }
@@ -2440,12 +2484,15 @@ RETRY_TRY_BLOCK:
           mrb_int z;
 
           if (mrb_int_add_overflow(x, y, &z)) {
+#ifndef MRB_WITHOUT_FLOAT
             SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + (mrb_float)y);
             break;
+#endif
           }
           SET_INT_VALUE(regs[a], z);
         }
         break;
+#ifndef MRB_WITHOUT_FLOAT
       case MRB_TT_FLOAT:
 #ifdef MRB_WORD_BOXING
         {
@@ -2456,6 +2503,7 @@ RETRY_TRY_BLOCK:
         mrb_float(regs[a]) += GETARG_C(i);
 #endif
         break;
+#endif
       default:
         SET_INT_VALUE(regs[a+1], GETARG_C(i));
         i = MKOP_ABC(OP_SEND, a, GETARG_B(i), 1);
@@ -2478,13 +2526,15 @@ RETRY_TRY_BLOCK:
           mrb_int z;
 
           if (mrb_int_sub_overflow(x, y, &z)) {
+#ifndef MRB_WITHOUT_FLOAT
             SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x - (mrb_float)y);
+            break;
+#endif
           }
-          else {
-            SET_INT_VALUE(regs_a[0], z);
-          }
+          SET_INT_VALUE(regs_a[0], z);
         }
         break;
+#ifndef MRB_WITHOUT_FLOAT
       case MRB_TT_FLOAT:
 #ifdef MRB_WORD_BOXING
         {
@@ -2495,6 +2545,7 @@ RETRY_TRY_BLOCK:
         mrb_float(regs_a[0]) -= GETARG_C(i);
 #endif
         break;
+#endif
       default:
         SET_INT_VALUE(regs_a[1], GETARG_C(i));
         i = MKOP_ABC(OP_SEND, a, GETARG_B(i), 1);
@@ -2505,6 +2556,25 @@ RETRY_TRY_BLOCK:
 
 #define OP_CMP_BODY(op,v1,v2) (v1(regs[a]) op v2(regs[a+1]))
 
+#ifdef MRB_WITHOUT_FLOAT
+#define OP_CMP(op) do {\
+  int result;\
+  /* need to check if - is overridden */\
+  switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {\
+  case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):\
+    result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\
+    break;\
+  default:\
+    goto L_SEND;\
+  }\
+  if (result) {\
+    SET_TRUE_VALUE(regs[a]);\
+  }\
+  else {\
+    SET_FALSE_VALUE(regs[a]);\
+  }\
+} while(0)
+#else
 #define OP_CMP(op) do {\
   int result;\
   /* need to check if - is overridden */\
@@ -2531,6 +2601,7 @@ RETRY_TRY_BLOCK:
     SET_FALSE_VALUE(regs[a]);\
   }\
 } while(0)
+#endif
 
     CASE(OP_EQ) {
       /* A B C  R(A) := R(A)==R(A+1) (Syms[B]=:==,C=1)*/
@@ -2645,7 +2716,7 @@ RETRY_TRY_BLOCK:
         v = mrb_ary_new_from_values(mrb, 1, &regs[a]);
       }
       ary = mrb_ary_ptr(v);
-      len = ARY_LEN(ary);
+      len = (int)ARY_LEN(ary);
       if (len > pre + post) {
         v = mrb_ary_new_from_values(mrb, len - pre - post, ARY_PTR(ary)+pre);
         regs[a++] = v;
@@ -2670,15 +2741,21 @@ RETRY_TRY_BLOCK:
 
     CASE(OP_STRING) {
       /* A Bx           R(A) := str_new(Lit(Bx)) */
-      mrb_value str = mrb_str_dup(mrb, pool[GETARG_Bx(i)]);
-      regs[GETARG_A(i)] = str;
+      mrb_int a = GETARG_A(i);
+      mrb_int bx = GETARG_Bx(i);
+      mrb_value str = mrb_str_dup(mrb, pool[bx]);
+
+      regs[a] = str;
       mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
     CASE(OP_STRCAT) {
       /* A B    R(A).concat(R(B)) */
-      mrb_str_concat(mrb, regs[GETARG_A(i)], regs[GETARG_B(i)]);
+      mrb_int a = GETARG_A(i);
+      mrb_int b = GETARG_B(i);
+
+      mrb_str_concat(mrb, regs[a], regs[b]);
       NEXT;
     }
 
@@ -2701,16 +2778,20 @@ RETRY_TRY_BLOCK:
     CASE(OP_LAMBDA) {
       /* A b c  R(A) := lambda(SEQ[b],c) (b:c = 14:2) */
       struct RProc *p;
+      int a = GETARG_A(i);
+      int b = GETARG_b(i);
       int c = GETARG_c(i);
+      mrb_irep *nirep = irep->reps[b];
 
       if (c & OP_L_CAPTURE) {
-        p = mrb_closure_new(mrb, irep->reps[GETARG_b(i)]);
+        p = mrb_closure_new(mrb, nirep);
       }
       else {
-        p = mrb_proc_new(mrb, irep->reps[GETARG_b(i)]);
+        p = mrb_proc_new(mrb, nirep);
+        p->flags |= MRB_PROC_SCOPE;
       }
       if (c & OP_L_STRICT) p->flags |= MRB_PROC_STRICT;
-      regs[GETARG_A(i)] = mrb_obj_value(p);
+      regs[a] = mrb_obj_value(p);
       mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
@@ -2731,9 +2812,7 @@ RETRY_TRY_BLOCK:
       base = regs[a];
       super = regs[a+1];
       if (mrb_nil_p(base)) {
-        baseclass = mrb->c->ci->proc->target_class;
-        if (!baseclass) baseclass = mrb->c->ci->target_class;
-
+        baseclass = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
         base = mrb_obj_value(baseclass);
       }
       c = mrb_vm_define_class(mrb, base, super, id);
@@ -2751,9 +2830,7 @@ RETRY_TRY_BLOCK:
 
       base = regs[a];
       if (mrb_nil_p(base)) {
-        baseclass = mrb->c->ci->proc->target_class;
-        if (!baseclass) baseclass = mrb->c->ci->target_class;
-
+        baseclass = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
         base = mrb_obj_value(baseclass);
       }
       c = mrb_vm_define_module(mrb, base, id);
@@ -2765,15 +2842,20 @@ RETRY_TRY_BLOCK:
     CASE(OP_EXEC) {
       /* A Bx   R(A) := blockexec(R(A),SEQ[Bx]) */
       int a = GETARG_A(i);
+      int bx = GETARG_Bx(i);
       mrb_callinfo *ci;
       mrb_value recv = regs[a];
       struct RProc *p;
+      mrb_irep *nirep = irep->reps[bx];
 
       /* prepare closure */
-      p = mrb_closure_new(mrb, irep->reps[GETARG_Bx(i)]);
+      p = mrb_proc_new(mrb, nirep);
       p->c = NULL;
+      mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)proc);
+      MRB_PROC_SET_TARGET_CLASS(p, mrb_class_ptr(recv));
+      p->flags |= MRB_PROC_SCOPE;
 
-      /* prepare stack */
+      /* prepare call stack */
       ci = cipush(mrb);
       ci->pc = pc + 1;
       ci->acc = a;
@@ -2785,16 +2867,15 @@ RETRY_TRY_BLOCK:
       /* prepare stack */
       mrb->c->stack += a;
 
-      /* setup closure */
-      p->target_class = ci->target_class;
+      /* setup block to call */
       ci->proc = p;
 
       irep = p->body.irep;
       pool = irep->pool;
       syms = irep->syms;
-      stack_extend(mrb, irep->nregs);
-      stack_clear(regs+1, irep->nregs-1);
       ci->nregs = irep->nregs;
+      stack_extend(mrb, ci->nregs);
+      stack_clear(regs+1, ci->nregs-1);
       pc = irep->iseq;
       JUMP;
     }
@@ -2804,15 +2885,20 @@ RETRY_TRY_BLOCK:
       int a = GETARG_A(i);
       struct RClass *c = mrb_class_ptr(regs[a]);
       struct RProc *p = mrb_proc_ptr(regs[a+1]);
+      mrb_method_t m;
 
-      mrb_define_method_raw(mrb, c, syms[GETARG_B(i)], p);
+      MRB_METHOD_FROM_PROC(m, p);
+      mrb_define_method_raw(mrb, c, syms[GETARG_B(i)], m);
       mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
 
     CASE(OP_SCLASS) {
       /* A B    R(A) := R(B).singleton_class */
-      regs[GETARG_A(i)] = mrb_singleton_class(mrb, regs[GETARG_B(i)]);
+      int a = GETARG_A(i);
+      int b = GETARG_B(i);
+
+      regs[a] = mrb_singleton_class(mrb, regs[b]);
       mrb_gc_arena_restore(mrb, ai);
       NEXT;
     }
@@ -2854,12 +2940,8 @@ RETRY_TRY_BLOCK:
     CASE(OP_STOP) {
       /*        stop VM */
     L_STOP:
-      {
-        int epos = mrb->c->ci->epos;
-
-        while (mrb->c->eidx > epos) {
-          ecall(mrb, --mrb->c->eidx);
-        }
+      while (mrb->c->eidx > 0) {
+        ecall(mrb);
       }
       ERR_PC_CLR(mrb);
       mrb->jmp = prev_jmp;
@@ -2880,6 +2962,7 @@ RETRY_TRY_BLOCK:
       else {
         exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg);
       }
+      ERR_PC_SET(mrb, pc);
       mrb_exc_set(mrb, exc);
       goto L_RAISE;
     }
@@ -2916,7 +2999,6 @@ mrb_top_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int sta
     return mrb_vm_run(mrb, proc, self, stack_keep);
   }
   if (mrb->c->ci == mrb->c->cibase) {
-    mrb->c->ci->env = NULL;
     return mrb_vm_run(mrb, proc, self, stack_keep);
   }
   ci = cipush(mrb);
index 65368c3..1b96452 100644 (file)
@@ -34,11 +34,13 @@ MRuby.each_target do
         f.puts %Q[#include <mruby.h>]
         f.puts %Q[]
         f.write gem_func_decls
+        unless gem_final_calls.empty?
         f.puts %Q[]
-        f.puts %Q[static void]
-        f.puts %Q[mrb_final_mrbgems(mrb_state *mrb) {]
-        f.write gem_final_calls
-        f.puts %Q[}]
+          f.puts %Q[static void]
+          f.puts %Q[mrb_final_mrbgems(mrb_state *mrb) {]
+          f.write gem_final_calls
+          f.puts %Q[}]
+        end
         f.puts %Q[]
         f.puts %Q[void]
         f.puts %Q[mrb_init_mrbgems(mrb_state *mrb) {]
index f370c0a..fc2e0bf 100644 (file)
@@ -55,4 +55,12 @@ MRuby::Toolchain.new(:gcc) do |conf, _params|
       @header_search_paths
     end
   end
+  
+  def conf.enable_sanitizer(*opts)
+    fail 'sanitizer already set' if @sanitizer_list
+
+    @sanitizer_list = opts
+    flg = "-fsanitize=#{opts.join ','}"
+    [self.cc, self.cxx, self.linker].each{|cmd| cmd.flags << flg }
+  end
 end
index 5bc24a7..b008273 100644 (file)
@@ -55,14 +55,15 @@ MRuby::Toolchain.new(:visualcpp) do |conf, _params|
 
   conf.file_separator = '\\'
 
-  if require 'open3'
-    Open3.popen3 conf.cc.command do |_, _, e, _|
-      if /Version (\d{2})\.\d{2}\.\d{5}/ =~ e.gets && $1.to_i <= 17
-        m = "# VS2010/2012 support will be dropped after the next release! #"
-        h = "#" * m.length
-        puts h, m, h
-      end
-    end
-  end
+  # Unreliable detection and will result in invalid encoding errors for localized versions of Visual C++
+  # if require 'open3'
+  #   Open3.popen3 conf.cc.command do |_, _, e, _|
+  #     if /Version (\d{2})\.\d{2}\.\d{5}/ =~ e.gets && $1.to_i <= 17
+  #       m = "# VS2010/2012 support will be dropped after the next release! #"
+  #       h = "#" * m.length
+  #       puts h, m, h
+  #     end
+  #   end
+  # end
 
 end
index 8ab9db2..a9baae5 100644 (file)
@@ -20,7 +20,8 @@ end
 def assertion_string(err, str, iso=nil, e=nil, bt=nil)
   msg = "#{err}#{str}"
   msg += " [#{iso}]" if iso && iso != ''
-  msg += " => #{e.message}" if e
+  msg += " => #{e.cause}" if e && e.respond_to?(:cause)
+  msg += " => #{e.message}" if e && !e.respond_to?(:cause)
   msg += " (mrbgems: #{GEMNAME})" if Object.const_defined?(:GEMNAME)
   if $mrbtest_assert && $mrbtest_assert.size > 0
     $mrbtest_assert.each do |idx, assert_msg, diff|
@@ -56,7 +57,7 @@ def assert(str = 'Assertion failed', iso = '')
   rescue Exception => e
     bt = e.backtrace if $mrbtest_verbose
     if e.class.to_s == 'MRubyTestSkip'
-      $asserts.push "Skip: #{str} #{iso} #{e.cause}"
+      $asserts.push(assertion_string('Skip: ', str, iso, e, nil))
       t_print('?')
     else
       $asserts.push(assertion_string("#{e.class}: ", str, iso, e, bt))
@@ -143,52 +144,43 @@ def assert_not_include(collection, obj, msg = nil)
   assert_false(collection.include?(obj), msg, diff)
 end
 
-def assert_raise(*exp)
-  ret = true
-  if $mrbtest_assert
-    $mrbtest_assert_idx += 1
-    msg = exp.last.class == String ? exp.pop : nil
-    msg = msg.to_s + " : " if msg
-    should_raise = false
-    begin
-      yield
-      should_raise = true
-    rescue Exception => e
-      msg = "#{msg}#{exp.inspect} exception expected, not"
-      diff = "      Class: <#{e.class}>\n" +
-             "    Message: #{e.message}"
-      unless exp.any?{|ex| ex.instance_of?(Module) ? e.kind_of?(ex) : ex == e.class }
-        $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff])
-        ret = false
-      end
-    end
+def assert_raise(*exc)
+  return true unless $mrbtest_assert
+  $mrbtest_assert_idx += 1
 
-    exp = exp.first if exp.first
-    if should_raise
-      msg = "#{msg}#{exp.inspect} expected but nothing was raised."
-      $mrbtest_assert.push([$mrbtest_assert_idx, msg, nil])
-      ret = false
-    end
+  msg = (exc.last.is_a? String) ? exc.pop : nil
+
+  begin
+    yield
+    msg ||= "Expected to raise #{exc} but nothing was raised."
+    diff = nil
+    $mrbtest_assert.push [$mrbtest_assert_idx, msg, diff]
+    false
+  rescue *exc
+    true
+  rescue Exception => e
+    msg ||= "Expected to raise #{exc}, not"
+    diff = "      Class: <#{e.class}>\n" +
+           "    Message: #{e.message}"
+    $mrbtest_assert.push [$mrbtest_assert_idx, msg, diff]
+    false
   end
-  ret
 end
 
-def assert_nothing_raised(*exp)
-  ret = true
-  if $mrbtest_assert
-    $mrbtest_assert_idx += 1
-    msg = exp.last.class == String ? exp.pop : ""
-    begin
-      yield
-    rescue Exception => e
-      msg = "#{msg} exception raised."
-      diff = "      Class: <#{e.class}>\n" +
-             "    Message: #{e.message}"
-      $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff])
-      ret = false
-    end
+def assert_nothing_raised(msg = nil)
+  return true unless $mrbtest_assert
+  $mrbtest_assert_idx += 1
+
+  begin
+    yield
+    true
+  rescue Exception => e
+    msg ||= "Expected not to raise #{exc.join(', ')} but it raised"
+    diff =  "      Class: <#{e.class}>\n" +
+            "    Message: #{e.message}"
+    $mrbtest_assert.push [$mrbtest_assert_idx, msg, diff]
+    false
   end
-  ret
 end
 
 ##
index 7c11265..d2edd01 100644 (file)
@@ -55,7 +55,7 @@ assert('Array#[]', '15.2.12.5.4') do
   assert_equal(nil, [1,2,3].[](-4))
 
   a = [ "a", "b", "c", "d", "e" ]
-  assert_equal("b", a[1.1])
+  assert_equal("b", a[1.1]) if class_defined?("Float")
   assert_equal(["b", "c"], a[1,2])
   assert_equal(["b", "c", "d"], a[1..-2])
 end
index 04a4a15..62eb7e3 100644 (file)
@@ -336,7 +336,7 @@ end
 
 assert('BS Block 28') do
   assert_equal(10) do
-    3.times{|bl|
+    3.times{
       break 10
     }
   end
index eb077fc..a5118fa 100644 (file)
@@ -358,7 +358,7 @@ assert('singleton tests') do
         7
       end
     end
-  end
+  end if class_defined?("Float")
 end
 
 assert('clone Class') do
index 29f5ad5..4c9e2c5 100644 (file)
@@ -73,3 +73,125 @@ assert('negate literal register alignment') do
 
   assert_equal [2], a
 end
+
+assert('register window of calls (#3783)') do
+  # NODE_FOR
+  assert_nothing_raised do
+    for i in []; end
+  end
+
+  # NODE_SYMBOLS
+  assert_nothing_raised do
+    %i(sym)
+  end
+
+  # NODE_SCALL
+  assert_nothing_raised do
+    Object.new&.__id__
+  end
+
+  # NODE_RESCUE with splat
+  assert_nothing_raised do
+    begin
+      raise
+    rescue *[Exception]
+    end
+  end
+
+  # NODE_CASE
+  assert_nothing_raised do
+    case 1
+    when nil
+    end
+  end
+
+  # NODE_CASE with splat
+  assert_nothing_raised do
+    case 1
+    when *nil
+    end
+  end
+
+  # NODE_HASH
+  assert_nothing_raised do
+    {}.merge(
+        0=>0,     1=>1,     2=>2,     3=>3,     4=>4,     5=>5,     6=>6,     7=>7,     8=>8,     9=>9,
+       10=>10,   11=>11,   12=>12,   13=>13,   14=>14,   15=>15,   16=>16,   17=>17,   18=>18,   19=>19,
+       20=>20,   21=>21,   22=>22,   23=>23,   24=>24,   25=>25,   26=>26,   27=>27,   28=>28,   29=>29,
+       30=>30,   31=>31,   32=>32,   33=>33,   34=>34,   35=>35,   36=>36,   37=>37,   38=>38,   39=>39,
+       40=>40,   41=>41,   42=>42,   43=>43,   44=>44,   45=>45,   46=>46,   47=>47,   48=>48,   49=>49,
+       50=>50,   51=>51,   52=>52,   53=>53,   54=>54,   55=>55,   56=>56,   57=>57,   58=>58,   59=>59,
+       60=>60,   61=>61,   62=>62,   63=>63,   64=>64,   65=>65,   66=>66,   67=>67,   68=>68,   69=>69,
+       70=>70,   71=>71,   72=>72,   73=>73,   74=>74,   75=>75,   76=>76,   77=>77,   78=>78,   79=>79,
+       80=>80,   81=>81,   82=>82,   83=>83,   84=>84,   85=>85,   86=>86,   87=>87,   88=>88,   89=>89,
+       90=>90,   91=>91,   92=>92,   93=>93,   94=>94,   95=>95,   96=>96,   97=>97,   98=>98,   99=>99,
+      100=>100, 101=>101, 102=>102, 103=>103, 104=>104, 105=>105, 106=>106, 107=>107, 108=>108, 109=>109,
+      110=>110, 111=>111, 112=>112, 113=>113, 114=>114, 115=>115, 116=>116, 117=>117, 118=>118, 119=>119,
+      120=>120, 121=>121, 122=>122, 123=>123, 124=>124, 125=>125, 126=>126)
+  end
+
+  # NODE_OP_ASGN
+  o = Object.new
+  class << o
+    attr_accessor :a
+  end
+
+  o.a = 1
+  assert_nothing_raised{ o.a += 1 }
+  o.a = 1
+  assert_nothing_raised{ o.a <<= 1 }
+  o.a = 1
+  assert_nothing_raised{ o.a &&= 1 }
+
+  o = { k: 1 }
+  assert_nothing_raised{ o[:k] += 1 }
+  o = { k: 1 }
+  assert_nothing_raised{ o[:k] <<= 1 }
+  o = { k: 1 }
+  assert_nothing_raised{ o[:k] &&= 1 }
+
+  o = { k: 1 }
+  assert_nothing_raised{ o[*[:k]] += 1 }
+  o = { k: 1 }
+  assert_nothing_raised{ o[*[:k]] <<= 1 }
+  o = { k: 1 }
+  assert_nothing_raised{ o[*[:k]] &&= 1 }
+
+  # NODE_YIELD
+  def check_node_yield
+    yield
+  end
+  assert_nothing_raised do
+    check_node_yield{}
+  end
+
+  # NODE_DXSTR
+  assert_raise(NotImplementedError){ `#{:dynamic}` }
+
+  # NODE_XSTR
+  assert_raise(NotImplementedError){ `static` }
+
+  # NODE_DREGX
+  class Regexp; end
+  assert_raise(NoMethodError){ /#{'dynamic'}tail/ }
+  assert_raise(NoMethodError){ /#{'dynamic'}tail/iu }
+
+  # NODE_REGX
+  assert_raise(NoMethodError){ /static/ }
+  assert_raise(NoMethodError){ /static/iu }
+  Object.remove_const :Regexp
+
+  # NODE_UNDEF
+  assert_nothing_raised do
+    class << Object.new
+      undef send
+    end
+  end
+
+  # NODE_ALIAS
+  assert_nothing_raised do
+    class << Object.new
+      alias send2 send
+    end
+  end
+end
\ No newline at end of file
index 7e8c989..92f7a15 100644 (file)
@@ -1,6 +1,8 @@
 ##
 # Float ISO Test
 
+if class_defined?("Float")
+
 assert('Float', '15.2.9') do
   assert_equal Class, Float.class
 end
@@ -203,3 +205,5 @@ assert('Float#>>') do
   # Don't raise on large Right Shift
   assert_equal(-1, -23.0 >> 128)
 end
+
+end # class_defined?("Float")
index c63b8c0..5403a59 100644 (file)
@@ -8,7 +8,7 @@ end
 assert('Hash#==', '15.2.13.4.1') do
   assert_true({ 'abc' => 'abc' } == { 'abc' => 'abc' })
   assert_false({ 'abc' => 'abc' } ==  { 'cba' => 'cba' })
-  assert_true({ :equal => 1 } == { :equal => 1.0 })
+  assert_true({ :equal => 1 } == { :equal => 1.0 }) if class_defined?("Float")
   assert_false({ :a => 1 } == true)
 end
 
index d4ef8b5..cea97a1 100644 (file)
@@ -7,56 +7,65 @@ end
 
 assert('Integer#+', '15.2.8.3.1') do
   a = 1+1
-  b = 1+1.0
+  b = 1+1.0 if class_defined?("Float")
 
   assert_equal 2, a
-  assert_equal 2.0, b
+  assert_equal 2.0, b if class_defined?("Float")
 
   assert_raise(TypeError){ 0+nil }
   assert_raise(TypeError){ 1+nil }
 
   c = Mrbtest::FIXNUM_MAX + 1
   d = Mrbtest::FIXNUM_MAX.__send__(:+, 1)
-  e = Mrbtest::FIXNUM_MAX + 1.0
-  assert_equal Float, c.class
-  assert_equal Float, d.class
-  assert_float e, c
-  assert_float e, d
+
+  if class_defined?("Float")
+    e = Mrbtest::FIXNUM_MAX + 1.0
+    assert_equal Float, c.class
+    assert_equal Float, d.class
+    assert_float e, c
+    assert_float e, d
+  end
 end
 
 assert('Integer#-', '15.2.8.3.2') do
   a = 2-1
-  b = 2-1.0
+  b = 2-1.0 if class_defined?("Float")
 
   assert_equal 1, a
-  assert_equal 1.0, b
+  assert_equal 1.0, b if class_defined?("Float")
 
   c = Mrbtest::FIXNUM_MIN - 1
   d = Mrbtest::FIXNUM_MIN.__send__(:-, 1)
-  e = Mrbtest::FIXNUM_MIN - 1.0
-  assert_equal Float, c.class
-  assert_equal Float, d.class
-  assert_float e, c
-  assert_float e, d
+
+  if class_defined?("Float")
+    e = Mrbtest::FIXNUM_MIN - 1.0
+    assert_equal Float, c.class
+    assert_equal Float, d.class
+    assert_float e, c
+    assert_float e, d
+  end
 end
 
 assert('Integer#*', '15.2.8.3.3') do
   a = 1*1
-  b = 1*1.0
+  b = 1*1.0 if class_defined?("Float")
 
   assert_equal 1, a
-  assert_equal 1.0, b
+  assert_equal 1.0, b if class_defined?("Float")
 
   assert_raise(TypeError){ 0*nil }
   assert_raise(TypeError){ 1*nil }
 
   c = Mrbtest::FIXNUM_MAX * 2
   d = Mrbtest::FIXNUM_MAX.__send__(:*, 2)
-  e = Mrbtest::FIXNUM_MAX * 2.0
-  assert_equal Float, c.class
-  assert_equal Float, d.class
-  assert_float e, c
-  assert_float e, d
+
+  if class_defined?("Float")
+    e = Mrbtest::FIXNUM_MAX * 2.0
+    assert_equal Float, c.class
+    assert_equal Float, d.class
+    assert_float e, c
+    assert_float e, d
+  end
 end
 
 assert('Integer#/', '15.2.8.3.4') do
@@ -218,7 +227,7 @@ end
 
 assert('Integer#to_f', '15.2.8.3.23') do
   assert_equal 1.0, 1.to_f
-end
+end if class_defined?("Float")
 
 assert('Integer#to_i', '15.2.8.3.24') do
   assert_equal 1, 1.to_i
index 40a3482..eaae78e 100644 (file)
@@ -52,11 +52,6 @@ assert('Kernel.lambda', '15.3.1.2.6') do
   assert_equal Proc, m.class
 end
 
-# Not implemented at the moment
-#assert('Kernel.local_variables', '15.3.1.2.7') do
-#  Kernel.local_variables.class == Array
-#end
-
 assert('Kernel.loop', '15.3.1.2.8') do
   i = 0
 
@@ -334,11 +329,6 @@ assert('Kernel#lambda', '15.3.1.3.27') do
   assert_equal Proc, m.class
 end
 
-# Not implemented yet
-#assert('Kernel#local_variables', '15.3.1.3.28') do
-#  local_variables.class == Array
-#end
-
 assert('Kernel#loop', '15.3.1.3.29') do
   i = 0
 
@@ -359,45 +349,31 @@ assert('Kernel#method_missing', '15.3.1.3.30') do
   mm_test = MMTestClass.new
   assert_equal 'A call to no_method_named_this', mm_test.no_method_named_this
 
-  a = String.new
-  begin
-    a.no_method_named_this
-  rescue NoMethodError => e
-    assert_equal "undefined method 'no_method_named_this' for \"\"", e.message
-  end
-
-  class ShortInspectClass
-    def inspect
-      'An inspect string'
+  class SuperMMTestClass < MMTestClass
+    def no_super_method_named_this
+      super
     end
   end
-  b = ShortInspectClass.new
-  begin
-    b.no_method_named_this
-  rescue NoMethodError => e
-    assert_equal "undefined method 'no_method_named_this' for An inspect string", e.message
-  end
+  super_mm_test = SuperMMTestClass.new
+  assert_equal 'A call to no_super_method_named_this', super_mm_test.no_super_method_named_this
 
-  class LongInspectClass
-    def inspect
-      "A" * 70
+  class NoSuperMethodTestClass
+    def no_super_method_named_this
+      super
     end
   end
-  c = LongInspectClass.new
+  no_super_test = NoSuperMethodTestClass.new
   begin
-    c.no_method_named_this
+    no_super_test.no_super_method_named_this
   rescue NoMethodError => e
-    assert_equal "undefined method 'no_method_named_this' for #{c}", e.message
+    assert_equal "undefined method 'no_super_method_named_this'", e.message
   end
 
-  class NoInspectClass
-    undef inspect
-  end
-  d = NoInspectClass.new
+  a = String.new
   begin
-    d.no_method_named_this
+    a.no_method_named_this
   rescue NoMethodError => e
-    assert_equal "undefined method 'no_method_named_this' for #{d}", e.message
+    assert_equal "undefined method 'no_method_named_this'", e.message
   end
 end
 
@@ -551,7 +527,8 @@ assert('Kernel.local_variables', '15.3.1.2.7') do
 
   assert_equal [:a, :b, :c, :vars], Proc.new { |a, b|
     c = 2
-    Kernel.local_variables.sort
+    # Kernel#local_variables: 15.3.1.3.28
+    local_variables.sort
   }.call(-1, -2)
 end
 
index 0d70bb3..4ed1b3d 100755 (executable)
@@ -8,7 +8,7 @@
 #
 # For example, the following mruby code:
 #
-#   if i > 0 and i < 10 then
+#   if i > 0 and i < 10
 #
 # compiles to the following byte code:
 #
 
 assert('and', '11.2.3') do
   a = 1
-  if a > 0 and a < 10 then
+  if a > 0 and a < 10
     b = 1
   else
     b = 0
   end
   assert_equal 1, b
 
-  if a < 0 and a < 10 then
+  if a < 0 and a < 10
     b = 1
   else
     b = 0
   end
   assert_equal 0, b
 
-  if a < 0 and a > 10 then
+  if a < 0 and a > 10
     b = 1
   else
     b = 0
@@ -51,21 +51,21 @@ end
 
 assert('or','11.2.4') do
   a = 1
-  if a > 0 or a < 10 then
+  if a > 0 or a < 10
     b = 1
   else
     b = 0
   end
   assert_equal 1, b
 
-  if a < 0 or a < 10 then
+  if a < 0 or a < 10
     b = 1
   else
     b = 0
   end
   assert_equal 1, b
 
-  if a < 0 or a > 10 then
+  if a < 0 or a > 10
     b = 1
   else
     b = 0
index 419b0bf..5a46c24 100644 (file)
@@ -29,6 +29,18 @@ end
 
 # TODO not implemented ATM assert('Module.nesting', '15.2.2.3.2') do
 
+assert('Module.nesting', '15.2.2.2.2') do
+  module Test4ModuleNesting
+    module Test4ModuleNesting2
+      assert_equal [Test4ModuleNesting2, Test4ModuleNesting],
+                   Module.nesting
+    end
+  end
+  module Test4ModuleNesting::Test4ModuleNesting2
+    assert_equal [Test4ModuleNesting::Test4ModuleNesting2], Module.nesting
+  end
+end
+
 assert('Module#ancestors', '15.2.2.4.9') do
   class Test4ModuleAncestors
   end
@@ -268,6 +280,12 @@ assert('Module#const_get', '15.2.2.4.21') do
   end
 
   assert_equal 42, Test4ConstGet.const_get(:Const4Test4ConstGet)
+  assert_equal 42, Test4ConstGet.const_get("Const4Test4ConstGet")
+  assert_equal 42, Object.const_get("Test4ConstGet::Const4Test4ConstGet")
+
+  assert_raise(TypeError){ Test4ConstGet.const_get(123) }
+  assert_raise(NameError){ Test4ConstGet.const_get(:I_DO_NOT_EXIST) }
+  assert_raise(NameError){ Test4ConstGet.const_get("I_DO_NOT_EXIST::ME_NEITHER") }
 end
 
 assert('Module#const_missing', '15.2.2.4.22') do
@@ -280,7 +298,7 @@ assert('Module#const_missing', '15.2.2.4.22') do
   assert_equal 42, Test4ConstMissing.const_get(:ConstDoesntExist)
 end
 
-assert('Module#const_get', '15.2.2.4.23') do
+assert('Module#const_set', '15.2.2.4.23') do
   module Test4ConstSet
     Const4Test4ConstSet = 42
   end
@@ -309,10 +327,14 @@ assert('Module#include', '15.2.2.4.27') do
     Const4Include = 42
   end
   module Test4Include2
-    include Test4Include
+    @include_result = include Test4Include
+    class << self
+      attr_reader :include_result
+    end
   end
 
   assert_equal 42, Test4Include2.const_get(:Const4Include)
+  assert_equal Test4Include2, Test4Include2.include_result
 end
 
 assert('Module#include?', '15.2.2.4.28') do
@@ -540,6 +562,18 @@ end
     assert_equal(expected, obj.m1)
   end
 
+  assert('Module#prepend result') do
+    module TestPrepended; end
+    module TestPrependResult
+      @prepend_result = prepend TestPrepended
+      class << self
+        attr_reader :prepend_result
+      end
+    end
+
+    assert_equal TestPrependResult, TestPrependResult.prepend_result
+  end
+
   # mruby shouldn't be affected by this since there is
   # no visibility control (yet)
   assert('Module#prepend public') do
@@ -601,7 +635,7 @@ end
     c.singleton_class.class_eval do
       define_method(:method_removed) {|id| removed = id}
     end
-    assert_nothing_raised(NoMethodError, NameError, '[Bug #7843]') do
+    assert_nothing_raised('[Bug #7843]') do
       c.class_eval do
         remove_method(:foo)
       end
@@ -690,7 +724,7 @@ end
     end
     a = c.new
     assert_true a.respond_to?(:foo), bug8005
-    assert_nothing_raised(NoMethodError, bug8005) {a.send :foo}
+    assert_nothing_raised(bug8005) {a.send :foo}
   end
 
   # mruby has no visibility control
index 120cc96..38c62a6 100644 (file)
@@ -15,7 +15,7 @@ end
 
 assert('Numeric#abs', '15.2.7.4.3') do
   assert_equal(1, 1.abs)
-  assert_equal(1.0, -1.abs)
+  assert_equal(1.0, -1.abs) if class_defined?("Float")
 end
 
 assert('Numeric#pow') do
index ef4566e..42ac3b9 100644 (file)
@@ -11,6 +11,10 @@ assert('Proc.new', '15.2.17.3.1') do
   end
 
   assert_equal (Proc.new {}).class, Proc
+
+  assert_raise LocalJumpError do
+    Proc.new{ break }.call
+  end
 end
 
 assert('Proc#[]', '15.2.17.4.1') do
@@ -164,3 +168,13 @@ assert('&obj call to_proc if defined') do
 
   assert_raise(TypeError){ mock(&(Object.new)) }
 end
+
+assert('Creation of a proc through the block of a method') do
+  def m(&b) b end
+
+  assert_equal m{}.class, Proc
+
+  assert_raise LocalJumpError do
+    m{ break }.call
+  end
+end
index 5391369..3e67fcc 100644 (file)
@@ -8,7 +8,7 @@ end
 assert('Range#==', '15.2.14.4.1') do
   assert_true (1..10) == (1..10)
   assert_false (1..10) == (1..100)
-  assert_true (1..10) == Range.new(1.0, 10.0)
+  assert_true (1..10) == Range.new(1.0, 10.0) if class_defined?("Float")
 end
 
 assert('Range#===', '15.2.14.4.2') do
index a413962..e91b915 100644 (file)
@@ -20,6 +20,7 @@ assert('String#<=>', '15.2.10.5.1') do
   assert_equal  1, c
   assert_equal(-1, d)
   assert_equal  1, e
+  assert_nil 'a' <=> 1024
 end
 
 assert('String#==', '15.2.10.5.2') do
@@ -154,10 +155,11 @@ assert('String#[]=') do
     d[-10] = 'X'
   end
 
-  e = 'abc'
-  e[1.1] = 'X'
-  assert_equal 'aXc', e
-
+  if class_defined?("Float")
+   e = 'abc'
+   e[1.1] = 'X'
+   assert_equal 'aXc', e
+  end
 
   # length of args is 2
   a1 = 'abc'
@@ -366,9 +368,9 @@ assert('String#gsub', '15.2.10.5.18') do
   assert_equal('aBcaBc', 'abcabc'.gsub('b', 'B'), 'gsub without block')
   assert_equal('aBcaBc', 'abcabc'.gsub('b'){|w| w.capitalize }, 'gsub with block')
   assert_equal('$a$a$',  '#a#a#'.gsub('#', '$'), 'mruby/mruby#847')
-  assert_equal('$a$a$',  '#a#a#'.gsub('#'){|w| '$' }, 'mruby/mruby#847 with block')
+  assert_equal('$a$a$',  '#a#a#'.gsub('#'){|_w| '$' }, 'mruby/mruby#847 with block')
   assert_equal('$$a$$',  '##a##'.gsub('##', '$$'), 'mruby/mruby#847 another case')
-  assert_equal('$$a$$',  '##a##'.gsub('##'){|w| '$$' }, 'mruby/mruby#847 another case with block')
+  assert_equal('$$a$$',  '##a##'.gsub('##'){|_w| '$$' }, 'mruby/mruby#847 another case with block')
   assert_equal('A',      'a'.gsub('a', 'A'))
   assert_equal('A',      'a'.gsub('a'){|w| w.capitalize })
   assert_equal("<a><><>", 'a'.gsub('a', '<\0><\1><\2>'))
@@ -629,7 +631,7 @@ assert('String#to_f', '15.2.10.5.38') do
   assert_float(12345.6789, c)
   assert_float(0, d)
   assert_float(Float::INFINITY, e)
-end
+end if class_defined?("Float")
 
 assert('String#to_i', '15.2.10.5.39') do
   a = ''.to_i
@@ -684,7 +686,7 @@ assert('String#inspect', '15.2.10.5.46') do
   ("\1" * 100).inspect
   end
 
-  assert_equal "\"\\000\"", "\0".inspect
+  assert_equal "\"\\x00\"", "\0".inspect
 end
 
 # Not ISO specified
index c2c6e0c..5904d68 100644 (file)
@@ -6,7 +6,7 @@ MRuby::Build.new('debug') do |conf|
   conf.gembox 'full-core'
   conf.cc.flags += %w(-Werror=declaration-after-statement)
   conf.compilers.each do |c|
-    c.defines += %w(MRB_GC_STRESS MRB_GC_FIXED_ARENA)
+    c.defines += %w(MRB_GC_STRESS MRB_GC_FIXED_ARENA MRB_METHOD_CACHE)
   end
 
   build_mrbc_exec
index 29b35a9..420665e 100644 (file)
@@ -361,20 +361,30 @@ static void get_privsep_data(const RSA *rsa, struct st_neverbleed_rsa_exdata_t *
                              struct st_neverbleed_thread_data_t **thdata)
 {
     *exdata = RSA_get_ex_data(rsa, 0);
-    if (*exdata == NULL) {
-        errno = 0;
-        dief("invalid internal ref");
-    }
+    if (*exdata == NULL)
+        return;
     *thdata = get_thread_data((*exdata)->nb);
 }
 
+static const size_t default_reserved_size = 8192;
+
+struct key_slots {
+    size_t size;
+    size_t reserved_size;
+    /* bit array slots:
+     *   1-bit slot available
+     *   0-bit slot unavailable
+     */
+    uint8_t *bita_avail;
+};
+
 static struct {
     struct {
         pthread_mutex_t lock;
-        size_t size;
         RSA **keys;
-        size_t ecdsa_size;
+        struct key_slots rsa_slots;
         EC_KEY **ecdsa_keys;
+        struct key_slots ecdsa_slots;
     } keys;
     neverbleed_t *nb;
 } daemon_vars = {{PTHREAD_MUTEX_INITIALIZER}};
@@ -385,20 +395,89 @@ static RSA *daemon_get_rsa(size_t key_index)
 
     pthread_mutex_lock(&daemon_vars.keys.lock);
     rsa = daemon_vars.keys.keys[key_index];
+    if (rsa)
+        RSA_up_ref(rsa);
     pthread_mutex_unlock(&daemon_vars.keys.lock);
 
     return rsa;
 }
 
-static size_t daemon_set_rsa(RSA *rsa)
+/*
+ *  Returns an available slot in bit array B
+ *  or if not found, returns SIZE_MAX
+ */
+static size_t bita_ffirst(const uint8_t *b, const size_t tot, size_t bits)
 {
-    size_t index;
+    if (bits >= tot)
+        return SIZE_MAX;
+
+    uint64_t w = *((uint64_t *) b);
+    /* __builtin_ffsll returns one plus the index of the least significant 1-bit, or zero if not found */
+    uint32_t r = __builtin_ffsll(w);
+    if (r)
+        return bits + r - 1; /* adjust result */
+
+    return bita_ffirst(&b[8], tot, bits + 64);
+}
+
+/*
+ * bit operation helpers for the bit-array in key_slots
+ */
+#define BITMASK(b) (1 << ((b) % CHAR_BIT))
+#define BITBYTE(b) ((b) / CHAR_BIT)
+#define BITSET(a, b) ((a)[BITBYTE(b)] |= BITMASK(b))
+#define BITUNSET(a, b) ((a)[BITBYTE(b)] &= ~BITMASK(b))
+#define BITBYTES(nb) ((nb + CHAR_BIT - 1) / CHAR_BIT)
+#define BITCHECK(a, b) ((a)[BITBYTE(b)] & BITMASK(b))
+
+static void adjust_slots_reserved_size(int type, struct key_slots *slots)
+{
+#define ROUND2WORD(n) (n + 64 - 1 - (n + 64 - 1) % 64)
+    if (!slots->reserved_size || (slots->size >= slots->reserved_size)) {
+        size_t size = slots->reserved_size ? ROUND2WORD((size_t)(slots->reserved_size * 0.50) + slots->reserved_size)
+                : default_reserved_size;
+#undef ROUND2WORD
+
+        switch (type) {
+        case NEVERBLEED_TYPE_RSA:
+            if ((daemon_vars.keys.keys = realloc(daemon_vars.keys.keys, sizeof(*daemon_vars.keys.keys) * size)) == NULL)
+                dief("no memory");
+            break;
+        case NEVERBLEED_TYPE_ECDSA:
+            if ((daemon_vars.keys.ecdsa_keys = realloc(daemon_vars.keys.ecdsa_keys, sizeof(*daemon_vars.keys.ecdsa_keys) * size)) == NULL)
+                dief("no memory");
+            break;
+        default:
+            dief("invalid type adjusting reserved");
+        }
+
+        uint8_t *b;
+        if ((b = realloc(slots->bita_avail, BITBYTES(size))) == NULL)
+            dief("no memory");
+
+        /* set all bits to 1 making all slots available */
+        memset(&b[BITBYTES(slots->reserved_size)], 0xff, BITBYTES(size - slots->reserved_size));
 
+        slots->bita_avail = b;
+        slots->reserved_size = size;
+    }
+}
+
+static size_t daemon_set_rsa(RSA *rsa)
+{
     pthread_mutex_lock(&daemon_vars.keys.lock);
-    if ((daemon_vars.keys.keys = realloc(daemon_vars.keys.keys, sizeof(*daemon_vars.keys.keys) * (daemon_vars.keys.size + 1))) ==
-        NULL)
-        dief("no memory");
-    index = daemon_vars.keys.size++;
+
+    adjust_slots_reserved_size(NEVERBLEED_TYPE_RSA, &daemon_vars.keys.rsa_slots);
+
+    size_t index = bita_ffirst(daemon_vars.keys.rsa_slots.bita_avail, daemon_vars.keys.rsa_slots.reserved_size, 0);
+
+    if (index == SIZE_MAX)
+        dief("no available slot for key");
+
+    /* set slot as unavailable */
+    BITUNSET(daemon_vars.keys.rsa_slots.bita_avail, index);
+
+    daemon_vars.keys.rsa_slots.size++;
     daemon_vars.keys.keys[index] = rsa;
     RSA_up_ref(rsa);
     pthread_mutex_unlock(&daemon_vars.keys.lock);
@@ -460,6 +539,7 @@ static int priv_encdec_stub(const char *name,
     }
     ret = func((int)flen, from, to, rsa, (int)padding);
     expbuf_dispose(buf);
+    RSA_free(rsa);
 
     expbuf_push_num(buf, ret);
     expbuf_push_bytes(buf, to, ret > 0 ? ret : 0);
@@ -540,6 +620,7 @@ static int sign_stub(struct expbuf_t *buf)
     }
     ret = RSA_sign((int)type, m, (unsigned)m_len, sigret, &siglen, rsa);
     expbuf_dispose(buf);
+    RSA_free(rsa);
 
     expbuf_push_num(buf, ret);
     expbuf_push_bytes(buf, sigret, ret == 1 ? siglen : 0);
@@ -547,7 +628,7 @@ static int sign_stub(struct expbuf_t *buf)
     return 0;
 }
 
-#if !OPENSSL_1_1_API
+#if !OPENSSL_1_1_API && (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER < 0x2070000fL)
 
 static void RSA_get0_key(const RSA *rsa, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
 {
@@ -628,6 +709,8 @@ static EC_KEY *daemon_get_ecdsa(size_t key_index)
 
     pthread_mutex_lock(&daemon_vars.keys.lock);
     ec_key = daemon_vars.keys.ecdsa_keys[key_index];
+    if (ec_key)
+        EC_KEY_up_ref(ec_key);
     pthread_mutex_unlock(&daemon_vars.keys.lock);
 
     return ec_key;
@@ -635,13 +718,19 @@ static EC_KEY *daemon_get_ecdsa(size_t key_index)
 
 static size_t daemon_set_ecdsa(EC_KEY *ec_key)
 {
-    size_t index;
-
     pthread_mutex_lock(&daemon_vars.keys.lock);
-    if ((daemon_vars.keys.ecdsa_keys = realloc(daemon_vars.keys.ecdsa_keys,
-                                               sizeof(*daemon_vars.keys.ecdsa_keys) * (daemon_vars.keys.ecdsa_size + 1))) == NULL)
-        dief("no memory");
-    index = daemon_vars.keys.ecdsa_size++;
+
+    adjust_slots_reserved_size(NEVERBLEED_TYPE_ECDSA, &daemon_vars.keys.ecdsa_slots);
+
+    size_t index = bita_ffirst(daemon_vars.keys.ecdsa_slots.bita_avail, daemon_vars.keys.ecdsa_slots.reserved_size, 0);
+
+    if (index == SIZE_MAX)
+        dief("no available slot for key");
+
+    /* set slot as unavailable */
+    BITUNSET(daemon_vars.keys.ecdsa_slots.bita_avail, index);
+
+    daemon_vars.keys.ecdsa_slots.size++;
     daemon_vars.keys.ecdsa_keys[index] = ec_key;
     EC_KEY_up_ref(ec_key);
     pthread_mutex_unlock(&daemon_vars.keys.lock);
@@ -672,6 +761,8 @@ static int ecdsa_sign_stub(struct expbuf_t *buf)
     ret = ECDSA_sign((int)type, m, (unsigned)m_len, sigret, &siglen, ec_key);
     expbuf_dispose(buf);
 
+    EC_KEY_free(ec_key);
+
     expbuf_push_num(buf, ret);
     expbuf_push_bytes(buf, sigret, ret == 1 ? siglen : 0);
 
@@ -682,10 +773,8 @@ static void ecdsa_get_privsep_data(const EC_KEY *ec_key, struct st_neverbleed_rs
                                    struct st_neverbleed_thread_data_t **thdata)
 {
     *exdata = EC_KEY_get_ex_data(ec_key, 0);
-    if (*exdata == NULL) {
-        errno = 0;
-        dief("invalid internal ref");
-    }
+    if (*exdata == NULL)
+        return;
     *thdata = get_thread_data((*exdata)->nb);
 }
 
@@ -779,6 +868,71 @@ static EVP_PKEY *ecdsa_create_pkey(neverbleed_t *nb, size_t key_index, int curve
     return pkey;
 }
 
+static void priv_ecdsa_finish(EC_KEY *key)
+{
+    struct st_neverbleed_rsa_exdata_t *exdata;
+    struct st_neverbleed_thread_data_t *thdata;
+
+    ecdsa_get_privsep_data(key, &exdata, &thdata);
+    if (exdata == NULL)
+        return;
+
+    struct expbuf_t buf = {NULL};
+    size_t ret;
+
+    expbuf_push_str(&buf, "del_ecdsa_key");
+    expbuf_push_num(&buf, exdata->key_index);
+    if (expbuf_write(&buf, thdata->fd) != 0)
+        dief(errno != 0 ? "write error" : "connection closed by daemon");
+    expbuf_dispose(&buf);
+
+    if (expbuf_read(&buf, thdata->fd) != 0)
+        dief(errno != 0 ? "read error" : "connection closed by daemon");
+    if (expbuf_shift_num(&buf, &ret) != 0) {
+        errno = 0;
+        dief("failed to parse response");
+    }
+    expbuf_dispose(&buf);
+}
+
+static int del_ecdsa_key_stub(struct expbuf_t *buf)
+{
+    size_t key_index;
+    int ret = 0;
+
+    if (expbuf_shift_num(buf, &key_index) != 0) {
+        errno = 0;
+        warnf("%s: failed to parse request", __FUNCTION__);
+        return -1;
+    }
+
+    if (!daemon_vars.keys.ecdsa_keys || key_index >= daemon_vars.keys.ecdsa_slots.reserved_size) {
+        errno = 0;
+        warnf("%s: invalid key index %zu", __FUNCTION__, key_index);
+        goto respond;
+    }
+
+    if (BITCHECK(daemon_vars.keys.ecdsa_slots.bita_avail, key_index)) {
+        warnf("%s: index not in use %zu", __FUNCTION__, key_index);
+        goto respond;
+    }
+
+    pthread_mutex_lock(&daemon_vars.keys.lock);
+    /* set slot as available */
+    BITSET(daemon_vars.keys.ecdsa_slots.bita_avail, key_index);
+    daemon_vars.keys.ecdsa_slots.size--;
+    EC_KEY_free(daemon_vars.keys.ecdsa_keys[key_index]);
+    daemon_vars.keys.ecdsa_keys[key_index] = NULL;
+    pthread_mutex_unlock(&daemon_vars.keys.lock);
+
+    ret = 1;
+
+respond:
+    expbuf_dispose(buf);
+    expbuf_push_num(buf, ret);
+    return 0;
+}
+
 #endif
 
 int neverbleed_load_private_key_file(neverbleed_t *nb, SSL_CTX *ctx, const char *fn, char *errbuf)
@@ -786,7 +940,7 @@ int neverbleed_load_private_key_file(neverbleed_t *nb, SSL_CTX *ctx, const char
     struct st_neverbleed_thread_data_t *thdata = get_thread_data(nb);
     struct expbuf_t buf = {NULL};
     int ret = 1;
-    size_t key_index, type;
+    size_t index, type;
     EVP_PKEY *pkey;
 
     expbuf_push_str(&buf, "load_key");
@@ -797,7 +951,7 @@ int neverbleed_load_private_key_file(neverbleed_t *nb, SSL_CTX *ctx, const char
 
     if (expbuf_read(&buf, thdata->fd) != 0)
         dief(errno != 0 ? "read error" : "connection closed by daemon");
-    if (expbuf_shift_num(&buf, &type) != 0 || expbuf_shift_num(&buf, &key_index) != 0) {
+    if (expbuf_shift_num(&buf, &type) != 0 || expbuf_shift_num(&buf, &index) != 0) {
         errno = 0;
         dief("failed to parse response");
     }
@@ -810,7 +964,7 @@ int neverbleed_load_private_key_file(neverbleed_t *nb, SSL_CTX *ctx, const char
             errno = 0;
             dief("failed to parse response");
         }
-        pkey = create_pkey(nb, key_index, estr, nstr);
+        pkey = create_pkey(nb, index, estr, nstr);
         break;
     }
 #if OPENSSL_1_1_API
@@ -822,7 +976,7 @@ int neverbleed_load_private_key_file(neverbleed_t *nb, SSL_CTX *ctx, const char
             errno = 0;
             dief("failed to parse response");
         }
-        pkey = ecdsa_create_pkey(nb, key_index, curve_name, ec_pubkeystr);
+        pkey = ecdsa_create_pkey(nb, index, curve_name, ec_pubkeystr);
         break;
     }
 #endif
@@ -1052,6 +1206,74 @@ Redo:
     _exit(0);
 }
 
+static int priv_rsa_finish(RSA *rsa)
+{
+    struct st_neverbleed_rsa_exdata_t *exdata;
+    struct st_neverbleed_thread_data_t *thdata;
+
+    get_privsep_data(rsa, &exdata, &thdata);
+    if (exdata == NULL)
+        return 1;
+
+    struct expbuf_t buf = {NULL};
+    size_t ret;
+
+    expbuf_push_str(&buf, "del_rsa_key");
+    expbuf_push_num(&buf, exdata->key_index);
+    if (expbuf_write(&buf, thdata->fd) != 0)
+        dief(errno != 0 ? "write error" : "connection closed by daemon");
+    expbuf_dispose(&buf);
+
+    if (expbuf_read(&buf, thdata->fd) != 0)
+        dief(errno != 0 ? "read error" : "connection closed by daemon");
+    if (expbuf_shift_num(&buf, &ret) != 0) {
+        errno = 0;
+        dief("failed to parse response");
+    }
+    expbuf_dispose(&buf);
+
+    return (int)ret;
+}
+
+static int del_rsa_key_stub(struct expbuf_t *buf)
+{
+    size_t key_index;
+
+    int ret = 0;
+
+    if (expbuf_shift_num(buf, &key_index) != 0) {
+        errno = 0;
+        warnf("%s: failed to parse request", __FUNCTION__);
+        return -1;
+    }
+
+    if (!daemon_vars.keys.keys || key_index >= daemon_vars.keys.rsa_slots.reserved_size) {
+        errno = 0;
+        warnf("%s: invalid key index %zu", __FUNCTION__, key_index);
+        goto respond;
+    }
+
+    if (BITCHECK(daemon_vars.keys.rsa_slots.bita_avail, key_index)) {
+        warnf("%s: index not in use %zu", __FUNCTION__, key_index);
+        goto respond;
+    }
+
+    pthread_mutex_lock(&daemon_vars.keys.lock);
+    /* set slot as available */
+    BITSET(daemon_vars.keys.rsa_slots.bita_avail, key_index);
+    daemon_vars.keys.rsa_slots.size--;
+    RSA_free(daemon_vars.keys.keys[key_index]);
+    daemon_vars.keys.keys[key_index] = NULL;
+    pthread_mutex_unlock(&daemon_vars.keys.lock);
+
+    ret = 1;
+
+respond:
+    expbuf_dispose(buf);
+    expbuf_push_num(buf, ret);
+    return 0;
+}
+
 static void *daemon_conn_thread(void *_sock_fd)
 {
     int sock_fd = (int)((char *)_sock_fd - (char *)NULL);
@@ -1093,10 +1315,16 @@ static void *daemon_conn_thread(void *_sock_fd)
         } else if (strcmp(cmd, "ecdsa_sign") == 0) {
             if (ecdsa_sign_stub(&buf) != 0)
                 break;
+        } else if (strcmp(cmd, "del_ecdsa_key") == 0) {
+            if (del_ecdsa_key_stub(&buf) != 0)
+                break;
 #endif
         } else if (strcmp(cmd, "load_key") == 0) {
             if (load_key_stub(&buf) != 0)
                 break;
+        } else if (strcmp(cmd, "del_rsa_key") == 0) {
+            if (del_rsa_key_stub(&buf) != 0)
+                break;
         } else if (strcmp(cmd, "setuidgid") == 0) {
             if (setuidgid_stub(&buf) != 0)
                 break;
@@ -1158,7 +1386,7 @@ static RSA_METHOD static_rsa_method = {
     NULL,                 /* rsa_mod_exp */
     NULL,                 /* bn_mod_exp */
     NULL,                 /* init */
-    NULL,                 /* finish */
+    priv_rsa_finish,      /* finish */
     RSA_FLAG_SIGN_VER,    /* flags */
     NULL,                 /* app data */
     sign_proxy,           /* rsa_sign */
@@ -1176,24 +1404,21 @@ int neverbleed_init(neverbleed_t *nb, char *errbuf)
     const RSA_METHOD *default_method = RSA_PKCS1_OpenSSL();
     EC_KEY_METHOD *ecdsa_method;
     const EC_KEY_METHOD *ecdsa_default_method;
-    RSA_METHOD *rsa_method = RSA_meth_new("privsep RSA method", 0);
+    RSA_METHOD *rsa_method = RSA_meth_dup(RSA_PKCS1_OpenSSL());
 
+    RSA_meth_set1_name(rsa_method, "privsep RSA method");
     RSA_meth_set_priv_enc(rsa_method, priv_enc_proxy);
     RSA_meth_set_priv_dec(rsa_method, priv_dec_proxy);
     RSA_meth_set_sign(rsa_method, sign_proxy);
-
-    RSA_meth_set_pub_enc(rsa_method, RSA_meth_get_pub_enc(default_method));
-    RSA_meth_set_pub_dec(rsa_method, RSA_meth_get_pub_dec(default_method));
-    RSA_meth_set_verify(rsa_method, RSA_meth_get_verify(default_method));
+    RSA_meth_set_finish(rsa_method, priv_rsa_finish);
 
     /* setup EC_KEY_METHOD for ECDSA */
     ecdsa_default_method = EC_KEY_get_default_method();
     ecdsa_method = EC_KEY_METHOD_new(ecdsa_default_method);
 
-    EC_KEY_METHOD_set_keygen(ecdsa_method, NULL);
-    EC_KEY_METHOD_set_compute_key(ecdsa_method, NULL);
     /* it seems sign_sig and sign_setup is not used in TLS ECDSA. */
     EC_KEY_METHOD_set_sign(ecdsa_method, ecdsa_sign_proxy, NULL, NULL);
+    EC_KEY_METHOD_set_init(ecdsa_method, NULL, priv_ecdsa_finish, NULL, NULL, NULL, NULL);
 #else
     const RSA_METHOD *default_method = RSA_PKCS1_SSLeay();
     RSA_METHOD *rsa_method = &static_rsa_method;
@@ -1201,6 +1426,7 @@ int neverbleed_init(neverbleed_t *nb, char *errbuf)
     rsa_method->rsa_pub_enc = default_method->rsa_pub_enc;
     rsa_method->rsa_pub_dec = default_method->rsa_pub_dec;
     rsa_method->rsa_verify = default_method->rsa_verify;
+    rsa_method->bn_mod_exp = default_method->bn_mod_exp;
 #endif
 
     /* setup the daemon */