]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
Support building on MSVC (#632)
authorRafael Kitover <rkitover@gmail.com>
Thu, 27 Aug 2020 19:23:26 +0000 (19:23 +0000)
committerGitHub <noreply@github.com>
Thu, 27 Aug 2020 19:23:26 +0000 (21:23 +0200)
With these changes, the project builds with Visual Studio 2019, unit
tests pass and it works correctly with mingw gcc.

NOTE: The very latest version of Visual Studio 2019 is required, because
there was just a necessary fix for template arguments.

Tested building and running unit tests on Windows+MSVC, Windows+MinGW,
Linux and macOS.

- Enable `ZSTD_FROM_INTERNET` by default for MSVC when not using vcpkg
  or conan.

- Add include tests for some standard UNIX headers not available on
  MSVC.

- Add necessary MSVC compiler flags.

- In `Args::from_gcc_atfile()` iterate over the string via `c_str()`
  instead of `cbegin()`, the MSVC string character iterator does not
  include the ending null byte.

- Misc. minor cmake fix-ups.

- Add some headers that are not implicitly included from other headers
  like `<algorithm>`, `<ios>`, `<cstdint>` and `<cstdarg>` in some
  places, gcc does this but MSVC does not.

- Add `std::filesystem` version of `Util::traverse()` when dirent.h is
  not available, which is preferred for performance reasons.

- Add implementations of the following functions that are not available
  in MSVC in Win32Util.cpp: `gettimeofday()`, `localtime_r()`,
  `asprintf()`.

- Add Windows implementation of `getopt_long()` from
  https://www.codeproject.com/Articles/157001/Full-getopt-Port-for-Unicode-and-Multibyte-Microso
  to third_party/win32.

- Add some compatibility typedefs, constants and macros to the `_WIN32`
  section of system.hpp, as well as the prototypes for the functions
  added to Win32Util.cpp.

- Fix up unit tests expecting '/' separated paths to expect paths
  delimited by `DIR_DELIM_CH`.

- Invoke test/run with bash from cmake, necessary on msys2+mingw64, many
  fail, there is more work to do here.

- Set the warning level to `/W4` and silence all the uninteresting
  warning types. Compiles with no warnings now.

- Switch to using standard C++ attributes `[[nodiscard]]` and
  `[[maybe_unused]]` and define macros for gcc for their equivalents.

- `#define DOCTEST_CONFIG_USE_STD_HEADERS` for MSVC only, because it
  requires explicitly including `<ostream>`.

- Add vim files to .gitignore.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
28 files changed:
.gitignore
CMakeLists.txt
LGPL-3.0.txt [new file with mode: 0644]
LICENSE.adoc
cmake/GenerateConfigurationFile.cmake
cmake/StandardSettings.cmake
cmake/StandardWarnings.cmake
cmake/config.h.in
misc/format-files
src/Args.cpp
src/CMakeLists.txt
src/Lockfile.cpp
src/ProgressBar.cpp
src/Result.cpp
src/Util.cpp
src/Util.hpp
src/Win32Util.cpp
src/ZstdCompressor.cpp
src/ccache.cpp
src/system.hpp
src/third_party/CMakeLists.txt
src/third_party/win32/getopt.c [new file with mode: 0644]
src/third_party/win32/getopt.h [new file with mode: 0644]
test/CMakeLists.txt
unittest/TestUtil.hpp
unittest/test_Stat.cpp
unittest/test_Util.cpp
unittest/test_argprocessing.cpp

index 4ccbc98cbf124319c31e4f1721386cb37ab167ed..bb7158fcc64a9bf034aa5e09f86b54f26aefb9ce 100644 (file)
@@ -23,3 +23,7 @@ testdir/
 
 # Visual Studio Code
 /.vscode/
+
+# Vim
+.*.sw?
+.*.un~
index edd06ae14299896b252df5720037c75781083a64..8b1ee1b5eaada2ab7643573b1617bbac9b401116 100644 (file)
@@ -38,7 +38,14 @@ endif()
 #
 # Third party
 #
-option(ZSTD_FROM_INTERNET "Download and use libzstd from the Internet" OFF)
+set(ZSTD_FROM_INTERNET_DEFAULT OFF)
+
+# Default to downloading deps for Visual Studio, unless using a package manager.
+if(MSVC AND NOT CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg|conan")
+  set(ZSTD_FROM_INTERNET_DEFAULT ON)
+endif()
+
+option(ZSTD_FROM_INTERNET "Download and use libzstd from the Internet" ${ZSTD_FROM_INTERNET_DEFAULT})
 find_package(zstd 1.1.2 REQUIRED)
 
 #
diff --git a/LGPL-3.0.txt b/LGPL-3.0.txt
new file mode 100644 (file)
index 0000000..0a04128
--- /dev/null
@@ -0,0 +1,165 @@
+                   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
index 68b01afb14aebf17374375cbe51e40ae4e9d9d2d..5a6e3af6eb16cfd7aeaad6be7e56fc4ed013a68e 100644 (file)
@@ -158,6 +158,17 @@ https://www.postgresql.org[PostgreSQL] and has the following license text:
 -------------------------------------------------------------------------------
 
 
+src/third_party/win32/getopt.[hc]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This implementation of `getopt_long()` for Win32 was taken from
+https://www.codeproject.com/Articles/157001/Full-getopt-Port-for-Unicode-and-Multibyte-Microso
+and is licensed under the LGPL.
+
+The full license text can be found in LGPL-3.0.txt and at
+https://www.gnu.org/licenses/lgpl-3.0.html.
+
+
 src/third_party/nonstd/optional.hpp
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
index d169648760088a99936f99e9ef5493cf8780fa52..7e9020ee45d07851142c3de607ccfa4ecb9a40e6 100644 (file)
@@ -7,8 +7,15 @@ set(include_files
     sys/mman.h
     sys/time.h
     sys/wait.h
+    sys/file.h
     syslog.h
-    termios.h)
+    termios.h
+    dirent.h
+    strings.h
+    unistd.h
+    utime.h
+    sys/utime.h
+    varargs.h)
 foreach(include_file IN ITEMS ${include_files})
   string(TOUPPER ${include_file} include_var)
   string(REGEX REPLACE "[/.]" "_" include_var ${include_var})
@@ -65,5 +72,9 @@ endif()
 # alias
 set(MTR_ENABLED "${ENABLE_TRACING}")
 
+# Check sizeof(int).
+include(CheckTypeSize)
+check_type_size(int SIZEOF_INT)
+
 configure_file(${CMAKE_SOURCE_DIR}/cmake/config.h.in
                ${CMAKE_BINARY_DIR}/config.h @ONLY)
index 9ca91a2e1aab8ede7604b0bbd62ad1a816ec7ed6..a30f8207241d385bd8c5a5ecf1bb7791bb29a73e 100644 (file)
@@ -47,4 +47,6 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "^GNU|Clang$")
       standard_settings
       INTERFACE -fsanitize=${LIST_OF_SANITIZERS})
   endif()
+elseif(MSVC)
+  target_compile_options(standard_settings INTERFACE /std:c++latest /Zc:preprocessor /Zc:__cplusplus /D_CRT_SECURE_NO_WARNINGS)
 endif()
index f5f954c82096166962297f4575418a687c8a1b41..f879da0918542babe79dea3d10dd1a6ecee7dd46 100644 (file)
@@ -137,4 +137,24 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
     add_target_compile_flag_if_supported(
       standard_warnings "-Wno-unused-variable")
   endif()
+elseif(MSVC)
+  # Remove any warning level flags added by cmake.
+  string(REGEX REPLACE "/W[0-4]" "" CMAKE_C_FLAGS   "${CMAKE_C_FLAGS}")
+  string(REGEX REPLACE "/W[0-4]" "" CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS}")
+  string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+
+  target_compile_options(
+    standard_warnings
+    INTERFACE
+    /W4
+    # Ignore bad macro in winbase.h triggered by /Zc:preprocessor
+    /wd5105
+    # Conversion warnings.
+    /wd4244
+    /wd4267
+    # Assignment in conditional.
+    /wd4706
+    # Non-underscore-prefixed POSIX functions.
+    /wd4996
+  )
 endif()
index 34935e05fcce48aff96d74ea83e9d89e60826dd6..30393c84f6569999f73fef9f59d019321ac97e95 100644 (file)
@@ -52,6 +52,8 @@
 #  pragma clang diagnostic pop
 #endif
 
+#define SIZEOF_INT @SIZEOF_INT@
+
 #cmakedefine MTR_ENABLED
 
 /* Define to 1 if you have the `asctime_r' function. */
 /* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
 #cmakedefine HAVE_SYS_WAIT_H
 
+/* Define to 1 if you have the <sys/file.h> header file. */
+#cmakedefine HAVE_SYS_FILE_H
+
 /* Define to 1 if you have the <termios.h> header file. */
 #cmakedefine HAVE_TERMIOS_H
 
+/* Define to 1 if you have the <dirent.h> header file. */
+#cmakedefine HAVE_DIRENT_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#cmakedefine HAVE_STRINGS_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#cmakedefine HAVE_UNISTD_H
+
+/* Define to 1 if you have the <utime.h> header file. */
+#cmakedefine HAVE_UTIME_H
+
+/* Define to 1 if you have the <sys/utime.h> header file. */
+#cmakedefine HAVE_SYS_UTIME_H
+
+/* Define to 1 if you have the <varargs.h> header file. */
+#cmakedefine HAVE_VARARGS_H
+
 /* Define to 1 if you have the `unsetenv' function. */
 #cmakedefine HAVE_UNSETENV
 
