From: Michael Wallner Date: Thu, 3 Dec 2020 10:53:42 +0000 (+0100) Subject: msvc support X-Git-Tag: 1.1.0-beta1~77 X-Git-Url: https://git.m6w6.name/?a=commitdiff_plain;h=aeac01630eed825d8a624e7fd7c107d47bb2e40c;p=awesomized%2Flibmemcached msvc support --- diff --git a/.github/workflows/cmake-build-ci.gen b/.github/workflows/cmake-build-ci.gen index d7f7679e..1c758836 100755 --- a/.github/workflows/cmake-build-ci.gen +++ b/.github/workflows/cmake-build-ci.gen @@ -6,8 +6,10 @@ echo "# Generated file; do not edit!\n"; const DEF = [ "os" => "Linux", "Linux" => "ubuntu-20.04", + "Windows" => "windows-2019", "macOS" => "macos-10.15", "ubuntu-20.04" => "gnu", + "windows-2019" => "msvc", "macos-10.15" => "clang", "gnu" => [ "ver" => "cur", @@ -19,6 +21,11 @@ const DEF = [ "CC" => "clang", "CXX" => "clang++", ], + "msvc" => [ // dummy + "ver" => "cur", + "CC" => "msvc", + "CXX" => "msvc", + ], ]; const ENV = [ "ubuntu-20.04" => [ @@ -74,7 +81,16 @@ const MAP = [ ] ] ] - ] + ], + "windows-2019" => [ + 'env.CC_VND' => [ + "msvc" => [ + 'env.CC_VER' => [ + "cur" => "2019", + ], + ], + ], + ], ] ]; @@ -294,6 +310,29 @@ jobs: - uses: codecov/codecov-action@v1.0.13 + # win build + win-msvc: + name: win-msvc (, , ) + runs-on: # + env: + CMAKE_BUILD_TYPE: Release + BUILD_TESTING: "OFF" + BISON_ROOT: "C:/msys64/usr" + FLEX_ROOT: "C:/msys64/usr" + OS_VND: Windows + OS_VER: # + CC_VND: # + CC_VER: # + continue-on-error: true + steps: + - uses: actions/checkout@v2 + - name: Generate build tree (${{ env.CMAKE_BUILD_TYPE }}) + run: cmake --config ${{ env.CMAKE_BUILD_TYPE }} -S . -B build + - name: Build all with ${{ env.CC_VND }} + run: cmake --build build --config ${{ env.CMAKE_BUILD_TYPE }} + - name: Install + run: cmake --install build --prefix installed --config ${{ env.CMAKE_BUILD_TYPE }} + # release builds release: strategy: diff --git a/.github/workflows/cmake-build-ci.yml b/.github/workflows/cmake-build-ci.yml index 4a52dc76..1b45af18 100644 --- a/.github/workflows/cmake-build-ci.yml +++ b/.github/workflows/cmake-build-ci.yml @@ -6,7 +6,7 @@ on: - "include/**" - "src/**" - "test/**" - - "**CMake**" + - "CMake*" - ".github/workflows/cmake-build-ci*" branches-ignore: - gh-pages @@ -346,6 +346,29 @@ jobs: --data-urlencode "message=Github [${GITHUB_REPOSITORY}](https://github.com/${GITHUB_REPOSITORY}/commits/${REF}) (${REF}) [failure](https://github.com/m6w6/libmemcached/actions/runs/${GITHUB_RUN_ID}) (${ImageOS}/${CC:-${CC_VND}-${CC_VER}})" - uses: codecov/codecov-action@v1.0.13 + # win build + win-msvc: + name: win-msvc (windows-2019, msvc, cur) + runs-on: windows-2019 # + env: + CMAKE_BUILD_TYPE: Release + BUILD_TESTING: "OFF" + BISON_ROOT: "C:/msys64/usr" + FLEX_ROOT: "C:/msys64/usr" + OS_VND: Windows + OS_VER: windows-2019 # + CC_VND: msvc # + CC_VER: cur # + continue-on-error: true + steps: + - uses: actions/checkout@v2 + - name: Generate build tree (${{ env.CMAKE_BUILD_TYPE }}) + run: cmake --config ${{ env.CMAKE_BUILD_TYPE }} -S . -B build + - name: Build all with ${{ env.CC_VND }} + run: cmake --build build --config ${{ env.CMAKE_BUILD_TYPE }} + - name: Install + run: cmake --install build --prefix installed --config ${{ env.CMAKE_BUILD_TYPE }} + # release builds release: strategy: @@ -427,6 +450,11 @@ jobs: echo CC="clang-6.0" >> ${GITHUB_ENV} echo CXX="clang++-6.0" >> ${GITHUB_ENV} echo CXXFLAGS="-stdlib=libc++" >> ${GITHUB_ENV} + - name: Prepare environment (for cur msvc on windows-2019) + if: (env.OS_VER=='windows-2019') && (env.CC_VND=='msvc') && (env.CC_VER=='cur') + run: | + echo CC="msvc2019" >> ${GITHUB_ENV} + echo CXX="msvc2019" >> ${GITHUB_ENV} - name: Install dependencies (Linux) if: runner.os == 'Linux' run: | diff --git a/CMake/CheckDebug.cmake b/CMake/CheckDebug.cmake index b83406d8..fb188529 100644 --- a/CMake/CheckDebug.cmake +++ b/CMake/CheckDebug.cmake @@ -32,7 +32,7 @@ macro(check_sanitizer VAR NAME LIB) endif() endmacro() -if(CMAKE_BUILD_TYPE STREQUAL "Debug") +if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT MSVC) add_definitions(-DDEBUG=1) if(CMAKE_CXX_FLAGS MATCHES --coverage) message("-- Coverage build detected!") diff --git a/CMake/CheckThreads.cmake b/CMake/CheckThreads.cmake index 785d8a6e..8c79c2df 100644 --- a/CMake/CheckThreads.cmake +++ b/CMake/CheckThreads.cmake @@ -1,3 +1,4 @@ +configure_define(HAVE_PTHREAD_H) set(THREADS_PREFER_PTHREAD_FLAG ON) set(CMAKE_THREAD_PREFER_PTHREAD ON) -find_package(Threads REQUIRED) +find_package(Threads) diff --git a/CMake/_Include.cmake b/CMake/_Include.cmake index 7c1b546d..6f9a092b 100644 --- a/CMake/_Include.cmake +++ b/CMake/_Include.cmake @@ -118,21 +118,21 @@ configure_set(HAVE_HSIEH_HASH ${ENABLE_HASH_HSIEH}) check_include(alloca.h) check_include(arpa/inet.h) check_include(dlfcn.h) +check_include(getopt.h) +check_include(libgen.h) check_include(netdb.h) check_include(poll.h) check_include(strings.h) +check_include(sys/poll.h) check_include(sys/socket.h) check_include(sys/time.h) check_include(sys/un.h) check_include(unistd.h) -if(WIN32) - check_include(io.h) - check_include(winsock2.h) - check_include(ws2tcpip.h) -endif() - check_type(in_port_t netinet/in.h) +check_type(pid_t sys/types.h) +check_type(ssize_t sys/types.h) +check_type("struct msghdr" sys/socket.h) check_cxx_symbol(abi::__cxa_demangle cxxabi.h) check_symbol(fcntl fcntl.h) @@ -142,6 +142,7 @@ check_symbol(MSG_MORE sys/socket.h) check_symbol(MSG_NOSIGNAL sys/socket.h) check_symbol(SO_RCVTIMEO sys/socket.h) check_symbol(SO_SNDTIMEO sys/socket.h) +check_symbol(sendmsg sys/socket.h) check_symbol(setenv stdlib.h) check_symbol(strerror_r string.h) check_c_source(" @@ -152,3 +153,11 @@ check_c_source(" }" HAVE_STRERROR_R_CHAR_P ) + +if(WIN32) + check_include(io.h) + check_include(winsock2.h) + check_include(ws2tcpip.h) + + check_symbol(htonll winsock2.h) +endif() diff --git a/CMakeConfig.txt b/CMakeConfig.txt index 4d17185f..b8c60a76 100644 --- a/CMakeConfig.txt +++ b/CMakeConfig.txt @@ -80,10 +80,6 @@ if(BUILD_DOCS) CACHE STRING "append verbatim code to sphinx' conf.py") endif() -# legacy - -set(HAVE_VISIBILITY 1) - # modules list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMake") diff --git a/README.md b/README.md index 9a6b9d3f..e881e057 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,20 @@ set any preferred options. make sudo make install +#### Requirements + +* CMake 3.9+ +* A C++11 compiler +* GNU Bison 2.3+ and Flex + +##### Optional dependencies + +* A C++17 compiler (tests: required) +* Intel's libtbb (tests: optional for GCC's stdlib parallelism support) +* PThreads (tests, contrib/bin/memaslap, libmemcachedutil/pool) +* libevent (contrib/bin/memaslap) +* Cyrus' libsasl2 (libmemacached/sasl) + ## Testing [![Codecov Badge]](https://codecov.io/gh/m6w6/libmemcached) diff --git a/contrib/bin/memaslap/CMakeLists.txt b/contrib/bin/memaslap/CMakeLists.txt index cbc44f60..abd3c83d 100644 --- a/contrib/bin/memaslap/CMakeLists.txt +++ b/contrib/bin/memaslap/CMakeLists.txt @@ -1,4 +1,4 @@ -if(ENABLE_MEMASLAP) +if(ENABLE_MEMASLAP AND CMAKE_USE_PTHREADS_INIT) add_definitions(-D_GNU_SOURCE) include(CheckAtomics) diff --git a/include/libmemcached-1.0/configure.h.in b/include/libmemcached-1.0/configure.h.in index 91e3f1ce..fcda4200 100644 --- a/include/libmemcached-1.0/configure.h.in +++ b/include/libmemcached-1.0/configure.h.in @@ -18,9 +18,12 @@ #cmakedefine01 LIBMEMCACHED_ENABLE_DEPRECATED #cmakedefine01 LIBMEMCACHED_WITH_SASL_SUPPORT -#cmakedefine HAVE_IN_PORT_T 1 #cmakedefine HAVE_NETDB_H 1 +#cmakedefine HAVE_IN_PORT_T 1 +#cmakedefine HAVE_PID_T 1 +#cmakedefine HAVE_SSIZE_T 1 + #define LIBMEMCACHED_VERSION_STRING "@LIBMEMCACHED_VERSION@" #define LIBMEMCACHED_VERSION_HEX @LIBMEMCACHED_VERSION_HEX@ diff --git a/include/libmemcached-1.0/memcached.h b/include/libmemcached-1.0/memcached.h index f2ef0642..6f87e438 100644 --- a/include/libmemcached-1.0/memcached.h +++ b/include/libmemcached-1.0/memcached.h @@ -34,6 +34,13 @@ #include +#ifndef HAVE_PID_T +typedef int pid_t; +#endif +#ifndef HAVE_SSIZE_T +typedef long int ssize_t; +#endif + #include "libmemcached-1.0/visibility.h" #include "libmemcached-1.0/configure.h" #include "libmemcached-1.0/platform.h" diff --git a/include/libmemcachedprotocol-0.0/binary.h b/include/libmemcachedprotocol-0.0/binary.h index 43782408..837f3adf 100644 --- a/include/libmemcachedprotocol-0.0/binary.h +++ b/include/libmemcachedprotocol-0.0/binary.h @@ -30,7 +30,10 @@ * host order. */ #ifdef __cplusplus +# include extern "C" { +#else +# include #endif /** diff --git a/include/libmemcachedprotocol-0.0/callback.h b/include/libmemcachedprotocol-0.0/callback.h index ceb441bf..64684329 100644 --- a/include/libmemcachedprotocol-0.0/callback.h +++ b/include/libmemcachedprotocol-0.0/callback.h @@ -15,6 +15,17 @@ #pragma once +#ifdef __cplusplus +# include +extern "C" { +#else +# include +#endif + +#ifdef _MSC_VER +# undef interface +#endif + /** * Callback to send data back from a successful GET/GETQ/GETK/GETKQ command * @@ -362,3 +373,7 @@ typedef struct { memcached_binary_protocol_callback_v1_st v1; } interface; } memcached_binary_protocol_callback_st; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/include/libmemcachedprotocol-0.0/handler.h b/include/libmemcachedprotocol-0.0/handler.h index 74bdee3d..5f009056 100644 --- a/include/libmemcachedprotocol-0.0/handler.h +++ b/include/libmemcachedprotocol-0.0/handler.h @@ -16,8 +16,16 @@ #pragma once #include -#if !defined(__cplusplus) +#ifndef HAVE_SSIZE_T +typedef long int ssize_t; +#endif + +#ifndef __cplusplus # include +# include +# include +#else +# include #endif #include "libmemcached-1.0/visibility.h" diff --git a/src/bin/common/CMakeLists.txt b/src/bin/common/CMakeLists.txt index c5b4faa5..84443e26 100644 --- a/src/bin/common/CMakeLists.txt +++ b/src/bin/common/CMakeLists.txt @@ -1,4 +1,7 @@ add_library(libclient_common STATIC options.cpp checks.hpp random.hpp time.hpp) +if(NOT HAVE_GETOPT_H) + target_sources(libclient_common PRIVATE ../../win32/getopt.c) +endif() add_library(client_common ALIAS libclient_common) set_target_properties(libclient_common PROPERTIES CXX_STANDARD ${CXX_STANDARD}) target_link_libraries(libclient_common PUBLIC libmemcached) diff --git a/src/bin/common/options.cpp b/src/bin/common/options.cpp index 8c520b8f..c62020a3 100644 --- a/src/bin/common/options.cpp +++ b/src/bin/common/options.cpp @@ -149,7 +149,7 @@ bool client_options::parse(int argc, char **argv, char ***argp) { bool client_options::apply(memcached_st *memc) { #ifdef _WIN32 WSADATA wsaData; - if (WSAStartup(MAKEWORD(2, 0), &wsaData)) { + if (WSAStartup(MAKEWORD(2, 2), &wsaData)) { std::cerr << "Socket Initialization Error.\n"; return false; } diff --git a/src/bin/common/options.hpp b/src/bin/common/options.hpp index 5682c786..7d2b3e38 100644 --- a/src/bin/common/options.hpp +++ b/src/bin/common/options.hpp @@ -19,11 +19,16 @@ #include #include #include -#include #include #include #include +#ifdef HAVE_GETOPT_H +# include +#elif defined _MSC_VER +# include "win32/getopt.h" +#endif + #include "libmemcached/common.h" class client_options { diff --git a/src/bin/common/random.hpp b/src/bin/common/random.hpp index eab844f1..35be5ad9 100644 --- a/src/bin/common/random.hpp +++ b/src/bin/common/random.hpp @@ -17,6 +17,7 @@ #include "time.hpp" #include +#undef max class random64 { public: @@ -27,8 +28,8 @@ public: , dst{} {} - typ operator()(typ min = 0, typ max = std::numeric_limits::max()) { - return (dst(gen) % (max - min)) + min; + typ operator()(typ min_ = 0, typ max_ = std::numeric_limits::max()) { + return (dst(gen) % (max_ - min_)) + min_; } void fill(char *buf, size_t len, diff --git a/src/bin/memcapable.cc b/src/bin/memcapable.cc index a3a4e3a2..1b9af02a 100644 --- a/src/bin/memcapable.cc +++ b/src/bin/memcapable.cc @@ -17,12 +17,6 @@ #include "mem_config.h" -#ifdef HAVE_POLL_H -# include -#else -# include "poll/poll.h" -#endif - #include #include #include @@ -31,14 +25,19 @@ #include #include #include -#include +#include #include #include #if HAVE_UNISTD_H # include #endif -#include "libmemcached-1.0/memcached.h" +#ifndef HAVE_GETOPT_H +# include "../../win32/getopt.h" +#endif + +#include "libmemcached-1.0/memcached.h" +#include "libmemcached/poll.h" #include "libmemcached/socket.hpp" #include "libmemcachedprotocol-0.0/binary.h" #include "libmemcached/byteorder.h" @@ -176,13 +175,13 @@ static memcached_socket_t connect_server(const char *hostname, const char *port) return sock; } -static ssize_t timeout_io_op(memcached_socket_t fd, short direction, void *buf, size_t len) { +static ssize_t timeout_io_op(memcached_socket_t fd, short direction, const char *buf, size_t len) { ssize_t ret; if (direction == POLLOUT) { ret = send(fd, buf, len, 0); } else { - ret = recv(fd, buf, len, 0); + ret = recv(fd, const_cast(buf), len, 0); } if (ret == SOCKET_ERROR && get_socket_errno() == EWOULDBLOCK) { @@ -196,7 +195,7 @@ static ssize_t timeout_io_op(memcached_socket_t fd, short direction, void *buf, if (direction == POLLOUT) { ret = send(fd, buf, len, 0); } else { - ret = recv(fd, buf, len, 0); + ret = recv(fd, const_cast(buf), len, 0); } } else if (err == 0) { errno = ETIMEDOUT; @@ -251,7 +250,7 @@ static enum test_return retry_write(const void *buf, size_t len) { do { size_t num_bytes = len - offset; - ssize_t nw = timeout_io_op(sock, POLLOUT, (void *) (ptr + offset), num_bytes); + ssize_t nw = timeout_io_op(sock, POLLOUT, (ptr + offset), num_bytes); if (nw == -1) { verify(get_socket_errno() == EINTR || get_socket_errno() == EAGAIN); } else { @@ -1882,7 +1881,14 @@ int main(int argc, char **argv) { return EXIT_FAILURE; } - //initialize_sockets(); + #ifdef _WIN32 + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData)) { + fprintf(stderr, "Socket Initialization Error.\n"); + return EXIT_FAILURE; + } +#endif // _WIN32 + sock = connect_server(hostname, port); if (sock == INVALID_SOCKET) { fprintf(stderr, "Failed to connect to <%s:%s>: %s\n", hostname, port, @@ -1958,5 +1964,9 @@ int main(int argc, char **argv) { fprintf(stderr, "%d of %d tests failed\n", failed, total); } +#ifdef _WIN32 + WSACleanup(); +#endif + return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/bin/memcp.cc b/src/bin/memcp.cc index eb7151e6..7ad86787 100644 --- a/src/bin/memcp.cc +++ b/src/bin/memcp.cc @@ -25,10 +25,20 @@ #include #include #include -#include +#if HAVE_LIBGEN_H +# include +#endif #include #include +#ifndef PATH_MAX +# ifdef MAX_PATH +# define PATH_MAX MAX_PATH +# else +# define PATH_MAX 256 +# endif +#endif + struct memcp_file { enum class type { basename, @@ -130,7 +140,7 @@ static bool path2key(const client_options &opt, memcp_file &file, char **path) { } else if (file.key == memcp_file::type::relative) { *path = file.path; } else { - *path = basename((file.path)); + *path = basename(file.path); } return true; } diff --git a/src/libhashkit/common.h b/src/libhashkit/common.h index 140d8815..6d483584 100644 --- a/src/libhashkit/common.h +++ b/src/libhashkit/common.h @@ -22,6 +22,7 @@ #include #include #include +#include #ifndef __WORDSIZE # ifdef __MINGW32__ diff --git a/src/libmemcached/CMakeLists.txt b/src/libmemcached/CMakeLists.txt index 35e5b0f0..15947215 100644 --- a/src/libmemcached/CMakeLists.txt +++ b/src/libmemcached/CMakeLists.txt @@ -12,8 +12,12 @@ bison_target(CSL_PARSER csl/parser.yy ${CMAKE_CURRENT_BINARY_DIR}/csl/parser.cc COMPILE_FLAGS ${BISON_WARNINGS} ) set_source_files_properties(${BISON_CSL_PARSER_OUTPUTS} PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON) +if(NOT HAVE_UNISTD_H) + set(FLEX_FLAGS --nounistd) +endif() flex_target(CSL_SCANNER csl/scanner.l ${CMAKE_CURRENT_BINARY_DIR}/csl/scanner.cc DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/csl/scanner.h + COMPILE_FLAGS "${FLEX_FLAGS}" ) set_source_files_properties(${FLEX_CSL_SCANNER_OUTPUTS} PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON) add_flex_bison_dependency(CSL_SCANNER CSL_PARSER) @@ -91,6 +95,9 @@ set_target_properties(libmemcached PROPERTIES SOVERSION ${LIBMEMCACHED_SO_VERSION}) target_compile_definitions(libmemcached PRIVATE -DBUILDING_LIBMEMCACHED) target_link_libraries(libmemcached PUBLIC libhashkit Threads::Threads ${CMAKE_DL_LIBS}) +if(MSVC) + target_link_libraries(libmemcached PUBLIC wsock32 ws2_32) +endif() if(HAVE_BACKTRACE) target_link_libraries(libmemcached PUBLIC ${BACKTRACE}) endif() @@ -131,6 +138,9 @@ add_library(memcachedinternal ALIAS libmemcachedinternal) set_target_properties(libmemcachedinternal PROPERTIES CXX_STANDARD ${CXX_STANDARD} LIBRARY_OUTPUT_NAME memcachedinternal) target_compile_definitions(libmemcachedinternal PRIVATE -DBUILDING_LIBMEMCACHEDINTERNAL) target_link_libraries(libmemcachedinternal PUBLIC libhashkit Threads::Threads ${CMAKE_DL_LIBS}) +if(MSVC) + target_link_libraries(libmemcached PUBLIC wsock32 ws2_32) +endif() if(HAVE_LIBSASL) target_link_libraries(libmemcachedinternal PUBLIC ${LIBSASL}) endif() diff --git a/src/libmemcached/byteorder.h b/src/libmemcached/byteorder.h index ecc89467..36d3f101 100644 --- a/src/libmemcached/byteorder.h +++ b/src/libmemcached/byteorder.h @@ -16,10 +16,12 @@ #pragma once #ifdef __cplusplus +# include extern "C" { +#else +# include #endif -#include uint64_t memcached_ntohll(uint64_t); diff --git a/src/libmemcached/callback.cc b/src/libmemcached/callback.cc index fbbf68d7..e3d80bd2 100644 --- a/src/libmemcached/callback.cc +++ b/src/libmemcached/callback.cc @@ -16,10 +16,6 @@ #include "libmemcached/common.h" #include -#ifndef __INTEL_COMPILER -# pragma GCC diagnostic ignored "-Wstrict-aliasing" -#endif - /* These functions provide data and function callback support */ diff --git a/src/libmemcached/common.h b/src/libmemcached/common.h index f9b20c4e..eea5319b 100644 --- a/src/libmemcached/common.h +++ b/src/libmemcached/common.h @@ -26,6 +26,7 @@ # include # include # include +# include #else # include # include @@ -34,6 +35,7 @@ # include # include # include +# include #endif #ifdef HAVE_SYS_UN_H @@ -72,11 +74,7 @@ #include "libmemcached/is.h" typedef struct memcached_st Memcached; -#ifdef HAVE_POLL_H -# include -#else -# include "libmemcached/poll.h" -#endif +#include "libmemcached/poll.h" #ifdef __cplusplus memcached_instance_st *memcached_instance_fetch(memcached_st *ptr, uint32_t server_key); diff --git a/src/libmemcached/connect.cc b/src/libmemcached/connect.cc index 248cd7cd..ff7772ca 100644 --- a/src/libmemcached/connect.cc +++ b/src/libmemcached/connect.cc @@ -59,7 +59,7 @@ static memcached_return_t connect_poll(memcached_instance_st *server, const int while (--loop_max) // Should only loop on cases of ERESTART or EINTR { int number_of; - if ((number_of = poll(fds, 1, server->root->connect_timeout)) == -1) { + if ((number_of = poll(fds, 1, server->root->connect_timeout)) == SOCKET_ERROR) { int local_errno = get_socket_errno(); // We cache in case closesocket() modifies errno switch (local_errno) { #ifdef __linux__ @@ -90,7 +90,7 @@ static memcached_return_t connect_poll(memcached_instance_st *server, const int } if (number_of == 0) { - if (connection_error == EINPROGRESS) { + if (connection_error != EALREADY) { int err; socklen_t len = sizeof(err); if (getsockopt(server->fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len) == -1) { @@ -138,7 +138,7 @@ static memcached_return_t connect_poll(memcached_instance_st *server, const int } assert(fds[0].revents & POLLOUT); - if (fds[0].revents & POLLOUT and connection_error == EINPROGRESS) { + if (fds[0].revents & POLLOUT and connection_error != EALREADY) { int err; socklen_t len = sizeof(err); if (getsockopt(server->fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len) == -1) { @@ -521,6 +521,7 @@ static memcached_return_t network_connect(memcached_instance_st *server) { #if EWOULDBLOCK != EAGAIN case EWOULDBLOCK: #endif + case EAGAIN: case EINPROGRESS: // nonblocking mode - first return case EALREADY: // nonblocking mode - subsequent returns { diff --git a/src/libmemcached/do.cc b/src/libmemcached/do.cc index eb592a6d..8385a300 100644 --- a/src/libmemcached/do.cc +++ b/src/libmemcached/do.cc @@ -17,7 +17,7 @@ static memcached_return_t _vdo_udp(memcached_instance_st *instance, libmemcached_io_vector_st vector[], const size_t count) { -#ifndef __MINGW32__ +#if HAVE_SENDMSG && HAVE_STRUCT_MSGHDR if (vector[0].buffer or vector[0].length) { return memcached_set_error( *instance->root, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT, diff --git a/src/libmemcached/error.cc b/src/libmemcached/error.cc index b8f0cc33..cd14e06b 100644 --- a/src/libmemcached/error.cc +++ b/src/libmemcached/error.cc @@ -191,10 +191,6 @@ memcached_return_t memcached_set_error(memcached_instance_st &self, memcached_re return memcached_set_error(self, rc, at, tmp); } -#ifndef __INTEL_COMPILER -# pragma GCC diagnostic ignored "-Wformat-nonliteral" -#endif - memcached_return_t memcached_set_error(Memcached &memc, memcached_return_t rc, const char *at, memcached_string_t &str) { assert_msg(rc != MEMCACHED_ERRNO, diff --git a/src/libmemcached/poll.cc b/src/libmemcached/poll.cc index 81062cae..5914aa47 100644 --- a/src/libmemcached/poll.cc +++ b/src/libmemcached/poll.cc @@ -22,7 +22,7 @@ # endif # include # if HAVE_STRINGS_H -# include +# include # endif int poll(struct pollfd fds[], nfds_t nfds, int tmo) { @@ -49,7 +49,7 @@ int poll(struct pollfd fds[], nfds_t nfds, int tmo) { } } - struct timeval timeout = {.tv_sec = tmo / 1000, .tv_usec = (tmo % 1000) * 1000}; + struct timeval timeout = {tmo / 1000, (tmo % 1000) * 1000}; struct timeval *tp = &timeout; if (tmo == -1) { tp = NULL; diff --git a/src/libmemcached/poll.h b/src/libmemcached/poll.h index fab30d1f..8c0ad1ff 100644 --- a/src/libmemcached/poll.h +++ b/src/libmemcached/poll.h @@ -15,9 +15,15 @@ #pragma once -#if defined(_WIN32) - -# include +#if defined HAVE_SYS_POLL_H +# include +#elif defined HAVE_POLL_H +# include +#elif defined _WIN32 +# include "windows.hpp" +# define poll WSAPoll +typedef int nfds_t; +#elif !defined _MSC_VER # ifdef __cplusplus extern "C" { diff --git a/src/libmemcached/socket.hpp b/src/libmemcached/socket.hpp index d1a33963..ed7810f7 100644 --- a/src/libmemcached/socket.hpp +++ b/src/libmemcached/socket.hpp @@ -24,7 +24,8 @@ */ #if defined(WIN32) || defined(__MINGW32__) # include "win32/wrappers.h" -# define get_socket_errno() WSAGetLastError() +# include "windows.hpp" +# define get_socket_errno() translate_windows_error() #else # include # define INVALID_SOCKET -1 diff --git a/src/libmemcached/windows.hpp b/src/libmemcached/windows.hpp index afa0be15..b867a6d4 100644 --- a/src/libmemcached/windows.hpp +++ b/src/libmemcached/windows.hpp @@ -25,18 +25,6 @@ # define WIN32_LEAN_AND_MEAN #endif -#ifndef _WIN32_WINNT -# define _WIN32_WINNT 0x0501 -#endif - -#ifdef __MINGW32__ -# if (_WIN32_WINNT >= 0x0501) -# else -# undef _WIN32_WINNT -# define _WIN32_WINNT 0x0501 -# endif /* _WIN32_WINNT >= 0x0501 */ -#endif /* __MINGW32__ */ - #if defined(HAVE_WINSOCK2_H) && HAVE_WINSOCK2_H # include #endif @@ -58,26 +46,56 @@ static inline int translate_windows_error() { int local_errno = WSAGetLastError(); switch (local_errno) { - case WSAEINVAL: local_errno = EINPROGRESS; break; + case WSAEINVAL: + local_errno = EINPROGRESS; + break; case WSAEALREADY: - case WSAEWOULDBLOCK: local_errno = EAGAIN; break; + case WSAEWOULDBLOCK: + local_errno = EAGAIN; + break; - case WSAECONNREFUSED: local_errno = ECONNREFUSED; break; + case WSAECONNREFUSED: + local_errno = ECONNREFUSED; + break; - case WSAENETUNREACH: local_errno = ENETUNREACH; break; + case WSAENETUNREACH: + local_errno = ENETUNREACH; + break; - case WSAETIMEDOUT: local_errno = ETIMEDOUT; break; + case WSAETIMEDOUT: + local_errno = ETIMEDOUT; + break; - case WSAECONNRESET: local_errno = ECONNRESET; break; + case WSAECONNRESET: + local_errno = ECONNRESET; + break; - case WSAEADDRINUSE: local_errno = EADDRINUSE; break; + case WSAEADDRINUSE: + local_errno = EADDRINUSE; + break; - case WSAEOPNOTSUPP: local_errno = EOPNOTSUPP; break; + case WSAEOPNOTSUPP: + local_errno = EOPNOTSUPP; + break; - case WSAENOPROTOOPT: local_errno = ENOPROTOOPT; break; + case WSAENOPROTOOPT: + local_errno = ENOPROTOOPT; + break; - default: break; + default: + break; } return local_errno; } + +static inline char *basename(const char *filename) { + static char base[MAX_PATH * 2], ext[MAX_PATH], *ptr; + (void) _splitpath_s(filename, NULL, 0, NULL, 0, base, MAX_PATH, ext, MAX_PATH); + strcat_s(base, MAX_PATH * 2 - 1, ext); + return base; +} + +static inline char *realpath(const char *path, char real[MAX_PATH]) { + return _fullpath(real, path, MAX_PATH); +} \ No newline at end of file diff --git a/src/libmemcachedprotocol/CMakeLists.txt b/src/libmemcachedprotocol/CMakeLists.txt index dd66d6a8..61fbed15 100644 --- a/src/libmemcachedprotocol/CMakeLists.txt +++ b/src/libmemcachedprotocol/CMakeLists.txt @@ -7,6 +7,7 @@ add_library(libmemcachedprotocol SHARED common.h handler.c pedantic.c + ../libmemcached/byteorder.cc ) add_library(memcachedprotocol ALIAS libmemcachedprotocol) set_target_properties(libmemcachedprotocol PROPERTIES @@ -21,6 +22,9 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL AppleClang) ) endif() target_link_libraries(libmemcachedprotocol PUBLIC Threads::Threads) +if(MSVC) + target_link_libraries(libmemcachedprotocol PUBLIC wsock32 ws2_32) +endif() target_include_directories(libmemcachedprotocol PRIVATE ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}/src diff --git a/src/libmemcachedprotocol/cache.c b/src/libmemcachedprotocol/cache.c index 1de8b0a6..39e2fc7d 100644 --- a/src/libmemcachedprotocol/cache.c +++ b/src/libmemcachedprotocol/cache.c @@ -23,6 +23,27 @@ #ifndef HAVE_UMEM_H +# ifdef _MSC_VER +typedef SECURITY_ATTRIBUTES pthread_mutexattr_t; + +static inline int pthread_mutex_init(pthread_mutex_t *m, pthread_mutexattr_t *a) { + *m = CreateMutexA(a, FALSE, NULL); + return !*m; +} +static inline int pthread_mutex_destroy(pthread_mutex_t *m) { + return !CloseHandle(*m); +} +static inline int pthread_mutex_lock(pthread_mutex_t *m) { + if (WaitForSingleObject(*m, INFINITE)) { + return 0; + } + return GetLastError(); +} +static inline int pthread_mutex_unlock(pthread_mutex_t *m) { + return !ReleaseMutex(*m); +} +# endif + # ifndef NDEBUG # include diff --git a/src/libmemcachedprotocol/cache.h b/src/libmemcachedprotocol/cache.h index 616909f7..7b5efa98 100644 --- a/src/libmemcachedprotocol/cache.h +++ b/src/libmemcachedprotocol/cache.h @@ -15,7 +15,11 @@ #pragma once -#include +#ifdef HAVE_PTHREAD_H +# include +#else +typedef void *pthread_mutex_t; +#endif #ifdef HAVE_UMEM_H # include diff --git a/src/libmemcachedutil/CMakeLists.txt b/src/libmemcachedutil/CMakeLists.txt index 594099bf..b49d0d03 100644 --- a/src/libmemcachedutil/CMakeLists.txt +++ b/src/libmemcachedutil/CMakeLists.txt @@ -5,9 +5,11 @@ add_library(libmemcachedutil SHARED flush.cc pid.cc ping.cc - pool.cc version.cc ) +if(CMAKE_USE_PTHREADS_INIT) + target_sources(libmemcachedutil PRIVATE pool.cc) +endif() add_library(memcachedutil ALIAS libmemcachedutil) set_target_properties(libmemcachedutil PROPERTIES CXX_STANDARD ${CXX_STANDARD} @@ -20,6 +22,11 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL AppleClang) LINK_FLAGS "-Wl,-undefined,dynamic_lookup" ) endif() +if(MSVC) + set_target_properties(libmemcachedutil PROPERTIES + LINK_FLAGS "/FORCE:UNRESOLVED" + ) +endif() target_link_libraries(libmemcachedutil PUBLIC Threads::Threads) if(HAVE_LIBSASL) target_link_libraries(libmemcachedutil PUBLIC ${LIBSASL}) diff --git a/src/libmemcachedutil/common.h b/src/libmemcachedutil/common.h index e7095cd6..680f85c0 100644 --- a/src/libmemcachedutil/common.h +++ b/src/libmemcachedutil/common.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "libmemcachedutil-1.0/util.h" #include "libmemcached/assert.hpp" diff --git a/src/win32/getopt.c b/src/win32/getopt.c new file mode 100644 index 00000000..dd8cd704 --- /dev/null +++ b/src/win32/getopt.c @@ -0,0 +1,652 @@ +#ifndef __GETOPT_H__ +/** + * DISCLAIMER + * This file is part of the mingw-w64 runtime package. + * + * The mingw-w64 runtime package and its code is distributed in the hope that it + * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR + * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to + * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + /* + * Copyright (c) 2002 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define __GETOPT_H__ + +/* All the headers include this file. */ +#include +#include +#include +#include +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ + +#ifdef REPLACE_GETOPT +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +#undef optreset /* see getopt.h */ +#define optreset __mingw_optreset +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ +#endif + +//extern int optind; /* index of first non-option in argv */ +//extern int optopt; /* single option character, as parsed */ +//extern int opterr; /* flag to enable built-in diagnostics... */ +// /* (user may set to zero, to suppress) */ +// +//extern char *optarg; /* pointer to argument of current option */ + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#ifndef __CYGWIN__ +#define __progname __argv[0] +#else +extern char __declspec(dllimport) *__progname; +#endif + +#ifdef __CYGWIN__ +static char EMSG[] = ""; +#else +#define EMSG "" +#endif + +static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); +static int parse_long_options(char * const *, const char *, + const struct option *, int *, int); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptchar[] = "unknown option -- %c"; +static const char illoptstring[] = "unknown option -- %s"; + +static void +_vwarnx(const char *fmt,va_list ap) +{ + (void)fprintf(stderr,"%s: ",__progname); + if (fmt != NULL) + (void)vfprintf(stderr,fmt,ap); + (void)fprintf(stderr,"\n"); +} + +static void +warnx(const char *fmt,...) +{ + va_list ap; + va_start(ap,fmt); + _vwarnx(fmt,ap); + va_end(ap); +} + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +#ifdef REPLACE_GETOPT +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ +int +getopt(int nargc, char * const *nargv, const char *options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} +#endif /* REPLACE_GETOPT */ + +//extern int getopt(int nargc, char * const *nargv, const char *options); + +#ifdef _BSD_SOURCE +/* + * BSD adds the non-standard `optreset' feature, for reinitialisation + * of `getopt' parsing. We support this feature, for applications which + * proclaim their BSD heritage, before including this header; however, + * to maintain portability, developers are advised to avoid it. + */ +# define optreset __mingw_optreset +extern int optreset; +#endif +#ifdef __cplusplus +} +#endif +/* + * POSIX requires the `getopt' API to be specified in `unistd.h'; + * thus, `unistd.h' includes this header. However, we do not want + * to expose the `getopt_long' or `getopt_long_only' APIs, when + * included in this manner. Thus, close the standard __GETOPT_H__ + * declarations block, and open an additional __GETOPT_LONG_H__ + * specific block, only when *not* __UNISTD_H_SOURCED__, in which + * to declare the extended API. + */ +#endif /* !defined(__GETOPT_H__) */ + +#if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) +#define __GETOPT_LONG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct option /* specification for a long form option... */ +{ + const char *name; /* option name, without leading hyphens */ + int has_arg; /* does it take an argument? */ + int *flag; /* where to save its status, or NULL */ + int val; /* its associated status value */ +}; + +enum /* permitted values for its `has_arg' field... */ +{ + no_argument = 0, /* option never takes an argument */ + required_argument, /* option always requires an argument */ + optional_argument /* option may take an argument */ +}; + +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ +static int +parse_long_options(char * const *nargv, const char *options, + const struct option *long_options, int *idx, int short_too) +{ + char *current_argv, *has_equal; + size_t current_argv_len; + int i, ambiguous, match; + +#define IDENTICAL_INTERPRETATION(_x, _y) \ + (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \ + long_options[(_x)].flag == long_options[(_y)].flag && \ + long_options[(_x)].val == long_options[(_y)].val) + + current_argv = place; + match = -1; + ambiguous = 0; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + ambiguous = 0; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* partial match */ + match = i; + else if (!IDENTICAL_INTERPRETATION(i, match)) + ambiguous = 1; + } + if (ambiguous) { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + warnx(noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + warnx(recargstring, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + warnx(illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); +#undef IDENTICAL_INTERPRETATION +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int +getopt_internal(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int flags) +{ + char *oli; /* option letter list index */ + int optchar, short_too; + static int posixly_correct = -1; + + if (options == NULL) + return (-1); + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + * + * CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or + * optreset != 0 for GNU compatibility. + */ + if (posixly_correct == -1 || optreset != 0) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); + if (*options == '-') + flags |= FLAG_ALLARGS; + else if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + if (*options == '+' || *options == '-') + options++; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || + (place[1] == '\0' && strchr(options, '-') == NULL)) { + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; + if (*place == '-') + place++; /* --foo long option */ + else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + (optchar == (int)'-' && *place != '\0') || + (oli = (char*)strchr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++optind; + if (PRINT_ERROR) + warnx(illoptchar, optchar); + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[optind]; + optchar = parse_long_options(nargv, options, long_options, + idx, 0); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); +} + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} + +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int +getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); +} + +//extern int getopt_long(int nargc, char * const *nargv, const char *options, +// const struct option *long_options, int *idx); +//extern int getopt_long_only(int nargc, char * const *nargv, const char *options, +// const struct option *long_options, int *idx); +/* + * Previous MinGW implementation had... + */ +#ifndef HAVE_DECL_GETOPT +/* + * ...for the long form API only; keep this for compatibility. + */ +# define HAVE_DECL_GETOPT 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */ diff --git a/src/win32/getopt.h b/src/win32/getopt.h new file mode 100644 index 00000000..8f0835b7 --- /dev/null +++ b/src/win32/getopt.h @@ -0,0 +1,30 @@ +#pragma once + +extern "C" { + +extern int opterr; /* if error message should be printed */ +extern int optind; /* index into parent argv vector */ +extern char *optarg; /* argument associated with option */ + +struct option /* specification for a long form option... */ +{ + const char *name; /* option name, without leading hyphens */ + int has_arg; /* does it take an argument? */ + int *flag; /* where to save its status, or NULL */ + int val; /* its associated status value */ +}; + +enum /* permitted values for its `has_arg' field... */ +{ + no_argument = 0, /* option never takes an argument */ + required_argument, /* option always requires an argument */ + optional_argument /* option may take an argument */ +}; + +int getopt(int nargc, char * const *nargv, const char *options); +int getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx); +int getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx); + +} \ No newline at end of file diff --git a/src/win32/wrappers.h b/src/win32/wrappers.h index 5bf325e9..a009fbdc 100644 --- a/src/win32/wrappers.h +++ b/src/win32/wrappers.h @@ -59,6 +59,12 @@ #ifndef SHUT_RDWR # define SHUT_RDWR SD_BOTH #endif +#ifndef SHUT_WR +# define SHUT_WR SD_SEND +#endif +#ifndef SHUT_RD +# define SHUT_RD SD_RECEIVE +#endif /* EAI_SYSTEM isn't defined anywhere... just set it to... 11? */ #ifndef EAI_SYSTEM @@ -75,3 +81,24 @@ #define waitpid(a,b,c) (-1) #define fnmatch(a,b,c) (-1) #define sleep(a) Sleep(a*1000) + +#ifdef __cplusplus +# include +static inline int gettimeofday(struct timeval* tp, struct timezone* tzp) { + using clock = std::chrono::system_clock; + auto as_sec = [] (auto d) { + return std::chrono::duration_cast(d); + }; + auto as_usec = [] (auto d) { + return std::chrono::duration_cast(d); + }; + + auto now = clock::now().time_since_epoch(); + auto sec = as_sec(now); + auto usec = as_usec(now - sec); + + tp->tv_sec = sec.count(); + tp->tv_usec = usec.count(); + return 0; +} +#endif \ No newline at end of file