diff --git a/.github/workflows/annocheck.yml b/.github/workflows/annocheck.yml index 0aa21706160dbc..70e6398b044d33 100644 --- a/.github/workflows/annocheck.yml +++ b/.github/workflows/annocheck.yml @@ -72,7 +72,7 @@ jobs: builddir: build makeup: true - - uses: ruby/setup-ruby@a9bfc2ecf3dd40734a9418f89a7e9d484c32b990 # v1.248.0 + - uses: ruby/setup-ruby@ab177d40ee5483edb974554986f56b33477e21d0 # v1.265.0 with: ruby-version: '3.1' bundler: none diff --git a/.github/workflows/baseruby.yml b/.github/workflows/baseruby.yml index 4537157b830885..7cc357728fc50b 100644 --- a/.github/workflows/baseruby.yml +++ b/.github/workflows/baseruby.yml @@ -48,7 +48,7 @@ jobs: - ruby-3.3 steps: - - uses: ruby/setup-ruby@a9bfc2ecf3dd40734a9418f89a7e9d484c32b990 # v1.248.0 + - uses: ruby/setup-ruby@ab177d40ee5483edb974554986f56b33477e21d0 # v1.265.0 with: ruby-version: ${{ matrix.ruby }} bundler: none diff --git a/.github/workflows/check_dependencies.yml b/.github/workflows/check_dependencies.yml index e8b37d616143b8..28a9f9ec252885 100644 --- a/.github/workflows/check_dependencies.yml +++ b/.github/workflows/check_dependencies.yml @@ -40,7 +40,7 @@ jobs: - uses: ./.github/actions/setup/directories - - uses: ruby/setup-ruby@a9bfc2ecf3dd40734a9418f89a7e9d484c32b990 # v1.248.0 + - uses: ruby/setup-ruby@ab177d40ee5483edb974554986f56b33477e21d0 # v1.265.0 with: ruby-version: '3.1' bundler: none diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index 4a71f1753011a6..61ee5209592d1b 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -30,16 +30,30 @@ permissions: # jobs: make: - runs-on: windows-2022 + runs-on: windows-${{ matrix.os }} name: ${{ github.workflow }} (${{ matrix.msystem }}) env: MSYSTEM: ${{ matrix.msystem }} - MSYS2_ARCH: x86_64 - CHOST: 'x86_64-w64-mingw32' - CFLAGS: '-march=x86-64 -mtune=generic -O3 -pipe' - CXXFLAGS: '-march=x86-64 -mtune=generic -O3 -pipe' + MSYS2_ARCH: >- + ${{ + contains(matrix.msystem, 'arm64') && 'aarch64' || + contains(matrix.msystem, '64') && 'x86_64' || 'i686' + }} + MINGW_PACKAGE_PREFIX: >- + mingw-w${{ + endsWith(matrix.msystem, '64') && '64' || '32' + }}-${{ + startsWith(matrix.msystem, 'clang') && 'clang' || + startsWith(matrix.msystem, 'ucrt') && 'ucrt' || + 'mingw' + }}-${{ + contains(matrix.msystem, 'arm64') && 'aarch64' || + endsWith(matrix.msystem, '64') && 'x86_64' || 'i686' + }} + CFLAGS: '-mtune=generic -O3 -pipe' + CXXFLAGS: '-mtune=generic -O3 -pipe' CPPFLAGS: '-D_FORTIFY_SOURCE=2 -D__USE_MINGW_ANSI_STDIO=1 -DFD_SETSIZE=2048' LDFLAGS: '-pipe' GITPULLOPTIONS: --no-tags origin ${{ github.ref }} @@ -50,8 +64,12 @@ jobs: # To mitigate flakiness of MinGW CI, we test only one runtime that newer MSYS2 uses. # Ruby 3.2 is the first Windows Ruby to use OpenSSL 3.x - msystem: 'UCRT64' + os: 2022 test_task: 'check' test-all-opts: '--name=!/TestObjSpace#test_reachable_objects_during_iteration/' + - msystem: 'CLANGARM64' + os: 11-arm + test_task: 'test' fail-fast: false if: >- @@ -66,25 +84,26 @@ jobs: - uses: msys2/setup-msys2@fb197b72ce45fb24f17bf3f807a388985654d1f2 # v2.29.0 id: msys2 with: - msystem: UCRT64 + msystem: ${{ matrix.msystem }} update: true install: >- git make ruby autoconf - mingw-w64-ucrt-x86_64-gcc - mingw-w64-ucrt-x86_64-ragel - mingw-w64-ucrt-x86_64-openssl - mingw-w64-ucrt-x86_64-libyaml - mingw-w64-ucrt-x86_64-libffi + ${{ env.MINGW_PACKAGE_PREFIX }}-gcc + ${{ env.MINGW_PACKAGE_PREFIX }}-ragel + ${{ env.MINGW_PACKAGE_PREFIX }}-openssl + ${{ env.MINGW_PACKAGE_PREFIX }}-libyaml + ${{ env.MINGW_PACKAGE_PREFIX }}-libffi - name: Set up env id: setup-env working-directory: run: | $msys2 = ${env:MSYS2_LOCATION} - echo $msys2\usr\bin $msys2\ucrt64\bin | + $msystem = ${env:MSYSTEM}.ToLower() + echo $msys2\usr\bin $msys2\$msystem\bin | Tee-Object ${env:GITHUB_PATH} -Append -Encoding utf-8 # Use the fast device for the temporary directory. @@ -96,6 +115,7 @@ jobs: shell: pwsh # cmd.exe does not strip spaces before `|`. env: MSYS2_LOCATION: ${{ steps.msys2.outputs.msys2-location }} + MSYSTEM: ${{ matrix.msystem }} - name: Remove Strawberry Perl pkg-config working-directory: @@ -140,7 +160,7 @@ jobs: I libssl-3-x64.dll group Packages - pacman -Qs mingw-w64-ucrt-x86_64-* | /bin/sed -n "s,local/mingw-w64-ucrt-x86_64-,,p" + pacman -Qs $MINGW_PACKAGE_PREFIX-* | /bin/sed -n "s,local/$MINGW_PACKAGE_PREFIX-,,p" endgroup [ ${#failed[@]} -eq 0 ] @@ -164,6 +184,8 @@ jobs: ../src/configure --disable-install-doc --prefix=/. --build=$CHOST --host=$CHOST --target=$CHOST shell: sh + env: + CHOST: ${{ env.MSYS2_ARCH }}-w64-mingw32 - name: make all timeout-minutes: 30 diff --git a/.github/workflows/modgc.yml b/.github/workflows/modgc.yml index 96b1afa05fc48b..2a99f18e2de6cb 100644 --- a/.github/workflows/modgc.yml +++ b/.github/workflows/modgc.yml @@ -61,7 +61,7 @@ jobs: uses: ./.github/actions/setup/ubuntu if: ${{ contains(matrix.os, 'ubuntu') }} - - uses: ruby/setup-ruby@a9bfc2ecf3dd40734a9418f89a7e9d484c32b990 # v1.248.0 + - uses: ruby/setup-ruby@ab177d40ee5483edb974554986f56b33477e21d0 # v1.265.0 with: ruby-version: '3.1' bundler: none diff --git a/.github/workflows/parse_y.yml b/.github/workflows/parse_y.yml index 04be9f309d1c33..7e850e44576c84 100644 --- a/.github/workflows/parse_y.yml +++ b/.github/workflows/parse_y.yml @@ -58,7 +58,7 @@ jobs: - uses: ./.github/actions/setup/ubuntu - - uses: ruby/setup-ruby@a9bfc2ecf3dd40734a9418f89a7e9d484c32b990 # v1.248.0 + - uses: ruby/setup-ruby@ab177d40ee5483edb974554986f56b33477e21d0 # v1.265.0 with: ruby-version: '3.1' bundler: none diff --git a/.github/workflows/spec_guards.yml b/.github/workflows/spec_guards.yml index 81b242bbca52ae..bfbe8296845fd5 100644 --- a/.github/workflows/spec_guards.yml +++ b/.github/workflows/spec_guards.yml @@ -46,7 +46,7 @@ jobs: steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - uses: ruby/setup-ruby@a9bfc2ecf3dd40734a9418f89a7e9d484c32b990 # v1.248.0 + - uses: ruby/setup-ruby@ab177d40ee5483edb974554986f56b33477e21d0 # v1.265.0 with: ruby-version: ${{ matrix.ruby }} bundler: none diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 86e951bcb77e46..0dfba09bc39556 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -69,7 +69,7 @@ jobs: with: arch: ${{ matrix.arch }} - - uses: ruby/setup-ruby@a9bfc2ecf3dd40734a9418f89a7e9d484c32b990 # v1.248.0 + - uses: ruby/setup-ruby@ab177d40ee5483edb974554986f56b33477e21d0 # v1.265.0 with: ruby-version: '3.1' bundler: none diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 011351161aea07..fcb062a4c1d5c0 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -98,7 +98,7 @@ jobs: run: | echo "WASI_SDK_PATH=/opt/wasi-sdk" >> $GITHUB_ENV - - uses: ruby/setup-ruby@a9bfc2ecf3dd40734a9418f89a7e9d484c32b990 # v1.248.0 + - uses: ruby/setup-ruby@ab177d40ee5483edb974554986f56b33477e21d0 # v1.265.0 with: ruby-version: '3.1' bundler: none diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 38f628243ccff4..9c0f2fe5722bba 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -59,7 +59,7 @@ jobs: - run: md build working-directory: - - uses: ruby/setup-ruby@a9bfc2ecf3dd40734a9418f89a7e9d484c32b990 # v1.248.0 + - uses: ruby/setup-ruby@ab177d40ee5483edb974554986f56b33477e21d0 # v1.265.0 with: # windows-11-arm has only 3.4.1, 3.4.2, 3.4.3, head ruby-version: ${{ !endsWith(matrix.os, 'arm') && '3.1' || '3.4' }} diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml index bf12f80c0eae1a..d9855ee7410b03 100644 --- a/.github/workflows/yjit-ubuntu.yml +++ b/.github/workflows/yjit-ubuntu.yml @@ -127,7 +127,7 @@ jobs: - uses: ./.github/actions/setup/ubuntu - - uses: ruby/setup-ruby@a9bfc2ecf3dd40734a9418f89a7e9d484c32b990 # v1.248.0 + - uses: ruby/setup-ruby@ab177d40ee5483edb974554986f56b33477e21d0 # v1.265.0 with: ruby-version: '3.1' bundler: none diff --git a/.github/workflows/zjit-ubuntu.yml b/.github/workflows/zjit-ubuntu.yml index 02afda470aec47..ec92cd5342588c 100644 --- a/.github/workflows/zjit-ubuntu.yml +++ b/.github/workflows/zjit-ubuntu.yml @@ -106,7 +106,7 @@ jobs: - uses: ./.github/actions/setup/ubuntu - - uses: ruby/setup-ruby@a9bfc2ecf3dd40734a9418f89a7e9d484c32b990 # v1.248.0 + - uses: ruby/setup-ruby@ab177d40ee5483edb974554986f56b33477e21d0 # v1.265.0 with: ruby-version: '3.1' bundler: none diff --git a/configure.ac b/configure.ac index b035e1688a8461..339ee3b2f2e66e 100644 --- a/configure.ac +++ b/configure.ac @@ -272,6 +272,8 @@ AS_CASE(["${build_os}"], ]) AS_CASE(["${target_os}"], [cygwin*|msys*|mingw*|darwin*], [ + ac_ct_OBJCOPY=":" + ac_cv_prog_OBJCOPY=":" ac_cv_prog_ac_ct_OBJCOPY=":" ]) @@ -1236,6 +1238,11 @@ main() ac_cv_header_sys_time_h=no ac_cv_header_sys_times_h=no ac_cv_header_sys_socket_h=no + ac_cv_func_chown=yes + ac_cv_func_getegid=yes + ac_cv_func_geteuid=yes + ac_cv_func_getgid=yes + ac_cv_func_getuid=yes ac_cv_func_execv=yes ac_cv_func_lstat=yes ac_cv_func_times=yes @@ -1248,6 +1255,8 @@ main() ac_cv_func_readlink=yes ac_cv_func_shutdown=yes ac_cv_func_symlink=yes + ac_cv_func_clock_getres=yes + ac_cv_func_clock_gettime=yes ac_cv_lib_crypt_crypt=no ac_cv_func_getpgrp_void=no ac_cv_func_memcmp_working=yes @@ -1264,8 +1273,8 @@ main() AS_IF([test "$target_cpu" = x64], [ ac_cv_func___builtin_setjmp=yes ac_cv_func_round=no + ac_cv_func_tgamma=no ]) - ac_cv_func_tgamma=no AC_CHECK_TYPE([NET_LUID], [], [], [@%:@include @%:@include @@ -4295,6 +4304,24 @@ AS_IF([test "${ARCH_FLAG}"], [ CXXFLAGS=`echo "$CXXFLAGS" | sed "s| *$archflagpat"'||'` LDFLAGS=`echo "$LDFLAGS" | sed "s| *$archflagpat"'||'` ]) +AS_CASE([" $rb_cv_warnflags "], [*" -Wshorten-64-to-32 "*|*" -Werror=shorten-64-to-32 "*], [ + voidp_ll= + AS_CASE([$ac_cv_sizeof_voidp], + [SIZEOF_LONG_LONG], [voidp_ll=true], + [@<:@0-9@:>@*], [ + AS_IF([test $ac_cv_sizeof_voidp -gt $ac_cv_sizeof_long], [voidp_ll=true]) + ]) + AS_IF([test "$voidp_ll"], [ + # Disable the shorten-64-to-32 warning for now, because it currently + # generates a lot of warnings on platforms where `sizeof(void*)` is + # larger than `sizeof(long)`. + # + # TODO: Replace `long` with `ptrdiff_t` or something in the all sources. + rb_cv_warnflags=`echo "$rb_cv_warnflags" | + sed -e 's/ -W\(shorten-64-to-32 \)/ -Wno-\1/' \ + -e 's/ -Werror=\(shorten-64-to-32 \)/ -Wno-\1/'` + ]) +]) rb_cv_warnflags=`echo "$rb_cv_warnflags" | sed 's/^ *//;s/ *$//'` warnflags="$rb_cv_warnflags" AC_SUBST(cppflags)dnl diff --git a/include/ruby/win32.h b/include/ruby/win32.h index 31dc13e93251fb..69e92ed9ffa1ba 100644 --- a/include/ruby/win32.h +++ b/include/ruby/win32.h @@ -126,28 +126,30 @@ typedef unsigned int uintptr_t; #define O_SHARE_DELETE 0x20000000 /* for rb_w32_open(), rb_w32_wopen() */ typedef int clockid_t; -#if defined(__MINGW32__) -/* I don't know why but these return some strange values. */ -#undef CLOCK_PROCESS_CPUTIME_ID -#undef CLOCK_THREAD_CPUTIME_ID -#undef CLOCK_REALTIME_COARSE -#endif -/* defined in win32/win32.c for old versions */ -#if !defined(__MINGW32__) || !defined(HAVE_CLOCK_GETTIME) -# define HAVE_CLOCK_GETTIME 1 -# define NEED_CLOCK_GETTIME 1 -#endif -#if !defined(__MINGW32__) || !defined(HAVE_CLOCK_GETRES) -# define HAVE_CLOCK_GETRES 1 -# define NEED_CLOCK_GETRES 1 -#endif +/* + * Since we use our versions in win32/win32.c, not to depend on yet + * another DLL, prefix our versions not to conflict with inline + * versions provided in time.h. + */ +#define clock_gettime rb_w32_clock_gettime +#define clock_getres rb_w32_clock_getres + #ifndef CLOCK_REALTIME # define CLOCK_REALTIME 0 #endif #ifndef CLOCK_MONOTONIC # define CLOCK_MONOTONIC 1 #endif +#ifndef CLOCK_PROCESS_CPUTIME_ID +# define CLOCK_PROCESS_CPUTIME_ID 2 +#endif +#ifndef CLOCK_THREAD_CPUTIME_ID +# define CLOCK_THREAD_CPUTIME_ID 3 +#endif +#ifndef CLOCK_REALTIME_COARSE +# define CLOCK_REALTIME_COARSE 4 +#endif #undef utime #undef lseek diff --git a/lib/ipaddr.rb b/lib/ipaddr.rb index 1ad7980e9e6480..fca75deff95ebf 100644 --- a/lib/ipaddr.rb +++ b/lib/ipaddr.rb @@ -721,8 +721,8 @@ def in_addr(addr) octets = addr.split('.') end octets.inject(0) { |i, s| - (n = s.to_i) < 256 or raise InvalidAddressError, "invalid address: #{@addr}" - (s != '0') && s.start_with?('0') and raise InvalidAddressError, "zero-filled number in IPv4 address is ambiguous: #{@addr}" + (n = s.to_i) < 256 or raise InvalidAddressError, "invalid address: #{addr}" + (s != '0') && s.start_with?('0') and raise InvalidAddressError, "zero-filled number in IPv4 address is ambiguous: #{addr}" i << 8 | n } end diff --git a/test/test_ipaddr.rb b/test/test_ipaddr.rb index 64927a1444d7cb..005927cd054cd9 100644 --- a/test/test_ipaddr.rb +++ b/test/test_ipaddr.rb @@ -620,4 +620,16 @@ def test_hash assert_equal(true, s.include?(a5)) assert_equal(true, s.include?(a6)) end + + def test_raises_invalid_address_error_with_error_message + e = assert_raise(IPAddr::InvalidAddressError) do + IPAddr.new('192.168.0.1000') + end + assert_equal('invalid address: 192.168.0.1000', e.message) + + e = assert_raise(IPAddr::InvalidAddressError) do + IPAddr.new('192.168.01.100') + end + assert_equal('zero-filled number in IPv4 address is ambiguous: 192.168.01.100', e.message) + end end diff --git a/tool/lib/core_assertions.rb b/tool/lib/core_assertions.rb index 7e6dc8e6537bd6..1288b8c9aae132 100644 --- a/tool/lib/core_assertions.rb +++ b/tool/lib/core_assertions.rb @@ -838,6 +838,9 @@ def assert_all_assertions_foreach(msg = nil, *keys, &block) rescue # Constants may be defined but not implemented, e.g., mingw. else + unless Process.clock_getres(clk) < 1.0e-03 + next # needs msec precision + end PERFORMANCE_CLOCK = clk end end diff --git a/win32/install-msys-packages.cmd b/win32/install-msys-packages.cmd new file mode 100755 index 00000000000000..d3adbaf5956651 --- /dev/null +++ b/win32/install-msys-packages.cmd @@ -0,0 +1,29 @@ +::- Install msys packages for rubygems +::- The dependencies are taken from vcpkg.json to share the common info. + +@setlocal EnableExtensions DisableDelayedExpansion || exit /b - 1 +@set PROMPT=$h$e[96m$g$e[39m$s +@set script=%0 +@call set "srcdir=%%script:\win32\%~nx0=%%" + +@if not defined MINGW_PACKAGE_PREFIX ( + ::- Enable msys environment by ridk (from RubyInstaller-DevKit) + where ridk >nul 2>&1 || ( + (echo MINGW_PACKAGE_PREFIX is not set, you have to enable development environment.) 1>&2 + exit /b 1 + ) + call ridk enable %* + echo: +) else if not "%1" == "" ( + ::- Switch msys environment by ridk (from RubyInstaller-DevKit) + call ridk enable %* + echo: +) + +@set pkgs= +@( + for /f %%I in ('powershell -c "(ConvertFrom-Json $input).dependencies"') do @( + call set "pkgs=%%pkgs%% %%MINGW_PACKAGE_PREFIX%%-%%%%I" + ) +) < "%srcdir%\vcpkg.json" +pacman -S --needed --noconfirm %pkgs:~1% diff --git a/win32/win32.c b/win32/win32.c index 1a5308337a05d3..c13c4e17320268 100644 --- a/win32/win32.c +++ b/win32/win32.c @@ -4761,20 +4761,39 @@ gettimeofday(struct timeval *tv, struct timezone *tz) return 0; } -#ifdef NEED_CLOCK_GETTIME +/* License: Ruby's */ +static FILETIME +filetimes_plus(FILETIME t1, FILETIME t2) +{ + ULARGE_INTEGER i1 = {.u = {.LowPart = t1.dwLowDateTime, .HighPart = t1.dwHighDateTime}}; + ULARGE_INTEGER i2 = {.u = {.LowPart = t2.dwLowDateTime, .HighPart = t2.dwHighDateTime}}; + ULARGE_INTEGER i = {.QuadPart = i1.QuadPart + i2.QuadPart}; + return (FILETIME){.dwLowDateTime = i.LowPart, .dwHighDateTime = i.HighPart}; +} + +static void +filetime_to_timespec(FILETIME ft, struct timespec *sp) +{ + long subsec; + sp->tv_sec = filetime_split(&ft, &subsec); + sp->tv_nsec = subsec * 100; +} + +/* License: Ruby's */ +static const long secs_in_ns = 1000000000; + /* License: Ruby's */ int clock_gettime(clockid_t clock_id, struct timespec *sp) { switch (clock_id) { case CLOCK_REALTIME: + case CLOCK_REALTIME_COARSE: { FILETIME ft; - long subsec; GetSystemTimePreciseAsFileTime(&ft); - sp->tv_sec = filetime_split(&ft, &subsec); - sp->tv_nsec = subsec * 100; + filetime_to_timespec(ft, sp); return 0; } case CLOCK_MONOTONIC: @@ -4790,10 +4809,28 @@ clock_gettime(clockid_t clock_id, struct timespec *sp) return -1; } sp->tv_sec = count.QuadPart / freq.QuadPart; - if (freq.QuadPart < 1000000000) - sp->tv_nsec = (count.QuadPart % freq.QuadPart) * 1000000000 / freq.QuadPart; + if (freq.QuadPart < secs_in_ns) + sp->tv_nsec = (count.QuadPart % freq.QuadPart) * secs_in_ns / freq.QuadPart; else - sp->tv_nsec = (long)((count.QuadPart % freq.QuadPart) * (1000000000.0 / freq.QuadPart)); + sp->tv_nsec = (long)((count.QuadPart % freq.QuadPart) * ((double)secs_in_ns / freq.QuadPart)); + return 0; + } + case CLOCK_PROCESS_CPUTIME_ID: + case CLOCK_THREAD_CPUTIME_ID: + { + FILETIME ct, et, kt, ut; + BOOL ok; + if (clock_id == CLOCK_PROCESS_CPUTIME_ID) { + ok = GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut); + } + else { + ok = GetThreadTimes(GetCurrentThread(), &ct, &et, &kt, &ut); + } + if (!ok) { + errno = map_errno(GetLastError()); + return -1; + } + filetime_to_timespec(filetimes_plus(kt, ut), sp); return 0; } default: @@ -4801,15 +4838,14 @@ clock_gettime(clockid_t clock_id, struct timespec *sp) return -1; } } -#endif -#ifdef NEED_CLOCK_GETRES /* License: Ruby's */ int clock_getres(clockid_t clock_id, struct timespec *sp) { switch (clock_id) { case CLOCK_REALTIME: + case CLOCK_REALTIME_COARSE: { sp->tv_sec = 0; sp->tv_nsec = 1000; @@ -4823,7 +4859,15 @@ clock_getres(clockid_t clock_id, struct timespec *sp) return -1; } sp->tv_sec = 0; - sp->tv_nsec = (long)(1000000000.0 / freq.QuadPart); + sp->tv_nsec = (long)((double)secs_in_ns / freq.QuadPart); + return 0; + } + case CLOCK_PROCESS_CPUTIME_ID: + case CLOCK_THREAD_CPUTIME_ID: + { + const int frames_in_sec = 60; + sp->tv_sec = 0; + sp->tv_nsec = (long)(secs_in_ns / frames_in_sec); return 0; } default: @@ -4831,7 +4875,6 @@ clock_getres(clockid_t clock_id, struct timespec *sp) return -1; } } -#endif /* License: Ruby's */ static char *