index 3a79f7f6d7ec499ac2c429d050d84a8a782a6169..d54978433fa948b85758254448c269c1c1ea674d 100755 (executable)
@@ -24,7 +24,7 @@ for arg in "$@"; do
 done
 
 if [ -n "$all" ]; then
-    exec "$0" $check $(git ls-files '*.[ch]' '*.[ch]pp' ':!:src/third_party')
+    exec sh "$0" $check $(git ls-files '*.[ch]' '*.[ch]pp' ':!:src/third_party')
 fi
 
 clang_format=${CLANG_FORMAT:-clang-format}
index 9eff36be4f450571b9bfd21715fb2ba15926b672..034233fbf47f92ab7e492151ea295b686f29a776 100644 (file)
@@ -53,7 +53,7 @@ Args::from_gcc_atfile(const std::string& filename)
   }
 
   Args args;
-  auto pos = argtext.cbegin();
+  auto pos = argtext.c_str();
   std::string argbuf;
   argbuf.resize(argtext.length() + 1);
   auto argpos = argbuf.begin();
index 27c3f1fe3382f59a668e5ee3313cf3c52c4e94ee..250d2e19d5e2ae2323dc6f79db5f6f4907b24909 100644 (file)
@@ -57,7 +57,7 @@ if(WIN32)
   if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
     target_link_libraries(
       ccache_lib PRIVATE -static gcc stdc++ winpthread -dynamic)
-  else()
+  elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
     target_link_libraries(ccache_lib PRIVATE -static c++ -dynamic)
   endif()
 endif()
index ec6cfa83fff0a95db403bad0ab8c736a3acec7fb..5186a802b488374074653d9d4faf542f55d27d4a 100644 (file)
@@ -27,6 +27,8 @@
 
 #include "third_party/fmt/core.h"
 
