187j3x1
Alek Storm
Alex Nalivko
+Alexandros Konstantinakis-Karmis
Alexis La Goutte
Amir Pakdel
Anders Bakken
Tomasz Torcz
Vernon Tang
Viacheslav Biriukov
+Viktor Szakats
Viktor Szépe
Wenfeng Liu
Xiaoguang Sun
dalf
es
fangdingjun
+jwchoi
kumagi
lstefani
makovich
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)
${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})
-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
-# 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,
$(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,
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)
-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'`; \
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.
-# 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,
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,
# 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
])
# 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,
# 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,
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,
# 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],
# 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
# -----------------------------
# 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,
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.
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
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,
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,
# 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,
# 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,
# 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,
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,
# 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,
[
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])
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,
# 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,
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,
_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,
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,
# 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,
#! /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
# 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
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
# 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:
#! /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
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."
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 ;
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
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*,
# 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
;;
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
# 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)
# 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
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
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
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.
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
;;
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
# 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[]) {
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
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)
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
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()
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
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
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
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
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>
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
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 ()
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 ;;
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*:*:*)
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
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
#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.
# 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]*)
*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
&& 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:*:*:*)
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:*)
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; } ;;
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
*: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
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.
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) | \
# 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`
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
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
else
UNAME_MACHINE="$cputype"
fi
- echo ${UNAME_MACHINE}-unknown-plan9
+ echo "$UNAME_MACHINE"-unknown-plan9
exit ;;
*:TOPS-10:*:*)
echo pdp10-unknown-tops10
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 ;;
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
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
/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
#! /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
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."
*local*)
# First pass through any local machine types.
- echo $1
+ echo "$1"
exit ;;
* )
# 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* | \
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
;;
-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
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
| nios | nios2 | nios2eb | nios2el \
| ns16k | ns32k \
| open8 | or1k | or1knd | or32 \
- | pdp10 | pdp11 | pj | pjl \
+ | pdp10 | pj | pjl \
| powerpc | powerpc64 | powerpc64le | powerpcle \
| pru \
| pyramid \
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
;;
# 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.
# 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)
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
os=-linux
;;
blackfin-*)
- basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
+ basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'`
os=-linux
;;
bluegene*)
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
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)
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
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
;;
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
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
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
nsr-tandem)
basic_machine=nsr-tandem
;;
+ nsv-tandem)
+ basic_machine=nsv-tandem
+ ;;
nsx-tandem)
basic_machine=nsx-tandem
;;
os=-linux
;;
parisc-*)
- basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
+ basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'`
os=-linux
;;
pbd)
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
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
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
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
;;
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
basic_machine=a29k-wrs
os=-vxworks
;;
- wasm32)
- basic_machine=wasm32-unknown
- ;;
w65*)
basic_machine=w65-wdc
os=-none
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
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
;;
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
;;
# 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
# 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/'`
;;
*)
;;
-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.
| -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* \
| -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*)
-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
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
-wince*)
os=-wince
;;
- -osfrose*)
- os=-osfrose
- ;;
- -osf*)
- os=-osf
- ;;
-utek*)
os=-bsd
;;
-oss*)
os=-sysv3
;;
- -svr4)
+ -svr4*)
os=-sysv4
;;
-svr3)
-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
;;
*)
# 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
*-be)
os=-beos
;;
- *-haiku)
- os=-haiku
- ;;
*-ibm)
os=-aix
;;
i370-*)
os=-mvs
;;
- *-next)
- os=-nextstep3
- ;;
*-gould)
os=-sysv
;;
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:
#! /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>.
#
# 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=''
AMDEPBACKSLASH
AMDEP_FALSE
AMDEP_TRUE
-am__quote
am__include
DEPDIR
am__untar
PACKAGE_TARNAME
PACKAGE_NAME
PATH_SEPARATOR
-SHELL'
+SHELL
+am__quote'
ac_subst_files=''
ac_user_opts='
enable_option_checking
# 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]...
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
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.
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 $@
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
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 :
# Define the identity of the package.
PACKAGE='nghttp2'
- VERSION='1.31.1'
+ VERSION='1.34.0'
cat >>confdefs.h <<_ACEOF
# 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
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"`
# 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
# 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
# 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
# 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
# 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.
# 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'
$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
# 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
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\\"
-AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
+AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"
_ACEOF
# 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
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
}
;;
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])
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"`
-# 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,
*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)
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)'; \
#! /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
# 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
# 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"
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 \
-# 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,
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 \
*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)
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)'; \
_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
(``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
(``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
.\" 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
.
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
.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
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>
.. 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>
.\" 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
.
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.
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`.
--- /dev/null
+
+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.
.. 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.
.\" 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
.
.\" 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
.
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
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.
.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
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
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
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
.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
.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
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
.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
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
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
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.
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.
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``
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>
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
~~~~~~
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.
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
~~~~
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``
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
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
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
`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
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.
.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.
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
-----------------------------------------
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
-# 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,
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)
*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)
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$$||'`;\
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)'; \
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
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
.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 \
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;
});
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;
});
#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>
* 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>
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
}
return SSL_TLSEXT_ERR_OK;
}
+#endif /* !OPENSSL_NO_NEXTPROTONEG */
/*
* Setup SSL/TLS context.
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) {
* 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>
* 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>
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. */
}
return SSL_TLSEXT_ERR_OK;
}
+#endif /* !OPENSSL_NO_NEXTPROTONEG */
/* Create SSL_CTX. */
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;
}
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");
* 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>
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;
*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,
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) {
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;
}
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);
#!/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
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"`
fi
fi
+ case $dstdir in
+ */) dstdirslash=$dstdir;;
+ *) dstdirslash=$dstdir/;;
+ esac
+
obsolete_mkdir_used=false
if test $dstdir_status != 0; then
# 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
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
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:
set(GO_FILES
nghttpx_http1_test.go
nghttpx_http2_test.go
- nghttpx_spdy_test.go
server_tester.go
)
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
)
-# 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,
*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)
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)'; \
"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"
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
}
nextStreamID: 1,
authority: authority,
frCh: make(chan http2.Frame),
- spdyFrCh: make(chan spdy.Frame),
errCh: make(chan error),
}
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 {
}
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)
}
}
-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
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)
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
"${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
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
-# 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,
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)
$(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
*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)
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$$||'`;\
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)'; \
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
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
.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
-#
-# 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
-# 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,
*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)
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)'; \
/* 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
/* 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>
#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
* 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;
/**
/**
* 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 */
*
* 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,
* .. 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(
* 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`.
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
* @macro
* Version number of the nghttp2 library release
*/
-#define NGHTTP2_VERSION "1.31.1"
+#define NGHTTP2_VERSION "1.34.0"
/**
* @macro
* 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 */
#define NGHTTP2_BUF_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
#define NGHTTP2_CALLBACKS_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
#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 */
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;
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;
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;
#define NGHTTP2_FRAME_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
/* 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);
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.
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
/* 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),
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':
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;
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;
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;
}
#define NGHTTP2_HD_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
NGHTTP2_TOKEN_KEEP_ALIVE,
NGHTTP2_TOKEN_PROXY_CONNECTION,
NGHTTP2_TOKEN_UPGRADE,
+ NGHTTP2_TOKEN__PROTOCOL,
} nghttp2_token;
struct nghttp2_hd_entry;
#define NGHTTP2_HD_HUFFMAN_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
}
/* 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 */,
}
/* 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 */,
#define NGHTTP2_HELPER_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <string.h>
}
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)) {
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':
}
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;
}
}
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;
}
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;
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;
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;
}
}
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);
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;
(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;
}
#define NGHTTP2_HTTP_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
#define NGHTTP2_INT_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
#define NGHTTP2_MAP_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
#define NGHTTP2_MEM_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
#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;
#define NGHTTP2_NPN_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
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;
}
#define NGHTTP2_OPTION_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
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;
#define NGHTTP2_OUTBOUND_ITEM_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
#define NGHTTP2_PQ_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
#define NGHTTP2_PRIORITY_SPEC_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
#define NGHTTP2_QUEUE_H
#ifdef HAVE_CONFIG_H
-#include "config.h"
+# include "config.h"
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
#define NGHTTP2_RCBUF_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
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;
}
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;
}
}
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
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);
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;
}
}
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];
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;
}
}
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;
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;
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);
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 "
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");
return rv;
}
- break;
+ return (ssize_t)inlen;
}
switch (iframe->frame.hd.type) {
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);
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");
if (nghttp2_is_fatal(rv)) {
return rv;
}
- break;
+ return (ssize_t)inlen;
}
if (rv == 1) {
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) {
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);
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) {
return rv;
}
- busy = 1;
-
- iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
-
- break;
+ return (ssize_t)inlen;
default:
DEBUGF("recv: extension frame\n");
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;
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);
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;
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);
return rv;
}
+ if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+ return (ssize_t)inlen;
+ }
+
session_inbound_frame_reset(session);
break;
return rv;
}
+ if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+ return (ssize_t)inlen;
+ }
+
session_inbound_frame_reset(session);
break;
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;
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,
return rv;
}
+ if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+ return (ssize_t)inlen;
+ }
+
session_inbound_frame_reset(session);
break;
return rv;
}
+ if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+ return (ssize_t)inlen;
+ }
+
session_inbound_frame_reset(session);
break;
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) &&
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;
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");
return rv;
}
+ if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+ return (ssize_t)inlen;
+ }
+
session_inbound_frame_reset(session);
break;
return rv;
}
+ if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+ return (ssize_t)inlen;
+ }
+
session_inbound_frame_reset(session);
break;
return rv;
}
- busy = 1;
-
- iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
-
- break;
+ return (ssize_t)inlen;
}
/* CONTINUATION won't bear NGHTTP2_PADDED flag */
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);
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(
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;
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 ||
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);
if (nghttp2_is_fatal(rv)) {
return rv;
}
+
+ if (iframe->state == NGHTTP2_IB_IGN_DATA) {
+ return (ssize_t)inlen;
+ }
}
rv = nghttp2_session_add_rst_stream(
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. */
if (nghttp2_is_fatal(rv)) {
return rv;
}
+
+ if (iframe->state == NGHTTP2_IB_IGN_ALL) {
+ return (ssize_t)inlen;
+ }
}
}
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);
}
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;
}
}
+ 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;
}
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) {
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);
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);
#define NGHTTP2_SESSION_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
*/
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 {
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;
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 {
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;
/* 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
* 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.
* 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);
* 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);
* 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.
*
* 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);
#define NGHTTP2_STREAM_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
/* "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 {
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;
#define NGHTTP2_SUBMIT_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
* 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>
-#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
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
# 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
{
$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
}
-# 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
}
_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"
_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 || {
#! /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
{
$debug_cmd
+ _G_rc_run_hooks=false
+
case " $hookable_fns " in
*" $1 "*) ;;
*) func_fatal_error "'$1' does not support hook funcions.n" ;;
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
}
## --------------- ##
# 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 ()
# {
# 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
#
# {
# $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
#
# $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
{
$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
}
# 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 ()
{
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
}
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
;;
--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
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
}
{
$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
}
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`
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)
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
{
$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
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
}
# 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
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" "/" ""
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
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
# 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=: ;;
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" && \
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
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
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.
\$1
_LTECHO_EOF'
}
- ECHO=\"$qECHO\"
+ ECHO=$qECHO
fi
# Very basic option parsing. These options are (a) specific to
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
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"
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"
# -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:
-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"
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)
*)
# 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
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=$?
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=$?
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.
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
# 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
# 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
# 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
# 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
# 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.
# 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'
#! /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
# 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
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 ()
{
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:
-# 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,
*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)
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)'; \
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
-# 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,
*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)
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)'; \
#ifdef HAVE_LIBXML2
-#include <libxml/HTMLparser.h>
+# include <libxml/HTMLparser.h>
#endif // HAVE_LIBXML2
#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>
#include "template.h"
#ifndef O_BINARY
-#define O_BINARY (0)
+# define O_BINARY (0)
#endif // O_BINARY
namespace nghttp2 {
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};
};
}
+#ifndef OPENSSL_NO_NEXTPROTONEG
namespace {
int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
void *arg) {
return SSL_TLSEXT_ERR_OK;
}
} // namespace
+#endif // !OPENSSL_NO_NEXTPROTONEG
namespace {
int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
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);
-# 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,
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@)
@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 \
$(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) \
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)
$(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
*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)
$(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=; \
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)
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$$||'`;\
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)
@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)'; \
$(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
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
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
.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
#include "nghttp2_config.h"
#ifndef _WIN32
-#include <sys/uio.h>
+# include <sys/uio.h>
#endif // !_WIN32
#include <cassert>
*/
#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>
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";
}
return "WINDOW_UPDATE";
case NGHTTP2_ALTSVC:
return "ALTSVC";
+ case NGHTTP2_ORIGIN:
+ return "ORIGIN";
}
std::string s = "extension(0x";
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;
}
#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>
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)
}
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>(
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_; }
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);
// 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() {}
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,
return SSL_TLSEXT_ERR_OK;
}
} // namespace
+#endif // !OPENSSL_NO_NEXTPROTONEG
boost::system::error_code
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();
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);
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 {
}
#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) {
return SSL_TLSEXT_ERR_OK;
},
nullptr);
+#endif // !OPENSSL_NO_NEXTPROTONEG
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
// ALPN selection callback
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) {
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) {
#define BASE64_TEST_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif // HAVE_CONFIG_H
namespace nghttp2 {
#define BUFFER_TEST_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif // HAVE_CONFIG_H
namespace nghttp2 {
#define NGHTTP2_COMP_HELPER_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <jansson.h>
* 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>
#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>
#include "template.h"
#ifndef O_BINARY
-#define O_BINARY (0)
+# define O_BINARY (0)
#endif // O_BINARY
using namespace nghttp2;
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);
}
} // namespace
+#ifndef OPENSSL_NO_NEXTPROTONEG
namespace {
int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
unsigned char *outlen, const unsigned char *in,
return SSL_TLSEXT_ERR_NOACK;
}
} // namespace
+#endif // !OPENSSL_NO_NEXTPROTONEG
namespace {
constexpr char UNIX_PATH_PREFIX[] = "unix:";
-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.
-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>
-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>
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
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.
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.
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
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."
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;
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
<< "%), " << 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)
#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>
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:
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;
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;
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;
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;
}
}
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) {
}
}
+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
#include "memchunk.h"
#include "template.h"
#include "allocator.h"
+#include "base64.h"
namespace nghttp2 {
// 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
HD__HOST,
HD__METHOD,
HD__PATH,
+ HD__PROTOCOL,
HD__SCHEME,
HD__STATUS,
HD_ACCEPT_ENCODING,
HD_CONTENT_TYPE,
HD_COOKIE,
HD_DATE,
+ HD_EARLY_DATA,
HD_EXPECT,
HD_FORWARDED,
HD_HOST,
HD_LINK,
HD_LOCATION,
HD_PROXY_CONNECTION,
+ HD_SEC_WEBSOCKET_ACCEPT,
+ HD_SEC_WEBSOCKET_KEY,
HD_SERVER,
HD_TE,
HD_TRAILER,
// 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
#define SHRPX_HTTP2_TEST_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif // HAVE_CONFIG_H
namespace shrpx {
-# 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,
*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)
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)'; \
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.
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.
* 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>
size_t iov_len; /* Length of data. */
};
#else // !_WIN32
-#include <sys/uio.h>
+# include <sys/uio.h>
#endif // !_WIN32
#include <cassert>
#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 {
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;
#define MEMCHUNK_TEST_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif // HAVE_CONFIG_H
namespace nghttp2 {
#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 {
#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>
#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 {
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); }
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())) {
// 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};
}
} // namespace
+#ifndef OPENSSL_NO_NEXTPROTONEG
namespace {
int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
unsigned char *outlen, const unsigned char *in,
return SSL_TLSEXT_ERR_OK;
}
} // namespace
+#endif // !OPENSSL_NO_NEXTPROTONEG
namespace {
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();
#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>
#define NGHTTP2_CONFIG_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif // HAVE_CONFIG_H
#endif // NGHTTP2_CONFIG_H
*/
#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
*/
int nghttp2_gzip_inflate_finished(nghttp2_gzip *inflater);
-#ifdef __cplusplus
+# ifdef __cplusplus
}
-#endif
+# endif
#endif /* NGHTTP2_GZIP_H */
#define NGHTTP2_GZIP_TEST_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#ifdef __cplusplus
#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>
--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.
* 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>
#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>
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
#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
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)
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;
{
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
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.
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
--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
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.
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>
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.
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
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>
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
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
{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;
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;
}
#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)
#include "shrpx_accept_handler.h"
#ifdef HAVE_UNISTD_H
-#include <unistd.h>
+# include <unistd.h>
#endif // HAVE_UNISTD_H
#include <cerrno>
}
}
+ downstream_->set_request_header_sent(true);
+ auto src = downstream_->get_blocked_request_buf();
+ auto dest = downstream_->get_request_buf();
+ src->remove(*dest);
+
return 0;
}
#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>
// 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);
}
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;
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;
}
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;
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)) {
// 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
#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>
#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"
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;
};
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.
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;
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;
}
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
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()) {
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;
}
} // 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
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
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) {
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;
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;
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;
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;
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;
}
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;
}
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, "
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:
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);
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);
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";
<< (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;
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;
#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>
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;
: 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.
// 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 {
StringRef private_key_file;
StringRef cert_file;
StringRef ciphers;
+ StringRef tls13_ciphers;
bool no_http2_cipher_black_list;
} client;
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
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;
http2_proxy{false},
single_process{false},
single_thread{false},
+ ignore_per_pattern_mruby_error{false},
ev_loop_flags{0} {}
~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;
};
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,
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,
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,
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,
#include "shrpx_config_test.h"
#ifdef HAVE_UNISTD_H
-#include <unistd.h>
+# include <unistd.h>
#endif // HAVE_UNISTD_H
#include <cstdlib>
#define SHRPX_CONFIG_TEST_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif // HAVE_CONFIG_H
namespace shrpx {
#include "shrpx_connection.h"
#ifdef HAVE_UNISTD_H
-#include <unistd.h>
+# include <unistd.h>
#endif // HAVE_UNISTD_H
#include <netinet/tcp.h>
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,
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),
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) {
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);
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)) {
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);
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;
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);
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);
}
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
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);
// 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;
}
struct TLSConnection {
DefaultMemchunks wbuf;
DefaultPeekMemchunks rbuf;
+ // Stores TLSv1.3 early data.
+ DefaultMemchunks earlybuf;
SSL *ssl;
SSL_SESSION *cached_session;
MemcachedRequest *cached_session_lookup_req;
// 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 {
#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>
: 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),
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
);
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);
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) {
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);
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);
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;
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;
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);
}
#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() {
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
+# include <sys/socket.h>
#endif // HAVE_SYS_SOCKET_H
#include <mutex>
#include <vector>
#include <random>
#ifndef NOTHREADS
-#include <future>
+# include <future>
#endif // NOTHREADS
#include <openssl/ssl.h>
#include <ev.h>
#ifdef HAVE_NEVERBLEED
-#include <neverbleed.h>
+# include <neverbleed.h>
#endif // HAVE_NEVERBLEED
#include "shrpx_downstream_connection_pool.h"
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.
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_;
#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"
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),
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;
#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();
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();
}
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());
}
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;
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_) {
}
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;
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;
}
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;
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;
+ }
}
}
}
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
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),
method(-1),
http_major(1),
http_minor(1),
+ connect_proto(CONNECT_PROTO_NONE),
upgrade_request(false),
http2_upgrade_seen(false),
connection_close(false),
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;
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().
// 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();
// 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_; }
// 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,
// 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_;
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
#define SHRPX_DOWNSTREAM_TEST_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif // HAVE_CONFIG_H
namespace shrpx {
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) {
#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"
#include "shrpx_log.h"
#include "http2.h"
#include "util.h"
+#include "ssl_compat.h"
using namespace nghttp2;
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;
}
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.
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();
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.
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();
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));
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);
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;
}
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);
// 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};
#include <netinet/tcp.h>
#ifdef HAVE_UNISTD_H
-#include <unistd.h>
+# include <unistd.h>
#endif // HAVE_UNISTD_H
#include <vector>
: 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),
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;
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";
}
}
downstream->set_response_state(Downstream::HEADER_COMPLETE);
- downstream->check_upgrade_fulfilled();
+ downstream->check_upgrade_fulfilled_http2();
if (downstream->get_upgraded()) {
resp.connection_close = true;
}
case NGHTTP2_SETTINGS: {
if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
+ http2session->on_settings_received(frame);
return 0;
}
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) {
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);
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);
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() {
continue;
}
+ auto &req = downstream->request();
+ if (req.connect_proto && !settings_recved_) {
+ continue;
+ }
+
auto upstream = downstream->get_upstream();
if (dconn->push_request_headers() != 0) {
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();
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
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();
// 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,
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();
#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"
}
}
+ 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) {
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
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) {
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;
}
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;
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;
#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();
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);
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));
}
}
- 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();
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);
}
#include "shrpx_log.h"
#include "http2.h"
#include "util.h"
+#include "ssl_compat.h"
using namespace nghttp2;
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) {
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),
} 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);
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;
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(' ');
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);
// 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: ");
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);
// 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;
}
// 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)) {
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;
// 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
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();
int noop();
+ int process_blocked_request_buf();
+
private:
Connection conn_;
std::function<int(HttpDownstreamConnection &)> on_read_, on_write_,
#include "shrpx_http_test.h"
#ifdef HAVE_UNISTD_H
-#include <unistd.h>
+# include <unistd.h>
#endif // HAVE_UNISTD_H
#include <cstdlib>
#define SHRPX_HTTP_TEST_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif // HAVE_CONFIG_H
namespace shrpx {
#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;
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) {
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()) {
#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();
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();
}
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");
+ }
}
}
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;
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);
#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>
};
} // 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; }
}
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;
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;
// 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;
;
}
+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,
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());
}
#include <sys/types.h>
-#include <sstream>
#include <memory>
#include <vector>
#include <chrono>
#include "shrpx_log_config.h"
#include "tls.h"
#include "template.h"
+#include "util.h"
using namespace nghttp2;
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" : "")
}
#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;
}
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
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) {
#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,
// 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()) {
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);
}
} // 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);
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
}
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)) {
}
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)) {
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);
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)) {
}
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;
}
#define SHRPX_ROUTER_TEST_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif // HAVE_CONFIG_H
namespace shrpx {
#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>
#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>
} // 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) {
return SSL_TLSEXT_ERR_OK;
}
} // namespace
+#endif // !OPENSSL_NO_NEXTPROTONEG
namespace {
int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
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);
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;
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);
}
}
}
-#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]);
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
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
#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,
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.
}
} // 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,
}
} // 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) {
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,
return static_cast<unsigned int>(secret.size());
}
} // namespace
-#endif // !LIBRESSL_IN_USE
+#endif // !OPENSSL_NO_PSK
struct TLSProtocol {
StringRef name;
(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;
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);
}
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
#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);
// 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
<< 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,
<< 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;
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) {
#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;
}
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));
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);
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;
}
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);
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;
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;
}
#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) {
#include <ev.h>
#ifdef HAVE_NEVERBLEED
-#include <neverbleed.h>
+# include <neverbleed.h>
#endif // HAVE_NEVERBLEED
#include "network.h"
#define SHRPX_TLS_TEST_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif // HAVE_CONFIG_H
namespace shrpx {
#include "shrpx_worker.h"
#ifdef HAVE_UNISTD_H
-#include <unistd.h>
+# include <unistd.h>
#endif // HAVE_UNISTD_H
#include <memory>
#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"
}
} // 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(
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;
}
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];
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>();
}
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;
#include <deque>
#include <thread>
#ifndef NOTHREADS
-#include <future>
+# include <future>
#endif // NOTHREADS
#include <openssl/ssl.h>
// 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;
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.
#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>
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;
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 {
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.
}
if (config->single_thread) {
- rv = conn_handler.create_single_worker();
+ rv = conn_handler->create_single_worker();
if (rv != 0) {
return -1;
}
}
#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;
}
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)) {
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
#include "shrpx_worker_test.h"
#ifdef HAVE_UNISTD_H
-#include <unistd.h>
+# include <unistd.h>
#endif // HAVE_UNISTD_H
#include <cstdlib>
#define SHRPX_WORKER_TEST_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif // HAVE_CONFIG_H
namespace shrpx {
*/
#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
#define TEMPLATE_TEST_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif // HAVE_CONFIG_H
namespace nghttp2 {
#define TIMEGM_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#ifdef __cplusplus
#endif /* __cplusplus */
#ifdef HAVE_TIME_H
-#include <time.h>
+# include <time.h>
#endif // HAVE_TIME_H
time_t nghttp2_timegm(struct tm *tm);
#include <openssl/ssl.h>
+#include "ssl_compat.h"
+
namespace nghttp2 {
namespace tls {
// 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;
#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>
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));
if (WSAStringToAddress(addr, af, NULL, (LPSOCKADDR)dst, &size) == 0)
return 1;
return 0;
-#endif
+# endif
}
} // namespace
#endif // _WIN32
} // 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();
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;
}
return -1;
}
- unsigned int mdlen = 32;
+ unsigned int mdlen = EVP_MD_size(meth);
rv = EVP_DigestFinal_ex(ctx, res, &mdlen);
if (rv != 1) {
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) {
#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>
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;
}
*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;
}
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);
// 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.
#define UTIL_TEST_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif // HAVE_CONFIG_H
namespace shrpx {
/* 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>
#define XSI_STRERROR_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <stddef.h>
#! /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
# 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
# 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:
-# 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,
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)
$(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
*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)
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$$||'`;\
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)
@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)'; \
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
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
.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
* 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>
/* 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 */
!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 */
return (int)num_tests_failed;
} else {
printf("CUnit Error: %s\n", CU_get_error_msg());
- return CU_get_error();
+ return (int)CU_get_error();
}
}
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) {
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) {
#define FAILMALLOC_TEST_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
void test_nghttp2_session_send(void);
* 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>
/* 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 */
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",
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) ||
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",
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",
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) ||
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 */
return (int)num_tests_failed;
} else {
printf("CUnit Error: %s\n", CU_get_error_msg());
- return CU_get_error();
+ return (int)CU_get_error();
}
}
#define MALLOC_WRAPPER_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <stdlib.h>
#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);
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;
#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);
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);
#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);
#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);
#define NGHTTP2_MAP_TEST_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
void test_nghttp2_map(void);
#define NGHTTP2_NPN_TEST_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
void test_nghttp2_npn(void);
#define NGHTTP2_PQ_TEST_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
void test_nghttp2_pq(void);
#define NGHTTP2_QUEUE_TEST_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
void test_nghttp2_queue(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) {
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;
nghttp2_outbound_item *item;
nghttp2_nv nv = MAKE_NV(":authority", "example.org");
nghttp2_mem *mem;
+ nghttp2_option *option;
mem = nghttp2_mem_default();
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);
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) {
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;
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) {
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"),
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();
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);
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);
#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);
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);
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);
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);
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);
#define NGHTTP2_STREAM_TEST_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#endif /* NGHTTP2_STREAM_TEST_H */
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);
#define NGHTTP2_TEST_HELPER_H
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
#endif /* HAVE_CONFIG_H */
#include "nghttp2_frame.h"
-# 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,
*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)
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)'; \
-# 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,
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)
*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)
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$$||'`;\
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)'; \
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
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
.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 \
Terence Lee
Zachary Scott
Tomasz Dąbrowski
+ Christopher Aue
+ Masahiro Wakame
+ YAMAMOTO Masaya
-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"),
## 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)
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"
clone_depth: 50
+cache:
+ - win_flex_bison-2.5.10.zip
+
+
environment:
matrix:
# Visual Studio 2015 64bit
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
# 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
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
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,
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
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
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
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)
@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
@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
# 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
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|
# conf.gem 'examples/mrbgems/c_and_ruby_extension_example'
#
# conf.test_runner.command = 'env'
-#
# end
```bash
$ mrdb --version
-mruby 1.3.0 (2017-7-4)
+mruby 1.4.1 (2018-4-27)
```
## 2.2 Basic Operation
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);
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;
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'
3
```
-#### mruby [1.3.0 (2017-7-4)]
+#### mruby [1.4.1 (2018-4-27)]
```
[1, 2, 3]
```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
``` [] ```
-#### mruby [1.3.0 (2017-7-4)]
+#### mruby [1.4.1 (2018-4-27)]
```ArgumentError``` is raised.
true
```
-#### mruby [1.3.0 (2017-7-4)]
+#### mruby [1.4.1 (2018-4-27)]
```
true
nil
```
-#### mruby [1.3.0 (2017-7-4)]
+#### mruby [1.4.1 (2018-4-27)]
```NameError``` is raised.
``` nil ```
-#### mruby [1.3.0 (2017-7-4)]
+#### mruby [1.4.1 (2018-4-27)]
Syntax error
```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.
/* 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 */
/*
** 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
#else
#define MRB_FLOAT_EPSILON DBL_EPSILON
#endif
+#endif
#include "mruby/common.h"
#include <mruby/value.h>
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*);
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;
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 */
mrb_int atexit_stack_len;
} mrb_state;
-
-typedef mrb_value (*mrb_func_t)(mrb_state *mrb, mrb_value);
-
/**
* Defines a new class.
*
*
* 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. |
* | | | 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. |
*
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.
#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
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);
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 *);
#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"))
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
/* 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;
}
#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;
#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)
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:
*
* @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);
/*
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:
*
*/
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 */
# 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
typedef struct mrb_value {
union {
+#ifndef MRB_WITHOUT_FLOAT
mrb_float f;
+#endif
void *p;
mrb_int i;
mrb_sym sym;
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
#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)
#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;
};
#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,
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
}\
} 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)
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:
}
/* 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 {\
}\
}\
} 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)
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*);
#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 */
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;
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);
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);
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
* 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,
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, ...);
struct RBreak {
MRB_OBJECT_HEADER;
- struct iv_tbl *iv;
struct RProc *proc;
mrb_value val;
};
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;
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;
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
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
#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);
};
#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)
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;
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 */
#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)))
/* 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
union {
mrb_int capa;
struct mrb_shared_string *shared;
+ struct RString *fshared;
} aux;
char *ptr;
} heap;
#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
*/
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.
*
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);
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);
/*
#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
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
#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)
/*
* 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;
SET_FLOAT_VALUE(mrb, v, f);
return v;
}
+#endif
static inline mrb_value
mrb_cptr_value(struct mrb_state *mrb, void *p)
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);
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*);
/*
* 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.
/*
* 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.
-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
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
attr_accessor :host_target, :build_target
def initialize(name, build_dir=nil, &block)
+ @endian = nil
@test_runner = Command::CrossTestRunner.new(self)
super
end
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."
@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
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]}"
# 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
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
@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
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
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"
# 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"
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.summary = 'Array class extension'
+ spec.add_test_dependency 'mruby-enumerator', core: 'mruby-enumerator'
end
#
def uniq(&block)
ary = self.dup
- if block
- ary.uniq!(&block)
- else
- ary.uniq!
- end
+ ary.uniq!(&block)
ary
end
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
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
# 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
##
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)
# 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
# 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
# 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
# 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
##
# 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
# 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
# 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
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
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
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))
);
}
{
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) {
}
}
- 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();
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
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
# 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
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)
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
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)
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
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
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
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;
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;
}
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;
}
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;
}
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;
}
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);
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;
}
}
return 0;
}
-
-
len += strlen(dir) + sizeof("/") - 1;
}
- path = mrb_malloc(mrb, len);
+ path = (char*)mrb_malloc(mrb, len);
memset(path, 0, len);
if (strcmp(dir, ".")) {
dirname(mrb_state *mrb, const char *path)
{
size_t len;
- char *p, *dir;
+ const char *p;
+ char *dir;
if (path == NULL) {
return NULL;
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';
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");
}
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;
}
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++) {
continue;
}
- if ((path = build_path(mrb, search_path[i], filename)) == NULL) {
+ if ((path = build_path(mrb, search_path[i], srcname)) == NULL) {
continue;
}
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 */
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);
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;
}
}
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;
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;
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);
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;
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));
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;
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:
#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()
#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()
p(mrb_state *mrb, mrb_value obj, int prompt)
{
mrb_value val;
+ char* msg;
val = mrb_funcall(mrb, obj, "inspect", 0);
if (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);
}
strcpy(last_code_line, line);
strcat(last_code_line, "\n");
MIRB_ADD_HISTORY(line);
- free(line);
+ MIRB_LINE_FREE(line);
#endif
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 */
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,
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);
static void
irep_remove_lv(mrb_state *mrb, mrb_irep *irep)
{
- size_t i;
+ int i;
if (irep->lv) {
mrb_free(mrb, irep->lv);
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)
{
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
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
--- /dev/null
+MRuby::Gem::Specification.new('mruby-compar-ext') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Enumerable module extension'
+end
--- /dev/null
+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
int icapa;
mrb_irep *irep;
- size_t pcapa;
- size_t scapa;
- size_t rcapa;
+ int pcapa, scapa, rcapa;
uint16_t nlocals;
uint16_t nregs;
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);
}
}
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));
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)];
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)
{
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);
}
}
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");
static inline int
new_lit(codegen_scope *s, mrb_value val)
{
- size_t i;
+ int i;
mrb_value *pv;
switch (mrb_type(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];
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];
*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;
static int
new_msym(codegen_scope *s, mrb_sym sym)
{
- size_t i, len;
+ int i, len;
mrb_assert(s->irep);
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;
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)
{
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));
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++;
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;
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();
}
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;
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;
}
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;
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));
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);
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;
}
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);
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) {
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();
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:
}
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);
}
genop(s, MKOP_ABx(OP_ERR, 1, idx));
}
+#ifndef MRB_WITHOUT_FLOAT
static double
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)
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);
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) {
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));
}
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) {
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;
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;
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:
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);
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);
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 {
else {
pop();
}
+ distcheck(s, pos2);
tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
pos2 = tmp;
n = n->cdr;
}
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);
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();
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));
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) {
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;
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 {
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);
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));
}
}
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;
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 {
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();
}
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));
}
}
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);
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++;
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();
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();
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();
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) {
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));
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();
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);
}
}
break;
+#ifndef MRB_WITHOUT_FLOAT
case NODE_FLOAT:
if (val) {
char *p = (char*)tree;
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;
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));
genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
}
else {
+#endif
if (i < MAXARG_sBx && i > -MAXARG_sBx) {
co = MKOP_AsBx(OP_LOADI, cursp(), i);
}
co = MKOP_ABx(OP_LOADL, cursp(), off);
}
genop(s, co);
+#ifndef MRB_WITHOUT_FLOAT
}
+#endif
push();
}
break;
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;
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 */
}
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);
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);
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;
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();
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()));
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) {
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();
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);
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));
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));
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()));
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);
}
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;
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;
}
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;
}
#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[] =
{
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)
{
#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[] =
{
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];
return 0;
}
#line 50 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
-
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)));
}
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))) */
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
%right keyword_not
%right '=' tOP_ASGN
%left modifier_rescue
-%right '?' ':'
+%right '?' ':' tLABEL_TAG
%nonassoc tDOT2 tDOT3
%left tOROP
%left tANDOP
backref_error(p, $1);
$$ = new_begin(p, 0);
}
- ;
+ ;
command_rhs : command_call %prec tOP_ASGN
| command_call modifier_rescue stmt
{
$$ = new_if(p, cond($1), $3, $6);
}
+ | arg '?' arg opt_nl tLABEL_TAG arg
+ {
+ $$ = new_if(p, cond($1), $3, $6);
+ }
| primary
{
$$ = $1;
{
$$ = $2;
}
- | keyword_do_LAMBDA compstmt keyword_end
+ | keyword_do_LAMBDA bodystmt keyword_end
{
$$ = $2;
}
local_nest(p);
}
opt_block_param
- compstmt
+ bodystmt
keyword_end
{
$$ = new_block(p,$3,$4);
$<num>$ = p->lineno;
}
opt_block_param
- compstmt keyword_end
+ bodystmt keyword_end
{
$$ = new_block(p,$3,$4);
SET_LINENO($$, $<num>2);
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;
}
;
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
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);
+ }
}
;
yyerror(parser_state *p, const char *s)
{
char* c;
- int n;
+ size_t n;
if (! p->capture_errors) {
#ifndef MRB_DISABLE_STDIO
yywarn(parser_state *p, const char *s)
{
char* c;
- int n;
+ size_t n;
if (! p->capture_errors) {
#ifndef MRB_DISABLE_STDIO
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);
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) {
}
s++;
if (peeks(p, s)) {
- int len = strlen(s);
+ size_t len = strlen(s);
while (len--) {
if (nextc(p) == '\n') {
#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) */
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++) {
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 */
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, '{')) {
{
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;
}
tokfix(p);
- p->lstate = EXPR_END;
+ p->lstate = EXPR_ENDARG;
end_strterm(p);
if (type & STR_FUNC_XQUOTE) {
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;
}
}
tokfix(p);
pylval.nd = new_str(p, tok(p), toklen(p));
- p->lstate = EXPR_END;
+ p->lstate = EXPR_ENDARG;
return tCHAR;
case '&':
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);
}
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;
}
pylval.nd = new_float(p, tok(p));
return tFLOAT;
+#endif
}
pylval.nd = new_int(p, tok(p), 10);
return tINTEGER;
if (c == ')')
p->lstate = EXPR_ENDFN;
else
- p->lstate = EXPR_ENDARG;
+ p->lstate = EXPR_END;
return c;
case ':':
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()) {
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);
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) {
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;
}
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);
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;
#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;
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;
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;
}
#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);
}
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
##
# 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
##
# 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>
# <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
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
# #=> [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) }
##
# 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 = []
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
##
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
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?
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
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
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
}
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
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)
# 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
#
# +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)
n = offset - 1
enumerator_block_call do |*i|
n += 1
- yield i.__svalue, n
+ block.call i.__svalue, n
end
end
# # => 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
end
obj.args = args
end
- return obj unless block_given?
+ return obj unless block
enumerator_block_call(&block)
end
# 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
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
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
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
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
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;
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];
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++) {
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);
if (GETARG_C(c) != 0) {
break;
}
- {
+ else {
mrb_code arg = search_variable(mrb, irep->syms[GETARG_B(c)], bnest);
if (arg != 0) {
/* must replace */
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;
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); */
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);
}
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);
}
mrb_int i = EXIT_SUCCESS;
mrb_get_args(mrb, "|i", &i);
- exit(i);
+ exit((int)i);
/* not reached */
return mrb_nil_value();
}
/* 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;
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;
}
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);
* 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)
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
elsif none != NONE
none
else
- raise KeyError, "Key not found: #{key}"
+ raise KeyError, "Key not found: #{key.inspect}"
end
else
self[key]
#
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)
#
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|
#
# 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
#
# 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
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
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++) {
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)
{
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
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")
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
--- /dev/null
+script:
+ - "ruby run_test.rb all test"
--- /dev/null
+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.
--- /dev/null
+/*
+** 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 */
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+##
+# 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
--- /dev/null
+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
--- /dev/null
+#!/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
--- /dev/null
+/*
+** 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));
+
+}
--- /dev/null
+/*
+** 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));
+}
--- /dev/null
+/*
+** 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"));
+}
--- /dev/null
+#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)
+{
+}
--- /dev/null
+##
+# 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
--- /dev/null
+##
+# 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
--- /dev/null
+#!/bin/sh
+
+ulimit -n 20
+mruby -e '100.times { File.open "'$0'" }'
--- /dev/null
+##
+# 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
--- /dev/null
+#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());
+}
--- /dev/null
+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
return mrb_convert_to_integer(mrb, arg, base);
}
+#ifndef MRB_WITHOUT_FLOAT
/*
* call-seq:
* Float(arg) -> float
mrb_get_args(mrb, "o", &arg);
return mrb_Float(mrb, arg);
}
+#endif
/*
* call-seq:
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)
{
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
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))
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))
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) {
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);
}
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+class UnboundMethod
+ def owner
+ @owner
+ end
+
+ def name
+ @name
+ end
+end
--- /dev/null
+#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)
+{
+}
--- /dev/null
+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
self
end
end
+
+ def positive?
+ self > 0
+ end
+
+ def negative?
+ self < 0
+ end
end
#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);
}
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
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?
return mrb_ary_new(mrb);
}
+#ifndef MRB_WITHOUT_FLOAT
/*
* call-seq:
* nil.to_f -> 0.0
{
return mrb_float_value(mrb, 0.0);
}
+#endif
/*
* call-seq:
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:
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());
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
1000.times do
objs << {}
end
- objs = nil
ObjectSpace.count_objects(h)
+ objs = nil
GC.start
ObjectSpace.count_objects(h_after)
--- /dev/null
+gem_*
+gem-*
+mrb-*.a
+src/*.o
+/tmp
--- /dev/null
+script:
+ - "ruby run_test.rb all test"
--- /dev/null
+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.
+
--- /dev/null
+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
--- /dev/null
+# 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"
--- /dev/null
+#!/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
--- /dev/null
+/*
+ ** 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)
+{
+}
--- /dev/null
+# 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
#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));
} else
#endif
fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stdout);
+ fflush(stdout);
}
}
{
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;
}
{
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;
}
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
{
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;
}
}
{
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[]. */
#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)
--- /dev/null
+language: c
+sudo: false
+script:
+ - "ruby run_test.rb all test"
--- /dev/null
+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.
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+#!/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
--- /dev/null
+#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
--- /dev/null
+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
--- /dev/null
+#!/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
+}
--- /dev/null
+/*
+** 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
+}
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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?
--- /dev/null
+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?
--- /dev/null
+#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());
+}
--- /dev/null
+#assert('TCPSocket.gethostbyname') do
+#assert('TCPSocket.new') do
+#assert('TCPSocket#close') do
+#assert('TCPSocket#write') do
--- /dev/null
+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
--- /dev/null
+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?
#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)
#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 { \
}
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)",
}
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)",
#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"); \
} \
} 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;
}
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;
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);
}
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));
}
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)) {
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;
}
break;
+#ifndef MRB_WITHOUT_FLOAT
case 'f':
case 'g':
case 'G':
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)) {
else if (flags & (FPLUS|FSPACE))
sign = (flags & FPLUS) ? '+' : ' ';
if (sign)
- ++need;
+ ++need;
if ((flags & FWIDTH) && need < width)
need = width;
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);
}
blen += n;
}
break;
+#endif
}
flags = FNONE;
}
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)
{
*buf++ = c;
*buf = '\0';
}
+#endif
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
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
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
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
# "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
# <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
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)
# 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?
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);
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);
}
{
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:
{
}
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:
* 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;
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)
{
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());
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());
}
+# coding: utf-8
##
# String(Ext) Test
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
#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)
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));
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
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
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;
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++) {
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)
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
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
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);
}
}
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());
if (mrb->exc) {
mrb_print_error(mrb);
+ mrb_close(mrb);
exit(EXIT_FAILURE);
}
mrb_close(core_test);
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)
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}));]
#include <mruby/class.h>
#include <mruby/data.h>
-#ifndef DISABLE_STDIO
+#ifndef MRB_DISABLE_STDIO
#include <stdio.h>
#else
#include <string.h>
/* 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
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;
{ "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",
};
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:
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 */
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);
{
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",
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
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)
m.append_features(self)
m.included(self)
end
+ self
end
def prepend(*args)
m.prepend_features(self)
m.prepended(self)
end
+ self
end
end
class NotImplementedError < ScriptError
end
+class FrozenError < RuntimeError
+end
+
class StopIteration < IndexError
attr_accessor :result
end
#
# 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
#
# 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
#
# 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
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
##
##
# 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__
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
#
# ISO 15.3.2.2.19
def sort(&block)
- self.map{|*val| val.__svalue}.sort
+ self.map{|*val| val.__svalue}.sort(&block)
end
##
--- /dev/null
+##
+# 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")
#
# 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
#
# 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
#
# 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
#
# 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
}
#
# 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
}
#
# 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
}
#
# 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
}
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}"
#
# 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
#
# 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
#
# 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
#
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)
# 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
#
# 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
#
# 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
#
# 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)
#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)
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);
}
}
-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);
}
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");
}
}
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);
}
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;
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");
}
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);
}
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);
}
}
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;
}
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");
&& 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);
}
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;
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
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;
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");
}
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;
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
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)) {
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)
{
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());
}
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... */
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;
}
loc.method_id = ci->mid;
- func(mrb, j, &loc, data);
+ func(mrb, &loc, data);
}
}
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;
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];
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;
static void
count_backtrace_i(mrb_state *mrb,
- int i,
struct backtrace_location *loc,
void *data)
{
static void
pack_backtrace_i(mrb_state *mrb,
- int i,
struct backtrace_location *loc,
void *data)
{
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);
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);
}
#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)
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);
}
}
}
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))
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) {
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);
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);
}
}
-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)
{
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)
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);
}
}
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;
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);
* \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);
}
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;
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);
}
}
}
+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.
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
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 '?':
}
}
break;
+#ifndef MRB_WITHOUT_FLOAT
case 'f':
{
mrb_float *p;
}
}
break;
+#endif
case 'i':
{
mrb_int *p;
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]);
*p = (mrb_int)f;
}
break;
+#endif
case MRB_TT_STRING:
mrb_raise(mrb, E_TYPE_ERROR, "no implicit conversion of String into Integer");
break;
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 '?':
return -1;
p = c->super;
- while(p) {
+ while (p) {
if (p->tt == MRB_TT_ICLASS) {
if (p->mt == m->mt) {
if (!superclass_seen) {
}
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;
}
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;
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:
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;
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));
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();
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);
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();
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;
}
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
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)
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);
}
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);
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);
}
}
{
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;
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)
{
}
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
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;
}
mrb_value *argv;
mrb_int argc, i;
mrb_sym mid;
- struct RProc *method_rproc;
+ mrb_method_t m;
struct RClass *rclass;
int ai;
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);
}
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)
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);
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));
}
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),
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)]),
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;
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++) {
#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;
}
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;
}
}
}
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));
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 };
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)
{
#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);
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);
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 */
}
break;
+#ifndef MRB_WITHOUT_FLOAT
case MRB_TT_FLOAT:
str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT);
{
size += (size_t)len;
}
break;
+#endif
case MRB_TT_STRING:
{
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;
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 */
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 */
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;
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++) {
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) {
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;
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;
{
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 */
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) {
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;
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;
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;
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;
}
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);
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;
}
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) {
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);
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;
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);
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++) {
static mrb_bool
is_lv_defined(mrb_irep *irep)
{
- size_t i;
+ int i;
if (irep->lv) { return TRUE; }
{
mrb_value exc;
mrb_value a;
- int argc;
+ mrb_int argc;
argc = mrb_get_args(mrb, "|o", &a);
if (argc == 0) return self;
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;
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));
}
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);
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);
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;
}
}
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;
}
}
}
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;
}
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
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:
}
#ifdef MRB_WORD_BOXING
+#ifndef MRB_WITHOUT_FLOAT
MRB_API mrb_value
mrb_word_boxing_float_value(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)
#include <mruby.h>
#include <mruby/string.h>
+#ifndef MRB_WITHOUT_FLOAT
struct fmt_args {
mrb_state *mrb;
mrb_value str;
#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;
#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;
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;
if ((t|32)=='a') {
long double round = 8.0;
- int re;
+ ptrdiff_t re;
if (t&32) prefix += 9;
pl += 2;
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;
}
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;
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 */
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;
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;
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;
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;
}
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
struct RException exc;
struct RBreak brk;
#ifdef MRB_WORD_BOXING
+#ifndef MRB_WITHOUT_FLOAT
struct RFloat floatv;
+#endif
struct RCptr cptr;
#endif
} as;
#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)
#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)
#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
size_t i;
size_t e;
mrb_value nil;
- int nregs;
+ mrb_int nregs;
if (c->stack == NULL) return;
e = c->stack - c->stbase;
int i;
mrb_callinfo *ci;
+ start:
if (c->status == MRB_FIBER_TERMINATED) return;
/* mark VM stack */
}
}
/* 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;
}
}
{
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;
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++) {
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:
/* 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);
{
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);
}
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;
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);
}
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) {
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;
}
mrb_int ratio;
mrb_get_args(mrb, "i", &ratio);
- mrb->gc.interval_ratio = ratio;
+ mrb->gc.interval_ratio = (int)ratio;
return mrb_nil_value();
}
mrb_int ratio;
mrb_get_args(mrb, "i", &ratio);
- mrb->gc.step_ratio = ratio;
+ mrb->gc.step_ratio = (int)ratio;
return mrb_nil_value();
}
}
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;
{
mrb_bool iterating = mrb->gc.iterating;
+ mrb_full_gc(mrb);
mrb->gc.iterating = TRUE;
if (iterating) {
gc_each_objects(mrb, &mrb->gc, callback, data);
#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
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:
default:
return FALSE;
}
+#endif
default:
return mrb_eql(mrb, a, b);
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);
}
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);
}
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;
}
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();
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;
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;
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));
}
}
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)));
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]);
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:
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 */
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();
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);
}
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;
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);
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"));
}
#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)
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);
}
/* 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))) {
}
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);
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;
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++) {
{
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);
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));
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) {
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);
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
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
** 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>
#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)
#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)
{
}
return mrb_float(val);
}
+#endif
/*
* call-seq:
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)) {
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 */
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) */
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
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 {
flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), 0, &mod);
return mrb_float_value(mrb, mod);
}
+#endif
/* 15.2.8.3.16 */
/*
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)
{
#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);
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:
{
return mrb_bool_value(isnan(mrb_float(num)));
}
+#endif
/*
* Document-class: Integer
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 */
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
}
/*
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;
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)
{
b = mrb_float_value(mrb, mod);
return mrb_assoc_new(mrb, a, b);
}
+#endif
/* 15.2.8.3.7 */
/*
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();
}
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 */
/*
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;
}
return mrb_float_value(mrb, f);
}
+#endif
}
static mrb_value
*
*/
+#ifndef MRB_WITHOUT_FLOAT
static mrb_value
fix_to_f(mrb_state *mrb, mrb_value num)
{
}
return mrb_fixnum_value(z);
}
+#endif
mrb_value
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 */
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 */
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;
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:
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 */
* 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)
{
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 */
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 */
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);
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);
#ifdef NAN
mrb_define_const(mrb, fl, "NAN", mrb_float_value(mrb, NAN));
#endif
+#endif
+ mrb_define_module(mrb, "Integral");
}
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));
{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"},
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, "#<");
}
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;
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 {
}
}
return mrb_flo_to_fixnum(mrb, val);
+#endif
case MRB_TT_FIXNUM:
if (base != 0) goto 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;
}
return mrb_convert_to_integer(mrb, val, 0);
}
+#ifndef MRB_WITHOUT_FLOAT
MRB_API mrb_value
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)
#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;
char page[];
};
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
struct mrb_pool {
mrb_state *mrb;
struct mrb_pool_page *pages;
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) {
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);
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.");
}
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
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;
}
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)
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 };
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);
#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)
{
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;
}
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;
{
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) {
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();
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 */
#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*);
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)
{
struct alloca_header {
struct alloca_header *next;
- char buf[];
+ char buf[1];
};
MRB_API void*
}
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);
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]));
}
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);
ns->as.heap.ptr[len] = '\0';
}
}
+ RSTR_SET_POOL_FLAG(ns);
+ MRB_SET_FROZEN_FLAG(ns);
return mrb_obj_value(ns);
}
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);
}
# define _CRT_NONSTDC_NO_DEPRECATE
#endif
+#ifndef MRB_WITHOUT_FLOAT
#include <float.h>
+#endif
#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
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;
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) {
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);
}
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;
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)
{
}
}
-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)
{
/* 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);
}
{
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)
/* 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;
}
const unsigned char *ys = (const unsigned char *)memchr(y, *x, n);
if (ys)
- return ys - y;
+ return (mrb_int)(ys - y);
else
return -1;
}
}
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);
}
}
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
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;
}
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);
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);
if (len) {
while (sbeg <= s) {
if (memcmp(s, t, len) == 0) {
- return s - RSTR_PTR(ps);
+ return (mrb_int)(s - RSTR_PTR(ps));
}
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)
}
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)
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);
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);
}
/*
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);
}
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();
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) {
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();
return str_substr(mrb, str, beg, len);
}
-mrb_int
+uint32_t
mrb_str_hash(mrb_state *mrb, mrb_value str)
{
/* 1-8-7 */
key = key*65599 + *p;
p++;
}
- return (mrb_int)(key + (key>>5));
+ return (uint32_t)(key + (key>>5));
}
/* 15.2.10.5.20 */
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);
}
* "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;
}
/* fall through */
case MRB_TT_STRING:
- pos = str_index(mrb, str, sub, pos);
+ pos = str_index_str(mrb, str, sub, pos);
break;
}
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;
}
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;
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;
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)
{
{
return mrb_float_value(mrb, mrb_str_to_dbl(mrb, self, FALSE));
}
+#endif
/* 15.2.10.5.40 */
/*
}
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;
}
}
}
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;
}
}
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;
}
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 */
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());
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.
*/
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,
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.
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;
}
/*
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;
}
/*
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;
}
/*
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;
}
/*
*/
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
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];
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;
/*
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);
}
/*
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;
}
/*
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;
}
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;
}
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;
}
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
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);
}
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)) {
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);
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);
}
}
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;
}
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);
}
{
struct RClass *klass = mrb_class_ptr(mod);
struct RClass *tmp;
- mrb_bool mod_retry = 0;
+ mrb_bool mod_retry = FALSE;
tmp = klass;
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;
}
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;
}
#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
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;
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++;
}
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
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);
}
}
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;
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);
}
mrb->jmp = 0;
}
else {
- struct RProc *p;
+ mrb_method_t m;
struct RClass *c;
mrb_callinfo *ci;
int n;
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));
}
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) {
}
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);
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;
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;
}
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;
}
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
{
struct RProc *p;
mrb_callinfo *ci;
- mrb_int max = 3;
if (mrb_nil_p(blk)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
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();
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:
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) {
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;
}
{
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
{
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
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;
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);
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) {
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;
}
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];
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;
}
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;
}
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) {
/* 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;
/* 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;
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;
}
}
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));
syms = irep->syms;
}
}
- mrb->c->stack[0] = result;
+ mrb->c->stack[0] = recv;
/* pop stackpos */
mrb->c->stack = ci->stackent;
pc = ci->pc;
}
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;
}
/* 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;
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);
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;
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;
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;
}
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]);
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)) {
}
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) {
/* 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;
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;
}
}
if (mrb->exc) {
mrb_callinfo *ci0;
- mrb_value *stk;
L_RAISE:
ci0 = ci = mrb->c->ci;
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;
}
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) {
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;
}
/* 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();
}
}
}
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);
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;
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;
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;
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);
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;
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) {
/* move stack */
value_move(mrb->c->stack, ®s[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) {
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;
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]);
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;
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]);
OP_MATH_BODY(-,mrb_float,mrb_float);
#endif
break;
+#endif
default:
goto L_SEND;
}
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]);
OP_MATH_BODY(*,mrb_float,mrb_float);
#endif
break;
+#endif
default:
goto L_SEND;
}
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;
}
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
{
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);
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
{
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);
#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 */\
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)*/
v = mrb_ary_new_from_values(mrb, 1, ®s[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;
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;
}
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;
}
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);
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);
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;
/* 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;
}
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;
}
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;
else {
exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg);
}
+ ERR_PC_SET(mrb, pc);
mrb_exc_set(mrb, exc);
goto L_RAISE;
}
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);
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) {]
@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
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
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|
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))
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
##
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
assert('BS Block 28') do
assert_equal(10) do
- 3.times{|bl|
+ 3.times{
break 10
}
end
7
end
end
- end
+ end if class_defined?("Float")
end
assert('clone Class') 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
##
# Float ISO Test
+if class_defined?("Float")
+
assert('Float', '15.2.9') do
assert_equal Class, Float.class
end
# Don't raise on large Right Shift
assert_equal(-1, -23.0 >> 128)
end
+
+end # class_defined?("Float")
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
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
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
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
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
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
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
#
# 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
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
# 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
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
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
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
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
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
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
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
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
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
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
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
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'
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>'))
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
("\1" * 100).inspect
end
- assert_equal "\"\\000\"", "\0".inspect
+ assert_equal "\"\\x00\"", "\0".inspect
end
# Not ISO specified
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
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}};
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);
}
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);
}
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);
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)
{
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;
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);
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);
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);
}
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)
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");
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");
}
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
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
_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);
} 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;
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 */
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;
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 */