# Visual Studio Code
/.vscode/
+
+# Vim
+.*.sw?
+.*.un~
#
# 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)
#
--- /dev/null
+ 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.
-------------------------------------------------------------------------------
+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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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})
# 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)
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()
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()
# 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
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}
}
Args args;
- auto pos = argtext.cbegin();
+ auto pos = argtext.c_str();
std::string argbuf;
argbuf.resize(argtext.length() + 1);
auto argpos = argbuf.begin();
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()
#include "third_party/fmt/core.h"
+#include <algorithm>
+
using Logging::log;
namespace {
# include <termios.h>
#endif
+#include <algorithm>
+
namespace {
const size_t k_max_width = 120;
#include "exceptions.hpp"
#include "stats.hpp"
+#include <algorithm>
+
// Result data format
// ==================
//
#include <algorithm>
#include <fstream>
+#ifndef HAVE_DIRENT_H
+# include <filesystem>
+#endif
+
#ifdef HAVE_PWD_H
# include <pwd.h>
#endif
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);
}
#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;
}
parse_int(const std::string& value)
{
size_t end;
- long result;
+ long result = 0;
bool failed = false;
try {
result = std::stoi(value, &end, 10);
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);
#else
char* string;
asprintf(&string, "%s=%s", name.c_str(), value.c_str());
- putenv(string); // Leak to environment.
+ putenv(string); // Leak to environment.
#endif
}
return result;
}
+#ifdef HAVE_DIRENT_H
+
void
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) {
}
}
+#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)
{
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) {
#include "third_party/nonstd/string_view.hpp"
#include <functional>
+#include <ios>
#include <memory>
#include <string>
#include <utility>
// 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.
}
// 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).
#include "Util.hpp"
+#include <chrono>
+#include <thread>
+
namespace Win32Util {
std::string
}
} // 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
#include "Logging.hpp"
#include "exceptions.hpp"
+#include <algorithm>
+
using Logging::log;
ZstdCompressor::ZstdCompressor(FILE* stream, int8_t compression_level)
#ifdef HAVE_GETOPT_LONG
# include <getopt.h>
+#elif defined(_WIN32)
+# include "third_party/win32/getopt.h"
#else
# include "third_party/getopt_long.h"
#endif
#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
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.
// _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
#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
-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()
# 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")
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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_
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(
#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
#include "third_party/doctest.h"
-#include <unistd.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
using TestUtil::TestContext;
#include "third_party/doctest.h"
+#include <algorithm>
+
using TestUtil::TestContext;
TEST_SUITE_BEGIN("Util");
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;
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);
}
}
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")
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")
{
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");
{
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");
}
}
#include "third_party/doctest.h"
+#include <algorithm>
+
using TestUtil::TestContext;
namespace {