+#include <algorithm>
+
 using Logging::log;
 
 namespace {
index 1e01e0aeff22e6831f4292398f6bee9f78b10d19..cb0811b2412dc45941be3bd30e106dba62aef417 100644 (file)
@@ -28,6 +28,8 @@
 #  include <termios.h>
 #endif
 
+#include <algorithm>
+
 namespace {
 
 const size_t k_max_width = 120;
index 3091e97b457da7b0cb07078b81553424314e2537..99e20c5cf5d839218e170b67641d9554cbc4b395 100644 (file)
@@ -31,6 +31,8 @@
 #include "exceptions.hpp"
 #include "stats.hpp"
 
+#include <algorithm>
+
 // Result data format
 // ==================
 //
index 15cb20cff9dcc9a51a6207940d9312dc790a4757..6b78b743e93750d03adeaec948913de31619f618 100644 (file)
 #include <algorithm>
 #include <fstream>
 
+#ifndef HAVE_DIRENT_H
+#  include <filesystem>
+#endif
+
 #ifdef HAVE_PWD_H
 #  include <pwd.h>
 #endif
@@ -477,7 +481,7 @@ fallocate(int fd, long new_size)
   int err = 0;
   try {
     write_fd(fd, buf, bytes_to_write);
-  } catch (Error& e) {
+  } catch ([[maybe_unused]] Error& e) {
     err = errno;
   }
   lseek(fd, saved_pos, SEEK_SET);
@@ -782,7 +786,7 @@ is_nfs_fd(int fd, bool* is_nfs)
 }
 #else
 int
-is_nfs_fd([[gnu::unused]] int fd, [[gnu::unused]] bool* is_nfs)
+is_nfs_fd([[maybe_unused]] int fd, [[maybe_unused]] bool* is_nfs)
 {
   return -1;
 }
@@ -959,7 +963,7 @@ int
 parse_int(const std::string& value)
 {
   size_t end;
-  long result;
+  long result = 0;
   bool failed = false;
   try {
     result = std::stoi(value, &end, 10);
@@ -1017,7 +1021,7 @@ uint32_t
 parse_uint32(const std::string& value)
 {
   size_t end;
-  long long result;
+  long long result = 0;
   bool failed = false;
   try {
     result = std::stoll(value, &end, 10);
@@ -1250,7 +1254,7 @@ setenv(const std::string& name, const std::string& value)
 #else
   char* string;
   asprintf(&string, "%s=%s", name.c_str(), value.c_str());
-  putenv(string);               // Leak to environment.
+  putenv(string); // Leak to environment.
 #endif
 }
 
@@ -1306,6 +1310,8 @@ to_lowercase(string_view string)
   return result;
 }
 
+#ifdef HAVE_DIRENT_H
+
 void
 traverse(const std::string& path, const TraverseVisitor& visitor)
 {
@@ -1320,11 +1326,11 @@ traverse(const std::string& path, const TraverseVisitor& visitor)
 
       std::string entry_path = path + "/" + entry->d_name;
       bool is_dir;
-#ifdef _DIRENT_HAVE_D_TYPE
+#  ifdef _DIRENT_HAVE_D_TYPE
       if (entry->d_type != DT_UNKNOWN) {
         is_dir = entry->d_type == DT_DIR;
       } else
-#endif
+#  endif
       {
         auto stat = Stat::lstat(entry_path);
         if (!stat) {
@@ -1352,6 +1358,31 @@ traverse(const std::string& path, const TraverseVisitor& visitor)
   }
 }
 
+#else // If not available, use the C++17 std::filesystem implementation.
+
+void
+traverse(const std::string& path, const TraverseVisitor& visitor)
+{
+  if (std::filesystem::is_directory(path)) {
+    for (auto&& p : std::filesystem::directory_iterator(path)) {
+      std::string entry = p.path().string();
+
+      if (p.is_directory()) {
+        traverse(entry, visitor);
+      } else {
+        visitor(entry, false);
+      }
+    }
+    visitor(path, true);
+  } else if (std::filesystem::exists(path)) {
+    visitor(path, false);
+  } else {
+    throw Error("failed to open directory {}: {}", path, strerror(errno));
+  }
+}
+
+#endif
+
 bool
 unlink_safe(const std::string& path, UnlinkLog unlink_log)
 {
@@ -1465,6 +1496,10 @@ write_file(const std::string& path,
            const std::string& data,
            std::ios_base::openmode open_mode)
 {
+  if (path.empty()) {
+    throw Error("No such file or directory");
+  }
+
   open_mode |= std::ios::out;
   std::ofstream file(path, open_mode);
   if (!file) {
index 712f06ad912c76d7eecce743286d037797b40818..f833656830e5f4ab8da9fa110214b9cdb15dec28 100644 (file)
@@ -26,6 +26,7 @@
 #include "third_party/nonstd/string_view.hpp"
 
 #include <functional>
+#include <ios>
 #include <memory>
 #include <string>
 #include <utility>
@@ -125,8 +126,7 @@ ends_with(nonstd::string_view string, nonstd::string_view suffix)
 
 // Expand all instances of $VAR or ${VAR}, where VAR is an environment variable,
 // in `str`. Throws `Error` if one of the environment variables.
-[[gnu::warn_unused_result]] std::string
-expand_environment_variables(const std::string& str);
+[[nodiscard]] std::string expand_environment_variables(const std::string& str);
 
 // Extends file size to at least new_size by calling posix_fallocate() if
 // supported, otherwise by writing zeros last to the file.
@@ -406,16 +406,13 @@ starts_with(nonstd::string_view string, nonstd::string_view prefix)
 }
 
 // Returns a copy of string with the specified ANSI CSI sequences removed.
-[[gnu::warn_unused_result]] std::string
-strip_ansi_csi_seqs(nonstd::string_view string);
+[[nodiscard]] std::string strip_ansi_csi_seqs(nonstd::string_view string);
 
 // Strip whitespace from left and right side of a string.
-[[gnu::warn_unused_result]] std::string
-strip_whitespace(const std::string& string);
+[[nodiscard]] std::string strip_whitespace(const std::string& string);
 
 // Convert a string to lowercase.
-[[gnu::warn_unused_result]] std::string
-to_lowercase(nonstd::string_view string);
+[[nodiscard]] std::string to_lowercase(nonstd::string_view string);
 
 // Traverse `path` recursively (postorder, i.e. files are visited before their
 // parent directory).
index ce22684a67fb50412da618f60504339253b5b79c..17c3b6330606b6fbc34fb0b3fbc2499b2f6ea382 100644 (file)
@@ -20,6 +20,9 @@
 
 #include "Util.hpp"
 
+#include <chrono>
+#include <thread>
+
 namespace Win32Util {
 
 std::string
@@ -91,3 +94,75 @@ argv_to_string(const char* const* argv, const std::string& prefix)
 }
 
 } // namespace Win32Util
+
+// From: https://stackoverflow.com/a/58162122/262458
+#ifdef _MSC_VER
+int
+gettimeofday(struct timeval* tp, [[maybe_unused]] struct timezone* tzp)
+{
+  namespace sc = std::chrono;
+  sc::system_clock::duration d = sc::system_clock::now().time_since_epoch();
+  sc::seconds s = sc::duration_cast<sc::seconds>(d);
+  tp->tv_sec = static_cast<long>(s.count());
+  tp->tv_usec =
+    static_cast<long>(sc::duration_cast<sc::microseconds>(d - s).count());
+
+  return 0;
+}
+#endif
+
+void
+usleep(int64_t usec)
+{
+  std::this_thread::sleep_for(std::chrono::microseconds(usec));
+}
+
+struct tm*
+localtime_r(time_t* _clock, struct tm* _result)
+{
+  struct tm* p = localtime(_clock);
+
+  if (p)
+    *(_result) = *p;
+
+  return p;
+}
+
+// From: https://stackoverflow.com/a/40160038/262458
+#ifdef _MSC_VER
+int
+vasprintf(char** strp, const char* fmt, va_list ap)
+{
+  // _vscprintf tells you how big the buffer needs to be
+  int len = _vscprintf(fmt, ap);
+  if (len == -1) {
+    return -1;
+  }
+  size_t size = (size_t)len + 1;
+  char* str = static_cast<char*>(malloc(size));
+  if (!str) {
+    return -1;
+  }
+  // vsprintf_s is the "secure" version of vsprintf
+  int r = vsprintf_s(str, len + 1, fmt, ap);
+  if (r == -1) {
+    free(str);
+    return -1;
+  }
+  *strp = str;
+  return r;
+}
+#endif
+
+// Also from: https://stackoverflow.com/a/40160038/262458
+#ifdef _MSC_VER
+int
+asprintf(char** strp, const char* fmt, ...)
+{
+  va_list ap;
+  va_start(ap, fmt);
+  int r = vasprintf(strp, fmt, ap);
+  va_end(ap);
+  return r;
+}
+#endif
index 4e054942aca1362ab950beea220227181be6cfc4..c50df840d7fa24eea152b7a05c21401f31ebdbe5 100644 (file)
@@ -21,6 +21,8 @@
 #include "Logging.hpp"
 #include "exceptions.hpp"
 
+#include <algorithm>
+
 using Logging::log;
 
 ZstdCompressor::ZstdCompressor(FILE* stream, int8_t compression_level)
index c9e8ec82e0be196631dc6d82172c44583a0dac0a..5526bc7b82e2c084ea297d62f1e28a7bede81b1a 100644 (file)
@@ -56,6 +56,8 @@
 
 #ifdef HAVE_GETOPT_LONG
 #  include <getopt.h>
+#elif defined(_WIN32)
+#  include "third_party/win32/getopt.h"
 #else
 #  include "third_party/getopt_long.h"
 #endif
index c96ea26e33d72c3614b4f6c6d7decf9e22f6c957..79d07effa9b0d76b96c82f9ed16b9cf7fbd4d915 100644 (file)
 
 #include "config.h"
 
-#include <sys/file.h>
+#ifdef HAVE_SYS_FILE_H
+#  include <sys/file.h>
+#endif
+
 #ifdef HAVE_SYS_MMAN_H
 #  include <sys/mman.h>
 #endif
 #include <csignal>
 #include <cstdarg>
 #include <cstddef>
+#include <cstdint>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
 #include <ctime>
-#include <dirent.h>
+
+#ifdef HAVE_DIRENT_H
+#  include <dirent.h>
+#endif
+
 #include <fcntl.h>
-#include <strings.h>
-#include <unistd.h>
-#include <utime.h>
+
+#ifdef HAVE_STRINGS_H
+#  include <strings.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#  include <unistd.h>
+#endif
+
+#ifdef HAVE_UTIME_H
+#  include <utime.h>
+#elif defined(HAVE_SYS_UTIME_H)
+#  include <sys/utime.h>
+#endif
+
+#ifdef HAVE_VARARGS_H
+#  include <varargs.h>
+#endif
 
 // AIX/PASE does not properly define usleep within its headers. However, the
 // function is available in libc.a. This extern define ensures that it is
@@ -60,8 +83,6 @@
 extern int usleep(useconds_t);
 #endif
 
-extern char** environ;
-
 #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
 
 // Buffer size for I/O operations. Should be a multiple of 4 KiB.
@@ -76,17 +97,76 @@ const size_t READ_BUFFER_SIZE = 65536;
 // _WIN32_WINNT is set in the generated header config.h
 #    error _WIN32_WINNT is undefined
 #  endif
+
+#  ifdef _MSC_VER
+typedef int mode_t;
+typedef int pid_t;
+#  endif
+
+#  ifndef __MINGW32__
+typedef int64_t ssize_t;
+#  endif
+
+// Defined in Win32Util.cpp
+void usleep(int64_t usec);
+struct tm* localtime_r(time_t* _clock, struct tm* _result);
+
+#  ifdef _MSC_VER
+int gettimeofday(struct timeval* tp, struct timezone* tzp);
+int asprintf(char** strp, const char* fmt, ...);
+#  endif
+
+// From:
+// http://mesos.apache.org/api/latest/c++/3rdparty_2stout_2include_2stout_2windows_8hpp_source.html
+#  ifdef _MSC_VER
+const mode_t S_IRUSR = mode_t(_S_IREAD);
+const mode_t S_IWUSR = mode_t(_S_IWRITE);
+#  endif
+
+// From https://stackoverflow.com/a/62371749/262458
+#  define _CRT_INTERNAL_NONSTDC_NAMES 1
+#  include <sys/stat.h>
+#  if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
+#    define S_ISREG(m) (((m)&S_IFMT) == S_IFREG)
+#  endif
+#  if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR)
+#    define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR)
+#  endif
+
+#  include <direct.h>
+#  include <io.h>
+#  include <process.h>
+#  define NOMINMAX 1
 #  include <windows.h>
-#  define mkdir(a, b) mkdir(a)
+#  define mkdir(a, b) _mkdir(a)
 #  define link(src, dst) (CreateHardLink(dst, src, nullptr) ? 0 : -1)
 #  define execv(a, b) win32execute(a, b, 0, -1, -1)
+#  define strncasecmp _strnicmp
+#  define strcasecmp _stricmp
+
+#  ifdef _MSC_VER
+#    define PATH_MAX MAX_PATH
+#  endif
+
+#  ifdef _MSC_VER
+#    define DLLIMPORT __declspec(dllimport)
+#  else
+#    define DLLIMPORT
+#  endif
+
+#  define STDIN_FILENO 0
+#  define STDOUT_FILENO 1
+#  define STDERR_FILENO 2
 #  define DIR_DELIM_CH '\\'
 #  define PATH_DELIM ";"
 #else
+#  define DLLIMPORT
 #  define DIR_DELIM_CH '/'
 #  define PATH_DELIM ":"
 #endif
 
+DLLIMPORT extern char** environ;
+
 // Work with silly DOS binary open.
 #ifndef O_BINARY
 #  define O_BINARY 0
@@ -102,3 +182,9 @@ const size_t READ_BUFFER_SIZE = 65536;
 #else
 #  define IS_TRIVIALLY_COPYABLE(T) std::is_trivially_copyable<T>::value
 #endif
+
+// GCC version of a couple of standard C++ attributes
+#ifdef __GNUC__
+#  define nodiscard gnu::warn_unused_result
+#  define maybe_unused gnu::unused
+#endif
index ed52d6e0f1f8b35c6b88e28595d4239635970810..24efe6e006599617d9f09e1709ee421be3770213 100644 (file)
@@ -1,4 +1,11 @@
-add_library(third_party_lib STATIC format.cpp getopt_long.c xxhash.c)
+if(NOT MSVC)
+  add_library(third_party_lib STATIC format.cpp getopt_long.c xxhash.c)
+else()
+  add_library(third_party_lib STATIC format.cpp win32/getopt.c xxhash.c)
+
+  target_compile_definitions(third_party_lib PUBLIC -DSTATIC_GETOPT)
+endif()
+
 if(ENABLE_TRACING)
   target_sources(third_party_lib PRIVATE minitrace.c)
 endif()
@@ -13,17 +20,27 @@ target_link_libraries(third_party_lib INTERFACE blake3)
 
 # These warnings are enabled by default even without e.g. -Wall, but we don't
 # want them in third_party.
-target_compile_options(
-  third_party_lib
-  PRIVATE
-    $<$<COMPILE_LANGUAGE:C>:-Wno-implicit-function-declaration
-    -Wno-int-conversion>)
+if(CMAKE_CXX_COMPILER_ID MATCHES "^GNU|Clang$")
+  target_compile_options(
+    third_party_lib
+    PRIVATE
+      $<$<COMPILE_LANGUAGE:C>:-Wno-implicit-function-declaration
+      -Wno-int-conversion>)
+endif()
+
 if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
   target_compile_options(
     third_party_lib
     PRIVATE $<$<COMPILE_LANGUAGE:C>:-Wno-attributes>)
 endif()
 
+# Silence warning from winbase.h due to /Zc:preprocessor.
+if(MSVC)
+  target_compile_options(
+    third_party_lib
+    PRIVATE /wd5105)
+endif()
+
 # The headers are included from the rest of the project, so turn off warnings as
 # required.
 if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
diff --git a/src/third_party/win32/getopt.c b/src/third_party/win32/getopt.c
new file mode 100644 (file)
index 0000000..6f98bbe
--- /dev/null
@@ -0,0 +1,975 @@
+/* Getopt for Microsoft C
+This code is a modification of the Free Software Foundation, Inc.
+Getopt library for parsing command line argument the purpose was
+to provide a Microsoft Visual C friendly derivative. This code
+provides functionality for both Unicode and Multibyte builds.
+
+Date: 02/03/2011 - Ludvik Jerabek - Initial Release
+Version: 1.0
+Comment: Supports getopt, getopt_long, and getopt_long_only
+and POSIXLY_CORRECT environment flag
+License: LGPL
+
+Revisions:
+
+02/03/2011 - Ludvik Jerabek - Initial Release
+02/20/2011 - Ludvik Jerabek - Fixed compiler warnings at Level 4
+07/05/2011 - Ludvik Jerabek - Added no_argument, required_argument, optional_argument defs
+08/03/2011 - Ludvik Jerabek - Fixed non-argument runtime bug which caused runtime exception
+08/09/2011 - Ludvik Jerabek - Added code to export functions for DLL and LIB
+02/15/2012 - Ludvik Jerabek - Fixed _GETOPT_THROW definition missing in implementation file
+08/01/2012 - Ludvik Jerabek - Created separate functions for char and wchar_t characters so single dll can do both unicode and ansi
+10/15/2012 - Ludvik Jerabek - Modified to match latest GNU features
+06/19/2015 - Ludvik Jerabek - Fixed maximum option limitation caused by option_a (255) and option_w (65535) structure val variable
+
+**DISCLAIMER**
+THIS MATERIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
+EITHER EXPRESS OR IMPLIED, INCLUDING, BUT Not LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE, OR NON-INFRINGEMENT. SOME JURISDICTIONS DO NOT ALLOW THE
+EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT
+APPLY TO YOU. IN NO EVENT WILL I BE LIABLE TO ANY PARTY FOR ANY
+DIRECT, INDIRECT, SPECIAL OR OTHER CONSEQUENTIAL DAMAGES FOR ANY
+USE OF THIS MATERIAL INCLUDING, WITHOUT LIMITATION, ANY LOST
+PROFITS, BUSINESS INTERRUPTION, LOSS OF PROGRAMS OR OTHER DATA ON
+YOUR INFORMATION HANDLING SYSTEM OR OTHERWISE, EVEN If WE ARE
+EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+#ifndef _CRT_SECURE_NO_WARNINGS
+       #define _CRT_SECURE_NO_WARNINGS
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <malloc.h>
+#include "getopt.h"
+
+#ifdef __cplusplus
+       #define _GETOPT_THROW throw()
+#else
+       #define _GETOPT_THROW
+#endif
+
+int optind = 1;
+int opterr = 1;
+int optopt = '?';
+enum ENUM_ORDERING { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER };
+
+//
+//
+//             Ansi structures and functions follow
+// 
+//
+
+static struct _getopt_data_a
+{
+       int optind;
+       int opterr;
+       int optopt;
+       char *optarg;
+       int __initialized;
+       char *__nextchar;
+       enum ENUM_ORDERING __ordering;
+       int __posixly_correct;
+       int __first_nonopt;
+       int __last_nonopt;
+} getopt_data_a;
+char *optarg_a;
+
+static void exchange_a(char **argv, struct _getopt_data_a *d)
+{
+       int bottom = d->__first_nonopt;
+       int middle = d->__last_nonopt;
+       int top = d->optind;
+       char *tem;
+       while (top > middle && middle > bottom)
+       {
+               if (top - middle > middle - bottom)
+               {
+                       int len = middle - bottom;
+                       register int i;
+                       for (i = 0; i < len; i++)
+                       {
+                               tem = argv[bottom + i];
+                               argv[bottom + i] = argv[top - (middle - bottom) + i];
+                               argv[top - (middle - bottom) + i] = tem;
+                       }
+                       top -= len;
+               }
+               else
+               {
+                       int len = top - middle;
+                       register int i;
+                       for (i = 0; i < len; i++)
+                       {
+                               tem = argv[bottom + i];
+                               argv[bottom + i] = argv[middle + i];
+                               argv[middle + i] = tem;
+                       }
+                       bottom += len;
+               }
+       }
+       d->__first_nonopt += (d->optind - d->__last_nonopt);
+       d->__last_nonopt = d->optind;
+}
+static const char *_getopt_initialize_a (const char *optstring, struct _getopt_data_a *d, int posixly_correct)
+{
+       d->__first_nonopt = d->__last_nonopt = d->optind;
+       d->__nextchar = NULL;
+       d->__posixly_correct = posixly_correct | !!getenv("POSIXLY_CORRECT");
+       if (optstring[0] == '-')
+       {
+               d->__ordering = RETURN_IN_ORDER;
+               ++optstring;
+       }
+       else if (optstring[0] == '+')
+       {
+               d->__ordering = REQUIRE_ORDER;
+               ++optstring;
+       }
+       else if (d->__posixly_correct)
+               d->__ordering = REQUIRE_ORDER;
+       else
+               d->__ordering = PERMUTE;
+       return optstring;
+}
+int _getopt_internal_r_a (int argc, char *const *argv, const char *optstring, const struct option_a *longopts, int *longind, int long_only, struct _getopt_data_a *d, int posixly_correct)
+{
+       int print_errors = d->opterr;
+       if (argc < 1)
+               return -1;
+       d->optarg = NULL;
+       if (d->optind == 0 || !d->__initialized)
+       {
+               if (d->optind == 0)
+                       d->optind = 1;
+               optstring = _getopt_initialize_a (optstring, d, posixly_correct);
+               d->__initialized = 1;
+       }
+       else if (optstring[0] == '-' || optstring[0] == '+')
+               optstring++;
+       if (optstring[0] == ':')
+               print_errors = 0;
+       if (d->__nextchar == NULL || *d->__nextchar == '\0')
+       {
+               if (d->__last_nonopt > d->optind)
+                       d->__last_nonopt = d->optind;
+               if (d->__first_nonopt > d->optind)
+                       d->__first_nonopt = d->optind;
+               if (d->__ordering == PERMUTE)
+               {
+                       if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
+                               exchange_a ((char **) argv, d);
+                       else if (d->__last_nonopt != d->optind)
+                               d->__first_nonopt = d->optind;
+                       while (d->optind < argc && (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0'))
+                               d->optind++;
+                       d->__last_nonopt = d->optind;
+               }
+               if (d->optind != argc && !strcmp(argv[d->optind], "--"))
+               {
+                       d->optind++;
+                       if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
+                               exchange_a((char **) argv, d);
+                       else if (d->__first_nonopt == d->__last_nonopt)
+                               d->__first_nonopt = d->optind;
+                       d->__last_nonopt = argc;
+                       d->optind = argc;
+               }
+               if (d->optind == argc)
+               {
+                       if (d->__first_nonopt != d->__last_nonopt)
+                               d->optind = d->__first_nonopt;
+                       return -1;
+               }
+               if ((argv[d->optind][0] != '-' || argv[d->optind][1] == '\0'))
+               {
+                       if (d->__ordering == REQUIRE_ORDER)
+                               return -1;
+                       d->optarg = argv[d->optind++];
+                       return 1;
+               }
+               d->__nextchar = (argv[d->optind] + 1 + (longopts != NULL && argv[d->optind][1] == '-'));
+       }
+       if (longopts != NULL && (argv[d->optind][1] == '-' || (long_only && (argv[d->optind][2] || !strchr(optstring, argv[d->optind][1])))))
+       {
+               char *nameend;
+               unsigned int namelen;
+               const struct option_a *p;
+               const struct option_a *pfound = NULL;
+               struct option_list
+               {
+                       const struct option_a *p;
+                       struct option_list *next;
+               } *ambig_list = NULL;
+               int exact = 0;
+               int indfound = -1;
+               int option_index;
+               for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++);
+               namelen = (unsigned int)(nameend - d->__nextchar);
+               for (p = longopts, option_index = 0; p->name; p++, option_index++)
+                       if (!strncmp(p->name, d->__nextchar, namelen))
+                       {
+                               if (namelen == (unsigned int)strlen(p->name))
+                               {
+                                       pfound = p;
+                                       indfound = option_index;
+                                       exact = 1;
+                                       break;
+                               }
+                               else if (pfound == NULL)
+                               {
+                                       pfound = p;
+                                       indfound = option_index;
+                               }
+                               else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val)
+                               {
+                                       struct option_list *newp = (struct option_list*)alloca(sizeof(*newp));
+                                       newp->p = p;
+                                       newp->next = ambig_list;
+                                       ambig_list = newp;
+                               }
+                       }
+                       if (ambig_list != NULL && !exact)
+                       {
+                               if (print_errors)
+                               {
+                                       struct option_list first;
+                                       first.p = pfound;
+                                       first.next = ambig_list;
+                                       ambig_list = &first;
+                                       fprintf (stderr, "%s: option '%s' is ambiguous; possibilities:", argv[0], argv[d->optind]);
+                                       do
+                                       {
+                                               fprintf (stderr, " '--%s'", ambig_list->p->name);
+                                               ambig_list = ambig_list->next;
+                                       }
+                                       while (ambig_list != NULL);
+                                       fputc ('\n', stderr);
+                               }
+                               d->__nextchar += strlen(d->__nextchar);
+                               d->optind++;
+                               d->optopt = 0;
+                               return '?';
+                       }
+                       if (pfound != NULL)
+                       {
+                               option_index = indfound;
+                               d->optind++;
+                               if (*nameend)
+                               {
+                                       if (pfound->has_arg)
+                                               d->optarg = nameend + 1;
+                                       else
+                                       {
+                                               if (print_errors)
+                                               {
+                                                       if (argv[d->optind - 1][1] == '-')
+                                                       {
+                                                               fprintf(stderr, "%s: option '--%s' doesn't allow an argument\n",argv[0], pfound->name);
+                                                       }
+                                                       else
+                                                       {
+                                                               fprintf(stderr, "%s: option '%c%s' doesn't allow an argument\n",argv[0], argv[d->optind - 1][0],pfound->name);
+                                                       }
+                                               }
+                                               d->__nextchar += strlen(d->__nextchar);
+                                               d->optopt = pfound->val;
+                                               return '?';
+                                       }
+                               }
+                               else if (pfound->has_arg == 1)
+                               {
+                                       if (d->optind < argc)
+                                               d->optarg = argv[d->optind++];
+                                       else
+                                       {
+                                               if (print_errors)
+                                               {
+                                                       fprintf(stderr,"%s: option '--%s' requires an argument\n",argv[0], pfound->name);
+                                               }
+                                               d->__nextchar += strlen(d->__nextchar);
+                                               d->optopt = pfound->val;
+                                               return optstring[0] == ':' ? ':' : '?';
+                                       }
+                               }
+                               d->__nextchar += strlen(d->__nextchar);
+                               if (longind != NULL)
+                                       *longind = option_index;
+                               if (pfound->flag)
+                               {
+                                       *(pfound->flag) = pfound->val;
+                                       return 0;
+                               }
+                               return pfound->val;
+                       }
+                       if (!long_only || argv[d->optind][1] == '-' || strchr(optstring, *d->__nextchar) == NULL)
+                       {
+                               if (print_errors)
+                               {
+                                       if (argv[d->optind][1] == '-')
+                                       {
+                                               fprintf(stderr, "%s: unrecognized option '--%s'\n",argv[0], d->__nextchar);
+                                       }
+                                       else
+                                       {
+                                               fprintf(stderr, "%s: unrecognized option '%c%s'\n",argv[0], argv[d->optind][0], d->__nextchar);
+                                       }
+                               }
+                               d->__nextchar = (char *)"";
+                               d->optind++;
+                               d->optopt = 0;
+                               return '?';
+                       }
+       }
+       {
+               char c = *d->__nextchar++;
+               char *temp = (char*)strchr(optstring, c);
+               if (*d->__nextchar == '\0')
+                       ++d->optind;
+               if (temp == NULL || c == ':' || c == ';')
+               {
+                       if (print_errors)
+                       {
+                               fprintf(stderr, "%s: invalid option -- '%c'\n", argv[0], c);
+                       }
+                       d->optopt = c;
+                       return '?';
+               }
+               if (temp[0] == 'W' && temp[1] == ';')
+               {
+                       char *nameend;
+                       const struct option_a *p;
+                       const struct option_a *pfound = NULL;
+                       int exact = 0;
+                       int ambig = 0;
+                       int indfound = 0;
+                       int option_index;
+                       if (longopts == NULL)
+                               goto no_longs;
+                       if (*d->__nextchar != '\0')
+                       {
+                               d->optarg = d->__nextchar;
+                               d->optind++;
+                       }
+                       else if (d->optind == argc)
+                       {
+                               if (print_errors)
+                               {
+                                       fprintf(stderr,"%s: option requires an argument -- '%c'\n",argv[0], c);
+                               }
+                               d->optopt = c;
+                               if (optstring[0] == ':')
+                                       c = ':';
+                               else
+                                       c = '?';
+                               return c;
+                       }
+                       else
+                               d->optarg = argv[d->optind++];
+                       for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != '='; nameend++);
+                       for (p = longopts, option_index = 0; p->name; p++, option_index++)
+                               if (!strncmp(p->name, d->__nextchar, nameend - d->__nextchar))
+                               {
+                                       if ((unsigned int) (nameend - d->__nextchar) == strlen(p->name))
+                                       {
+                                               pfound = p;
+                                               indfound = option_index;
+                                               exact = 1;
+                                               break;
+                                       }
+                                       else if (pfound == NULL)
+                                       {
+                                               pfound = p;
+                                               indfound = option_index;
+                                       }
+                                       else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val)
+                                               ambig = 1;
+                               }
+                               if (ambig && !exact)
+                               {
+                                       if (print_errors)
+                                       {
+                                               fprintf(stderr, "%s: option '-W %s' is ambiguous\n",argv[0], d->optarg);
+                                       }
+                                       d->__nextchar += strlen(d->__nextchar);
+                                       d->optind++;
+                                       return '?';
+                               }
+                               if (pfound != NULL)
+                               {
+                                       option_index = indfound;
+                                       if (*nameend)
+                                       {
+                                               if (pfound->has_arg)
+                                                       d->optarg = nameend + 1;
+                                               else
+                                               {
+                                                       if (print_errors)
+                                                       {
+                                                               fprintf(stderr, "%s: option '-W %s' doesn't allow an argument\n",argv[0], pfound->name);
+                                                       }
+                                                       d->__nextchar += strlen(d->__nextchar);
+                                                       return '?';
+                                               }
+                                       }
+                                       else if (pfound->has_arg == 1)
+                                       {
+                                               if (d->optind < argc)
+                                                       d->optarg = argv[d->optind++];
+                                               else
+                                               {
+                                                       if (print_errors)
+                                                       {
+                                                               fprintf(stderr, "%s: option '-W %s' requires an argument\n",argv[0], pfound->name);
+                                                       }
+                                                       d->__nextchar += strlen(d->__nextchar);
+                                                       return optstring[0] == ':' ? ':' : '?';
+                                               }
+                                       }
+                                       else
+                                               d->optarg = NULL;
+                                       d->__nextchar += strlen(d->__nextchar);
+                                       if (longind != NULL)
+                                               *longind = option_index;
+                                       if (pfound->flag)
+                                       {
+                                               *(pfound->flag) = pfound->val;
+                                               return 0;
+                                       }
+                                       return pfound->val;
+                               }
+no_longs:
+                               d->__nextchar = NULL;
+                               return 'W';
+               }
+               if (temp[1] == ':')
+               {
+                       if (temp[2] == ':')
+                       {
+                               if (*d->__nextchar != '\0')
+                               {
+                                       d->optarg = d->__nextchar;
+                                       d->optind++;
+                               }
+                               else
+                                       d->optarg = NULL;
+                               d->__nextchar = NULL;
+                       }
+                       else
+                       {
+                               if (*d->__nextchar != '\0')
+                               {
+                                       d->optarg = d->__nextchar;
+                                       d->optind++;
+                               }
+                               else if (d->optind == argc)
+                               {
+                                       if (print_errors)
+                                       {
+                                               fprintf(stderr,"%s: option requires an argument -- '%c'\n",argv[0], c);
+                                       }
+                                       d->optopt = c;
+                                       if (optstring[0] == ':')
+                                               c = ':';
+                                       else
+                                               c = '?';
+                               }
+                               else
+                                       d->optarg = argv[d->optind++];
+                               d->__nextchar = NULL;
+                       }
+               }
+               return c;
+       }
+}
+int _getopt_internal_a (int argc, char *const *argv, const char *optstring, const struct option_a *longopts, int *longind, int long_only, int posixly_correct)
+{
+       int result;
+       getopt_data_a.optind = optind;
+       getopt_data_a.opterr = opterr;
+       result = _getopt_internal_r_a (argc, argv, optstring, longopts,longind, long_only, &getopt_data_a,posixly_correct);
+       optind = getopt_data_a.optind;
+       optarg_a = getopt_data_a.optarg;
+       optopt = getopt_data_a.optopt;
+       return result;
+}
+int getopt_a (int argc, char *const *argv, const char *optstring) _GETOPT_THROW
+{
+       return _getopt_internal_a (argc, argv, optstring, (const struct option_a *) 0, (int *) 0, 0, 0);
+}
+int getopt_long_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW
+{
+       return _getopt_internal_a (argc, argv, options, long_options, opt_index, 0, 0);
+}
+int getopt_long_only_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW
+{
+       return _getopt_internal_a (argc, argv, options, long_options, opt_index, 1, 0);
+}
+int _getopt_long_r_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index, struct _getopt_data_a *d)
+{
+       return _getopt_internal_r_a (argc, argv, options, long_options, opt_index,0, d, 0);
+}
+int _getopt_long_only_r_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index, struct _getopt_data_a *d)
+{
+       return _getopt_internal_r_a (argc, argv, options, long_options, opt_index, 1, d, 0);
+}
+
+//
+//
+//     Unicode Structures and Functions
+// 
+//
+
+static struct _getopt_data_w
+{
+       int optind;
+       int opterr;
+       int optopt;
+       wchar_t *optarg;
+       int __initialized;
+       wchar_t *__nextchar;
+       enum ENUM_ORDERING __ordering;
+       int __posixly_correct;
+       int __first_nonopt;
+       int __last_nonopt;
+} getopt_data_w;
+wchar_t *optarg_w;
+
+static void exchange_w(wchar_t **argv, struct _getopt_data_w *d)
+{
+       int bottom = d->__first_nonopt;
+       int middle = d->__last_nonopt;
+       int top = d->optind;
+       wchar_t *tem;
+       while (top > middle && middle > bottom)
+       {
+               if (top - middle > middle - bottom)
+               {
+                       int len = middle - bottom;
+                       register int i;
+                       for (i = 0; i < len; i++)
+                       {
+                               tem = argv[bottom + i];
+                               argv[bottom + i] = argv[top - (middle - bottom) + i];
+                               argv[top - (middle - bottom) + i] = tem;
+                       }
+                       top -= len;
+               }
+               else
+               {
+                       int len = top - middle;
+                       register int i;
+                       for (i = 0; i < len; i++)
+                       {
+                               tem = argv[bottom + i];
+                               argv[bottom + i] = argv[middle + i];
+                               argv[middle + i] = tem;
+                       }
+                       bottom += len;
+               }
+       }
+       d->__first_nonopt += (d->optind - d->__last_nonopt);
+       d->__last_nonopt = d->optind;
+}
+static const wchar_t *_getopt_initialize_w (const wchar_t *optstring, struct _getopt_data_w *d, int posixly_correct)
+{
+       d->__first_nonopt = d->__last_nonopt = d->optind;
+       d->__nextchar = NULL;
+       d->__posixly_correct = posixly_correct | !!_wgetenv(L"POSIXLY_CORRECT");
+       if (optstring[0] == L'-')
+       {
+               d->__ordering = RETURN_IN_ORDER;
+               ++optstring;
+       }
+       else if (optstring[0] == L'+')
+       {
+               d->__ordering = REQUIRE_ORDER;
+               ++optstring;
+       }
+       else if (d->__posixly_correct)
+               d->__ordering = REQUIRE_ORDER;
+       else
+               d->__ordering = PERMUTE;
+       return optstring;
+}
+int _getopt_internal_r_w (int argc, wchar_t *const *argv, const wchar_t *optstring, const struct option_w *longopts, int *longind, int long_only, struct _getopt_data_w *d, int posixly_correct)
+{
+       int print_errors = d->opterr;
+       if (argc < 1)
+               return -1;
+       d->optarg = NULL;
+       if (d->optind == 0 || !d->__initialized)
+       {
+               if (d->optind == 0)
+                       d->optind = 1;
+               optstring = _getopt_initialize_w (optstring, d, posixly_correct);
+               d->__initialized = 1;
+       }
+       else if (optstring[0] == L'-' || optstring[0] == L'+')
+               optstring++;
+       if (optstring[0] == L':')
+               print_errors = 0;
+       if (d->__nextchar == NULL || *d->__nextchar == L'\0')
+       {
+               if (d->__last_nonopt > d->optind)
+                       d->__last_nonopt = d->optind;
+               if (d->__first_nonopt > d->optind)
+                       d->__first_nonopt = d->optind;
+               if (d->__ordering == PERMUTE)
+               {
+                       if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
+                               exchange_w((wchar_t **) argv, d);
+                       else if (d->__last_nonopt != d->optind)
+                               d->__first_nonopt = d->optind;
+                       while (d->optind < argc && (argv[d->optind][0] != L'-' || argv[d->optind][1] == L'\0'))
+                               d->optind++;
+                       d->__last_nonopt = d->optind;
+               }
+               if (d->optind != argc && !wcscmp(argv[d->optind], L"--"))
+               {
+                       d->optind++;
+                       if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
+                               exchange_w((wchar_t **) argv, d);
+                       else if (d->__first_nonopt == d->__last_nonopt)
+                               d->__first_nonopt = d->optind;
+                       d->__last_nonopt = argc;
+                       d->optind = argc;
+               }
+               if (d->optind == argc)
+               {
+                       if (d->__first_nonopt != d->__last_nonopt)
+                               d->optind = d->__first_nonopt;
+                       return -1;
+               }
+               if ((argv[d->optind][0] != L'-' || argv[d->optind][1] == L'\0'))
+               {
+                       if (d->__ordering == REQUIRE_ORDER)
+                               return -1;
+                       d->optarg = argv[d->optind++];
+                       return 1;
+               }
+               d->__nextchar = (argv[d->optind] + 1 + (longopts != NULL && argv[d->optind][1] == L'-'));
+       }
+       if (longopts != NULL && (argv[d->optind][1] == L'-' || (long_only && (argv[d->optind][2] || !wcschr(optstring, argv[d->optind][1])))))
+       {
+               wchar_t *nameend;
+               unsigned int namelen;
+               const struct option_w *p;
+               const struct option_w *pfound = NULL;
+               struct option_list
+               {
+                       const struct option_w *p;
+                       struct option_list *next;
+               } *ambig_list = NULL;
+               int exact = 0;
+               int indfound = -1;
+               int option_index;
+               for (nameend = d->__nextchar; *nameend && *nameend != L'='; nameend++);
+               namelen = (unsigned int)(nameend - d->__nextchar);
+               for (p = longopts, option_index = 0; p->name; p++, option_index++)
+                       if (!wcsncmp(p->name, d->__nextchar, namelen))
+                       {
+                               if (namelen == (unsigned int)wcslen(p->name))
+                               {
+                                       pfound = p;
+                                       indfound = option_index;
+                                       exact = 1;
+                                       break;
+                               }
+                               else if (pfound == NULL)
+                               {
+                                       pfound = p;
+                                       indfound = option_index;
+                               }
+                               else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val)
+                               {
+                                       struct option_list *newp = (struct option_list*)alloca(sizeof(*newp));
+                                       newp->p = p;
+                                       newp->next = ambig_list;
+                                       ambig_list = newp;
+                               }
+                       }
+                       if (ambig_list != NULL && !exact)
+                       {
+                               if (print_errors)
+                               {                                               
+                                       struct option_list first;
+                                       first.p = pfound;
+                                       first.next = ambig_list;
+                                       ambig_list = &first;
+                                       fwprintf(stderr, L"%s: option '%s' is ambiguous; possibilities:", argv[0], argv[d->optind]);
+                                       do
+                                       {
+                                               fwprintf (stderr, L" '--%s'", ambig_list->p->name);
+                                               ambig_list = ambig_list->next;
+                                       }
+                                       while (ambig_list != NULL);
+                                       fputwc (L'\n', stderr);
+                               }
+                               d->__nextchar += wcslen(d->__nextchar);
+                               d->optind++;
+                               d->optopt = 0;
+                               return L'?';
+                       }
+                       if (pfound != NULL)
+                       {
+                               option_index = indfound;
+                               d->optind++;
+                               if (*nameend)
+                               {
+                                       if (pfound->has_arg)
+                                               d->optarg = nameend + 1;
+                                       else
+                                       {
+                                               if (print_errors)
+                                               {
+                                                       if (argv[d->optind - 1][1] == L'-')
+                                                       {
+                                                               fwprintf(stderr, L"%s: option '--%s' doesn't allow an argument\n",argv[0], pfound->name);
+                                                       }
+                                                       else
+                                                       {
+                                                               fwprintf(stderr, L"%s: option '%c%s' doesn't allow an argument\n",argv[0], argv[d->optind - 1][0],pfound->name);
+                                                       }
+                                               }
+                                               d->__nextchar += wcslen(d->__nextchar);
+                                               d->optopt = pfound->val;
+                                               return L'?';
+                                       }
+                               }
+                               else if (pfound->has_arg == 1)
+                               {
+                                       if (d->optind < argc)
+                                               d->optarg = argv[d->optind++];
+                                       else
+                                       {
+                                               if (print_errors)
+                                               {
+                                                       fwprintf(stderr,L"%s: option '--%s' requires an argument\n",argv[0], pfound->name);
+                                               }
+                                               d->__nextchar += wcslen(d->__nextchar);
+                                               d->optopt = pfound->val;
+                                               return optstring[0] == L':' ? L':' : L'?';
+                                       }
+                               }
+                               d->__nextchar += wcslen(d->__nextchar);
+                               if (longind != NULL)
+                                       *longind = option_index;
+                               if (pfound->flag)
+                               {
+                                       *(pfound->flag) = pfound->val;
+                                       return 0;
+                               }
+                               return pfound->val;
+                       }
+                       if (!long_only || argv[d->optind][1] == L'-' || wcschr(optstring, *d->__nextchar) == NULL)
+                       {
+                               if (print_errors)
+                               {
+                                       if (argv[d->optind][1] == L'-')
+                                       {
+                                               fwprintf(stderr, L"%s: unrecognized option '--%s'\n",argv[0], d->__nextchar);
+                                       }
+                                       else
+                                       {
+                                               fwprintf(stderr, L"%s: unrecognized option '%c%s'\n",argv[0], argv[d->optind][0], d->__nextchar);
+                                       }
+                               }
+                               d->__nextchar = (wchar_t *)L"";
+                               d->optind++;
+                               d->optopt = 0;
+                               return L'?';
+                       }
+       }
+       {
+               wchar_t c = *d->__nextchar++;
+               wchar_t *temp = (wchar_t*)wcschr(optstring, c);
+               if (*d->__nextchar == L'\0')
+                       ++d->optind;
+               if (temp == NULL || c == L':' || c == L';')
+               {
+                       if (print_errors)
+                       {
+                               fwprintf(stderr, L"%s: invalid option -- '%c'\n", argv[0], c);
+                       }
+                       d->optopt = c;
+                       return L'?';
+               }
+               if (temp[0] == L'W' && temp[1] == L';')
+               {
+                       wchar_t *nameend;
+                       const struct option_w *p;
+                       const struct option_w *pfound = NULL;
+                       int exact = 0;
+                       int ambig = 0;
+                       int indfound = 0;
+                       int option_index;
+                       if (longopts == NULL)
+                               goto no_longs;
+                       if (*d->__nextchar != L'\0')
+                       {
+                               d->optarg = d->__nextchar;
+                               d->optind++;
+                       }
+                       else if (d->optind == argc)
+                       {
+                               if (print_errors)
+                               {
+                                       fwprintf(stderr,L"%s: option requires an argument -- '%c'\n",argv[0], c);
+                               }
+                               d->optopt = c;
+                               if (optstring[0] == L':')
+                                       c = L':';
+                               else
+                                       c = L'?';
+                               return c;
+                       }
+                       else
+                               d->optarg = argv[d->optind++];
+                       for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != L'='; nameend++);
+                       for (p = longopts, option_index = 0; p->name; p++, option_index++)
+                               if (!wcsncmp(p->name, d->__nextchar, nameend - d->__nextchar))
+                               {
+                                       if ((unsigned int) (nameend - d->__nextchar) == wcslen(p->name))
+                                       {
+                                               pfound = p;
+                                               indfound = option_index;
+                                               exact = 1;
+                                               break;
+                                       }
+                                       else if (pfound == NULL)
+                                       {
+                                               pfound = p;
+                                               indfound = option_index;
+                                       }
+                                       else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val)
+                                               ambig = 1;
+                               }
+                               if (ambig && !exact)
+                               {
+                                       if (print_errors)
+                                       {
+                                               fwprintf(stderr, L"%s: option '-W %s' is ambiguous\n",argv[0], d->optarg);
+                                       }
+                                       d->__nextchar += wcslen(d->__nextchar);
+                                       d->optind++;
+                                       return L'?';
+                               }
+                               if (pfound != NULL)
+                               {
+                                       option_index = indfound;
+                                       if (*nameend)
+                                       {
+                                               if (pfound->has_arg)
+                                                       d->optarg = nameend + 1;
+                                               else
+                                               {
+                                                       if (print_errors)
+                                                       {
+                                                               fwprintf(stderr, L"%s: option '-W %s' doesn't allow an argument\n",argv[0], pfound->name);
+                                                       }
+                                                       d->__nextchar += wcslen(d->__nextchar);
+                                                       return L'?';
+                                               }
+                                       }
+                                       else if (pfound->has_arg == 1)
+                                       {
+                                               if (d->optind < argc)
+                                                       d->optarg = argv[d->optind++];
+                                               else
+                                               {
+                                                       if (print_errors)
+                                                       {
+                                                               fwprintf(stderr, L"%s: option '-W %s' requires an argument\n",argv[0], pfound->name);
+                                                       }
+                                                       d->__nextchar += wcslen(d->__nextchar);
+                                                       return optstring[0] == L':' ? L':' : L'?';
+                                               }
+                                       }
+                                       else
+                                               d->optarg = NULL;
+                                       d->__nextchar += wcslen(d->__nextchar);
+                                       if (longind != NULL)
+                                               *longind = option_index;
+                                       if (pfound->flag)
+                                       {
+                                               *(pfound->flag) = pfound->val;
+                                               return 0;
+                                       }
+                                       return pfound->val;
+                               }
+no_longs:
+                               d->__nextchar = NULL;
+                               return L'W';
+               }
+               if (temp[1] == L':')
+               {
+                       if (temp[2] == L':')
+                       {
+                               if (*d->__nextchar != L'\0')
+                               {
+                                       d->optarg = d->__nextchar;
+                                       d->optind++;
+                               }
+                               else
+                                       d->optarg = NULL;
+                               d->__nextchar = NULL;
+                       }
+                       else
+                       {
+                               if (*d->__nextchar != L'\0')
+                               {
+                                       d->optarg = d->__nextchar;
+                                       d->optind++;
+                               }
+                               else if (d->optind == argc)
+                               {
+                                       if (print_errors)
+                                       {
+                                               fwprintf(stderr,L"%s: option requires an argument -- '%c'\n",argv[0], c);
+                                       }
+                                       d->optopt = c;
+                                       if (optstring[0] == L':')
+                                               c = L':';
+                                       else
+                                               c = L'?';
+                               }
+                               else
+                                       d->optarg = argv[d->optind++];
+                               d->__nextchar = NULL;
+                       }
+               }
+               return c;
+       }
+}
+int _getopt_internal_w (int argc, wchar_t *const *argv, const wchar_t *optstring, const struct option_w *longopts, int *longind, int long_only, int posixly_correct)
+{
+       int result;
+       getopt_data_w.optind = optind;
+       getopt_data_w.opterr = opterr;
+       result = _getopt_internal_r_w (argc, argv, optstring, longopts,longind, long_only, &getopt_data_w,posixly_correct);
+       optind = getopt_data_w.optind;
+       optarg_w = getopt_data_w.optarg;
+       optopt = getopt_data_w.optopt;
+       return result;
+}
+int getopt_w (int argc, wchar_t *const *argv, const wchar_t *optstring) _GETOPT_THROW
+{
+       return _getopt_internal_w (argc, argv, optstring, (const struct option_w *) 0, (int *) 0, 0, 0);
+}
+int getopt_long_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW
+{
+       return _getopt_internal_w (argc, argv, options, long_options, opt_index, 0, 0);
+}
+int getopt_long_only_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW
+{
+       return _getopt_internal_w (argc, argv, options, long_options, opt_index, 1, 0);
+}
+int _getopt_long_r_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d)
+{
+       return _getopt_internal_r_w (argc, argv, options, long_options, opt_index,0, d, 0);
+}
+int _getopt_long_only_r_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d)
+{
+       return _getopt_internal_r_w (argc, argv, options, long_options, opt_index, 1, d, 0);
+}
diff --git a/src/third_party/win32/getopt.h b/src/third_party/win32/getopt.h
new file mode 100644 (file)
index 0000000..ca74ba6
--- /dev/null
@@ -0,0 +1,136 @@
+/* Getopt for Microsoft C
+This code is a modification of the Free Software Foundation, Inc.
+Getopt library for parsing command line argument the purpose was
+to provide a Microsoft Visual C friendly derivative. This code
+provides functionality for both Unicode and Multibyte builds.
+
+Date: 02/03/2011 - Ludvik Jerabek - Initial Release
+Version: 1.0
+Comment: Supports getopt, getopt_long, and getopt_long_only
+and POSIXLY_CORRECT environment flag
+License: LGPL
+
+Revisions:
+
+02/03/2011 - Ludvik Jerabek - Initial Release
+02/20/2011 - Ludvik Jerabek - Fixed compiler warnings at Level 4
+07/05/2011 - Ludvik Jerabek - Added no_argument, required_argument, optional_argument defs
+08/03/2011 - Ludvik Jerabek - Fixed non-argument runtime bug which caused runtime exception
+08/09/2011 - Ludvik Jerabek - Added code to export functions for DLL and LIB
+02/15/2012 - Ludvik Jerabek - Fixed _GETOPT_THROW definition missing in implementation file
+08/01/2012 - Ludvik Jerabek - Created separate functions for char and wchar_t characters so single dll can do both unicode and ansi
+10/15/2012 - Ludvik Jerabek - Modified to match latest GNU features
+06/19/2015 - Ludvik Jerabek - Fixed maximum option limitation caused by option_a (255) and option_w (65535) structure val variable
+
+**DISCLAIMER**
+THIS MATERIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
+EITHER EXPRESS OR IMPLIED, INCLUDING, BUT Not LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE, OR NON-INFRINGEMENT. SOME JURISDICTIONS DO NOT ALLOW THE
+EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT
+APPLY TO YOU. IN NO EVENT WILL I BE LIABLE TO ANY PARTY FOR ANY
+DIRECT, INDIRECT, SPECIAL OR OTHER CONSEQUENTIAL DAMAGES FOR ANY
+USE OF THIS MATERIAL INCLUDING, WITHOUT LIMITATION, ANY LOST
+PROFITS, BUSINESS INTERRUPTION, LOSS OF PROGRAMS OR OTHER DATA ON
+YOUR INFORMATION HANDLING SYSTEM OR OTHERWISE, EVEN If WE ARE
+EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+#ifndef __GETOPT_H_
+       #define __GETOPT_H_
+
+       #ifdef _GETOPT_API
+               #undef _GETOPT_API
+       #endif
+
+       #if defined(EXPORTS_GETOPT) && defined(STATIC_GETOPT)
+               #error "The preprocessor definitions of EXPORTS_GETOPT and STATIC_GETOPT can only be used individually"
+       #elif defined(STATIC_GETOPT)
+//             #pragma message("Warning static builds of getopt violate the Lesser GNU Public License")
+               #define _GETOPT_API
+       #elif defined(EXPORTS_GETOPT)
+               #pragma message("Exporting getopt library")
+               #define _GETOPT_API __declspec(dllexport)
+       #else
+               #pragma message("Importing getopt library")
+               #define _GETOPT_API __declspec(dllimport)
+       #endif
+
+       // Change behavior for C\C++
+       #ifdef __cplusplus
+               #define _BEGIN_EXTERN_C extern "C" {
+               #define _END_EXTERN_C }
+               #define _GETOPT_THROW throw()
+       #else
+               #define _BEGIN_EXTERN_C
+               #define _END_EXTERN_C
+               #define _GETOPT_THROW
+       #endif
+
+       // Standard GNU options
+       #define null_argument           0       /*Argument Null*/
+       #define no_argument                     0       /*Argument Switch Only*/
+       #define required_argument       1       /*Argument Required*/
+       #define optional_argument       2       /*Argument Optional*/   
+
+       // Shorter Options
+       #define ARG_NULL        0       /*Argument Null*/
+       #define ARG_NONE        0       /*Argument Switch Only*/
+       #define ARG_REQ         1       /*Argument Required*/
+       #define ARG_OPT         2       /*Argument Optional*/
+
+       #include <string.h>
+       #include <wchar.h>
+
+_BEGIN_EXTERN_C
+
+       extern _GETOPT_API int optind;
+       extern _GETOPT_API int opterr;
+       extern _GETOPT_API int optopt;
+
+       // Ansi
+       struct option_a
+       {
+               const char* name;
+               int has_arg;
+               int *flag;
+               int val;
+       };
+       extern _GETOPT_API char *optarg_a;
+       extern _GETOPT_API int getopt_a(int argc, char *const *argv, const char *optstring) _GETOPT_THROW;
+       extern _GETOPT_API int getopt_long_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW;
+       extern _GETOPT_API int getopt_long_only_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW;
+
+       // Unicode
+       struct option_w
+       {
+               const wchar_t* name;
+               int has_arg;
+               int *flag;
+               int val;
+       };
+       extern _GETOPT_API wchar_t *optarg_w;
+       extern _GETOPT_API int getopt_w(int argc, wchar_t *const *argv, const wchar_t *optstring) _GETOPT_THROW;
+       extern _GETOPT_API int getopt_long_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW;
+       extern _GETOPT_API int getopt_long_only_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW;   
+       
+_END_EXTERN_C
+
+       #undef _BEGIN_EXTERN_C
+       #undef _END_EXTERN_C
+       #undef _GETOPT_THROW
+       #undef _GETOPT_API
+
+       #ifdef _UNICODE
+               #define getopt getopt_w
+               #define getopt_long getopt_long_w
+               #define getopt_long_only getopt_long_only_w
+               #define option option_w
+               #define optarg optarg_w
+       #else
+               #define getopt getopt_a
+               #define getopt_long getopt_long_a
+               #define getopt_long_only getopt_long_only_a
+               #define option option_a
+               #define optarg optarg_a
+       #endif
+#endif  // __GETOPT_H_
index 04e5f137f2f82b53c8b55d17e8fd61f372bc0112..8b6c015ee429c67a14e4627e6ea9210945d84090 100644 (file)
@@ -1,7 +1,7 @@
 function(addtest name)
   add_test(
     NAME "test.${name}"
-    COMMAND ${CMAKE_SOURCE_DIR}/test/run ${name}
+    COMMAND bash ${CMAKE_SOURCE_DIR}/test/run ${name}
     WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
   )
   set_tests_properties(
index 2db64ec03a33e82ff96bdd1e0dcb960c0489e4e0..0f06d3ee31d516e7d8dff25a4e29e6a5cb5d1762 100644 (file)
 
 #include <string>
 
+#ifdef _MSC_VER
+#  define DOCTEST_CONFIG_USE_STD_HEADERS
+#endif
+
 namespace TestUtil {
 
 // This class is intended to be instantiated in all test cases that create local
index 998cde17e586c0435c24f48d3c5a1b62bae4c933..c61a62c0bb6c2eaf0022f3e3790f4a6411b82a78 100644 (file)
@@ -22,7 +22,9 @@
 
 #include "third_party/doctest.h"
 
-#include <unistd.h>
+#ifdef HAVE_UNISTD_H
+#  include <unistd.h>
+#endif
 
 using TestUtil::TestContext;
 
index d89dd8f77ee0cef9088b0ee33a4890a4e6a94447..9425fc5a7b01c7bad05d9da54b14f7621e3c870f 100644 (file)
@@ -23,6 +23,8 @@
 
 #include "third_party/doctest.h"
 
+#include <algorithm>
+
 using TestUtil::TestContext;
 
 TEST_SUITE_BEGIN("Util");
@@ -297,6 +299,16 @@ TEST_CASE("Util::get_extension")
   CHECK(Util::get_extension("/foo/bar/f.abc.txt") == ".txt");
 }
 
+static inline std::string
+os_path(std::string path)
+{
+#if !defined(HAVE_DIRENT_H) && DIR_DELIM_CH != '/'
+  std::replace(path.begin(), path.end(), '/', DIR_DELIM_CH);
+#endif
+
+  return path;
+}
+
 TEST_CASE("Util::get_level_1_files")
 {
   TestContext test_context;
@@ -339,13 +351,13 @@ TEST_CASE("Util::get_level_1_files")
                 return f1->path() < f2->path();
               });
 
-    CHECK(files[0]->path() == "0/1/file_b");
+    CHECK(files[0]->path() == os_path("0/1/file_b"));
     CHECK(files[0]->lstat().size() == 1);
-    CHECK(files[1]->path() == "0/1/file_c");
+    CHECK(files[1]->path() == os_path("0/1/file_c"));
     CHECK(files[1]->lstat().size() == 2);
-    CHECK(files[2]->path() == "0/f/c/file_d");
+    CHECK(files[2]->path() == os_path("0/f/c/file_d"));
     CHECK(files[2]->lstat().size() == 3);
-    CHECK(files[3]->path() == "0/file_a");
+    CHECK(files[3]->path() == os_path("0/file_a"));
     CHECK(files[3]->lstat().size() == 0);
   }
 }
@@ -565,20 +577,19 @@ TEST_CASE("Util::parse_int")
   CHECK_THROWS_WITH(Util::parse_int("0 "), "invalid integer: \"0 \"");
 
   // check boundary values
-  if (sizeof(int) == 2) {
-    CHECK(Util::parse_int("-32768") == -32768);
-    CHECK(Util::parse_int("32767") == 32767);
-    CHECK_THROWS_WITH(Util::parse_int("-32768"), "invalid integer: \"-32768\"");
-    CHECK_THROWS_WITH(Util::parse_int("32768"), "invalid integer: \"32768\"");
-  }
-  if (sizeof(int) == 4) {
-    CHECK(Util::parse_int("-2147483648") == -2147483648);
-    CHECK(Util::parse_int("2147483647") == 2147483647);
-    CHECK_THROWS_WITH(Util::parse_int("-2147483649"),
-                      "invalid integer: \"-2147483649\"");
-    CHECK_THROWS_WITH(Util::parse_int("2147483648"),
-                      "invalid integer: \"2147483648\"");
-  }
+#if SIZEOF_INT == 2
+  CHECK(Util::parse_int("-32768") == -32768);
+  CHECK(Util::parse_int("32767") == 32767);
+  CHECK_THROWS_WITH(Util::parse_int("-32768"), "invalid integer: \"-32768\"");
+  CHECK_THROWS_WITH(Util::parse_int("32768"), "invalid integer: \"32768\"");
+#elif SIZEOF_INT == 4
+  CHECK(Util::parse_int("-2147483648") == -2147483648LL);
+  CHECK(Util::parse_int("2147483647") == 2147483647);
+  CHECK_THROWS_WITH(Util::parse_int("-2147483649"),
+                    "invalid integer: \"-2147483649\"");
+  CHECK_THROWS_WITH(Util::parse_int("2147483648"),
+                    "invalid integer: \"2147483648\"");
+#endif
 }
 
 TEST_CASE("Util::parse_size")
@@ -664,6 +675,9 @@ TEST_CASE("Util::read_file and Util::write_file")
 
   CHECK_THROWS_WITH(Util::write_file("", "does/not/exist"),
                     "No such file or directory");
+
+  CHECK_THROWS_WITH(Util::write_file("does/not/exist", "does/not/exist"),
+                    "No such file or directory");
 }
 
 TEST_CASE("Util::remove_extension")
@@ -870,8 +884,8 @@ TEST_CASE("Util::traverse")
   {
     CHECK_NOTHROW(Util::traverse("dir-with-files", visitor));
     REQUIRE(visited.size() == 3);
-    std::string f1 = "[f] dir-with-files/f1";
-    std::string f2 = "[f] dir-with-files/f2";
+    std::string f1 = os_path("[f] dir-with-files/f1");
+    std::string f2 = os_path("[f] dir-with-files/f2");
     CHECK(((visited[0] == f1 && visited[1] == f2)
            || (visited[0] == f2 && visited[1] == f1)));
     CHECK(visited[2] == "[d] dir-with-files");
@@ -881,8 +895,8 @@ TEST_CASE("Util::traverse")
   {
     CHECK_NOTHROW(Util::traverse("dir-with-subdir-and-file", visitor));
     REQUIRE(visited.size() == 3);
-    CHECK(visited[0] == "[f] dir-with-subdir-and-file/subdir/f");
-    CHECK(visited[1] == "[d] dir-with-subdir-and-file/subdir");
+    CHECK(visited[0] == os_path("[f] dir-with-subdir-and-file/subdir/f"));
+    CHECK(visited[1] == os_path("[d] dir-with-subdir-and-file/subdir"));
     CHECK(visited[2] == "[d] dir-with-subdir-and-file");
   }
 }
index 1348a4c07018115e0961c3dd0c640156b18428b0..93f152b278308c2da772ab7e7cedac4ec0f4e799 100644 (file)
@@ -26,6 +26,8 @@
 
 #include "third_party/doctest.h"
 
+#include <algorithm>
+
 using TestUtil::TestContext;
 
 namespace {