+++ /dev/null
-Gen-shell is created by Armaan Bhojwani
-
-Tasksh was made by the following people:
- The development of tasksh was made possible by the significant contributions of
- the following people:
-
- Paul Beckingham (Principal Author)
- Federico Hernandez (Principal Author)
- Dirk Deimeke (Technical Advisor & Evangelist)
-
- The following submitted code, packages or analysis, and deserve special thanks:
-
- Jörg Krause
- Ben Boeckel
- ilove zfs
- Paul Fenwick
-
- Thanks to the following, who submitted detailed bug reports and excellent
- suggestions:
-
- Kevin Gunn
- Fidel Mato
- David Stahl
- David Patrick
- jonbobbly
- hosaka
- Lars Kumbier
- Iain R. Learmonth
- Eric Hymowitz
- bjonnh
# gen-shell
![Jenkins](https://img.shields.io/jenkins/build?jobUrl=https%3A%2F%2Fbuild.bhojwani.org%2Fjob%2Fgen-shell%2F)
-A work in progress generic shell forked form [taskshell](https://github.com/GothenburgBitFactory/taskshell)
+A work in progress generic shell. This is a very reduced fork of [taskshell](https://github.com/GothenburgBitFactory/taskshell)
## Installation
Binaries can be downloaded [from here](https://build.bhojwani.org/job/gen-shell/lastSuccessfulBuild/artifact/build/src/gen-shell). Note that these binaries are compiled against libreadline7, so if your system only has libreadline8, you should symlink 8 to 7 with `sudo ln -s /usr/lib/x86_64-linux-gnu/libreadline.so.8.0 /usr/lib/x86_64-linux-gnu/libreadline.so.7`. This isn't a great solution, but it gets the job done.
${CMAKE_SOURCE_DIR}/src/libshared/src
${TASKSH_INCLUDE_DIRS})
-set (tasksh_SRCS diag.cpp
- help.cpp
- prompt.cpp
- shell.cpp)
+set (tasksh_SRCS prompt.cpp)
-set (libshared_SRCS libshared/src/Color.cpp libshared/src/Color.h
- libshared/src/Datetime.cpp libshared/src/Datetime.h
- libshared/src/Duration.cpp libshared/src/Duration.h
- libshared/src/FS.cpp libshared/src/FS.h
- libshared/src/Lexer.cpp libshared/src/Lexer.h
- libshared/src/Pig.cpp libshared/src/Pig.h
- libshared/src/shared.cpp libshared/src/shared.h
- libshared/src/format.cpp libshared/src/format.h
- libshared/src/unicode.cpp libshared/src/unicode.h
- libshared/src/utf8.cpp libshared/src/utf8.h
- libshared/src/wcwidth6.cpp)
+# set (libshared_SRCS libshared/src/Color.cpp libshared/src/Color.h
+ # libshared/src/Datetime.cpp libshared/src/Datetime.h
+ # libshared/src/Duration.cpp libshared/src/Duration.h
+ # libshared/src/FS.cpp libshared/src/FS.h
+ # libshared/src/Lexer.cpp libshared/src/Lexer.h
+ # libshared/src/Pig.cpp libshared/src/Pig.h
+ # libshared/src/shared.cpp libshared/src/shared.h
+ # libshared/src/format.cpp libshared/src/format.h
+ # libshared/src/unicode.cpp libshared/src/unicode.h
+ # libshared/src/utf8.cpp libshared/src/utf8.h
+ # libshared/src/wcwidth6.cpp)
add_library (tasksh STATIC ${tasksh_SRCS})
-add_library (libshared STATIC ${libshared_SRCS})
+# add_library (libshared STATIC ${libshared_SRCS})
add_executable (gen-sh_executable main.cpp)
-target_link_libraries (gen-sh_executable tasksh libshared ${GEN-SHELL_LIBRARIES})
+target_link_libraries (gen-sh_executable tasksh ${GEN-SHELL_LIBRARIES})
set_property (TARGET gen-sh_executable PROPERTY OUTPUT_NAME "gen-shell")
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez, 2020 Armaan Bhojwani.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <iostream>
-#include <string>
-#include <vector>
-#include <cstring>
-#include <stdlib.h>
-#include <FS.h>
-#include <Color.h>
-#include <shared.h>
-#include <format.h>
-
-#ifdef HAVE_READLINE
-#include <readline/readline.h>
-#include <readline/history.h>
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-int cmdDiagnostics ()
-{
- Color bold ("bold");
-
- std::cout << "\n"
- << bold.colorize (PACKAGE_STRING)
- << "\n"
- << " " << "Platform: " << osName ()
- << "\n\n";
-
- // Compiler.
- std::cout << bold.colorize ("Compiler")
- << "\n"
-#ifdef __VERSION__
- << " " << "Version: "
- << __VERSION__ << "\n"
-#endif
- << " " << "Caps:"
-#ifdef __STDC__
- << " +stdc"
-#endif
-#ifdef __STDC_HOSTED__
- << " +stdc_hosted"
-#endif
-#ifdef __STDC_VERSION__
- << " +" << __STDC_VERSION__
-#endif
-#ifdef _POSIX_VERSION
- << " +" << _POSIX_VERSION
-#endif
-#ifdef _POSIX2_C_VERSION
- << " +" << _POSIX2_C_VERSION
-#endif
-#ifdef _ILP32
- << " +ILP32"
-#endif
-#ifdef _LP64
- << " +LP64"
-#endif
- << " +c" << 8 * sizeof (char)
- << " +i" << 8 * sizeof (int)
- << " +l" << 8 * sizeof (long)
- << " +vp" << 8 * sizeof (void*)
- << " +time_t" << 8 * sizeof (time_t)
- << "\n";
-
- // Compiler compliance level.
- std::cout << " Compliance: "
- << cppCompliance ()
- << "\n\n";
-
- std::cout << bold.colorize ("Build Features")
- << "\n";
-
- std::cout << "libreadline: "
-#ifdef HAVE_READLINE
-#ifdef RL_VERSION_MAJOR
- << RL_VERSION_MAJOR << "." << RL_VERSION_MINOR
-#elif defined RL_READLINE_VERSION
- << "0x" << std::hex << RL_READLINE_VERSION
-#endif
-#else
- << "n/a"
-#endif
- << "\n";
-
- std::cout << " Build type: "
-#ifdef CMAKE_BUILD_TYPE
- << CMAKE_BUILD_TYPE
-#else
- << "-"
-#endif
- << "\n\n";
-
- std::cout << bold.colorize ("Configuration")
- << "\n";
-
- auto env = getenv ("TASKRC");
- std::cout << " TASKRC: "
- << (env ? env : "")
- << "\n";
-
- env = getenv ("TASKDATA");
- std::cout << " TASKDATA: "
- << (env ? env : "")
- << "\n";
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez, 2020 Armaan Bhojwani.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <iostream>
-
-////////////////////////////////////////////////////////////////////////////////
-int cmdHelp ()
-{
- std::cout << '\n'
- << "A generic shell";
- return 0;
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-The development of libshared was made possible by the significant contributions
-of the following people:
-
- Paul Beckingham (Principal Author)
- Federico Hernandez (Principal Author)
-
-The following submitted code, packages or analysis, and deserve special thanks:
-
- Lynoure Braakman
- Jörg Krause
- Ben Boeckel
- Iain R. Learmonth
- Toyam Cox
-
-Thanks to the following, who submitted detailed bug reports and excellent
-suggestions:
-
- Sunil Joshi
- Ellington Santos
- Yury Vidineev
- hosaka
+++ /dev/null
-cmake_minimum_required (VERSION 2.8)
-set (CMAKE_LEGACY_CYGWIN_WIN32 0) # Remove when CMake >= 2.8.4 is required
-set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
-set (HAVE_CMAKE true)
-
-project (shared)
-include (CXXSniffer)
-
-set (PROJECT_VERSION "1.0.0")
-
-set (PACKAGE "${PROJECT_NAME}")
-set (VERSION "${PROJECT_VERSION}")
-set (PACKAGE_BUGREPORT "support@taskwarrior.org")
-set (PACKAGE_NAME "${PACKAGE}")
-set (PACKAGE_TARNAME "${PACKAGE}")
-set (PACKAGE_VERSION "${VERSION}")
-set (PACKAGE_STRING "${PACKAGE} ${VERSION}")
-
-message ("-- Configuring cmake.h")
-configure_file (
- ${CMAKE_SOURCE_DIR}/cmake.h.in
- ${CMAKE_SOURCE_DIR}/cmake.h)
-
-add_subdirectory (src)
-if (EXISTS ${CMAKE_SOURCE_DIR}/test)
- add_subdirectory (test EXCLUDE_FROM_ALL)
-endif (EXISTS ${CMAKE_SOURCE_DIR}/test)
-
+++ /dev/null
-libshared
-
-Copyright 2015 - 2017, Paul Beckingham, Federico Hernandez.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-http://www.opensource.org/licenses/mit-license.php
+++ /dev/null
-/* cmake.h.in. Creates cmake.h during a cmake run */
-
-/* Package information */
-#define PACKAGE "${PACKAGE}"
-#define VERSION "${VERSION}"
-#define PACKAGE_BUGREPORT "${PACKAGE_BUGREPORT}"
-#define PACKAGE_NAME "${PACKAGE_NAME}"
-#define PACKAGE_TARNAME "${PACKAGE_TARNAME}"
-#define PACKAGE_VERSION "${PACKAGE_VERSION}"
-#define PACKAGE_STRING "${PACKAGE_STRING}"
-
-#define CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}"
-
-/* Compiling platform */
-#cmakedefine LINUX
-#cmakedefine DARWIN
-#cmakedefine CYGWIN
-#cmakedefine FREEBSD
-#cmakedefine OPENBSD
-#cmakedefine NETBSD
-#cmakedefine SOLARIS
-#cmakedefine KFREEBSD
-#cmakedefine GNUHURD
-#cmakedefine UNKNOWN
-
-/* Found tm.tm_gmtoff struct member */
-#cmakedefine HAVE_TM_GMTOFF
-
-/* Found st.st_birthtime struct member */
-#cmakedefine HAVE_ST_BIRTHTIME
-
-/* Functions */
-#cmakedefine HAVE_GET_CURRENT_DIR_NAME
-#cmakedefine HAVE_TIMEGM
-#cmakedefine HAVE_UUID_UNPARSE_LOWER
-
+++ /dev/null
-message ("-- Configuring C++11")
-message ("-- System: ${CMAKE_SYSTEM_NAME}")
-
-include (CheckCXXCompilerFlag)
-
-# NOTE: Phase out -std=gnu++0x and --std=c++0x as soon as realistically possible.
-CHECK_CXX_COMPILER_FLAG("-std=c++11" _HAS_CXX11)
-CHECK_CXX_COMPILER_FLAG("-std=c++0x" _HAS_CXX0X)
-CHECK_CXX_COMPILER_FLAG("-std=gnu++0x" _HAS_GNU0X)
-
-if (_HAS_CXX11)
- set (_CXX11_FLAGS "-std=c++11")
-elseif (_HAS_CXX0X)
- message (WARNING "Enabling -std=c++0x draft compile flag. Your compiler does not support the standard '-std=c++11' option. Consider upgrading.")
- set (_CXX11_FLAGS "-std=c++0x")
-elseif (_HAS_GNU0X)
- message (WARNING "Enabling -std=gnu++0x draft compile flag. Your compiler does not support the standard '-std=c++11' option. Consider upgrading.")
- set (_CXX11_FLAGS "-std=gnu++0x")
-else (_HAS_CXX11)
- message (FATAL_ERROR "C++11 support missing. Try upgrading your C++ compiler.")
-endif (_HAS_CXX11)
-
-if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
- set (LINUX true)
-elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
- set (DARWIN true)
- set (_CXX11_FLAGS "${_CXX11_FLAGS} -stdlib=libc++")
-elseif (${CMAKE_SYSTEM_NAME} MATCHES "kFreeBSD")
- set (KFREEBSD true)
-elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
- set (FREEBSD true)
-elseif (${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
- set (OPENBSD true)
-elseif (${CMAKE_SYSTEM_NAME} MATCHES "NetBSD")
- set (NETBSD true)
-elseif (${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
- set (SOLARIS true)
-elseif (${CMAKE_SYSTEM_NAME} STREQUAL "GNU")
- set (GNUHURD true)
-elseif (${CMAKE_SYSTEM_NAME} STREQUAL "CYGWIN")
- set (CYGWIN true)
- # NOTE: Not setting -std=gnu++0x leads to compile errors even with
- # GCC 4.8.3, and debugging those leads to insanity. Adding this
- # workaround instead of fixing Cygwin.
- set (_CXX11_FLAGS "-std=gnu++0x")
-else (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
- set (UNKNOWN true)
-endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
-
-set (CMAKE_CXX_FLAGS "${_CXX11_FLAGS} ${CMAKE_CXX_FLAGS}")
-set (CMAKE_CXX_FLAGS "-Wall -Wextra -Wsign-compare -Wreturn-type ${CMAKE_CXX_FLAGS}")
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2012 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <Args.h>
-#include <shared.h>
-#include <sstream>
-#include <string.h>
-
-////////////////////////////////////////////////////////////////////////////////
-void Args::addOption (const std::string& name, bool defaultValue)
-{
- _options[name] = defaultValue;
- _optionCount[name] = 0;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Args::addNamed (const std::string& name, const std::string& defaultValue)
-{
- _named[name] = defaultValue;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Args::limitPositionals (int limit)
-{
- _limit = limit;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Args::enableNegatives ()
-{
- _negatives = true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Args::scan (int argc, const char** argv)
-{
- for (int i = 1; i < argc; ++i)
- {
- // Is an option or named arg.
- if (argv[i][0] == '-' && strlen (argv[i]) > 1)
- {
- auto name = ltrim (argv[i], "-");
-
- std::string canonical;
- if (canonicalizeOption (name, canonical))
- {
- bool negated = _negatives && name.find ("no") == 0;
- _options[canonical] = ! negated;
- _optionCount[canonical]++;
- }
-
- else if (canonicalizeNamed (name, canonical))
- {
- if (i >= argc)
- throw std::string ("Argument '" + canonical + "' has no value.");
-
- ++i;
- _named[canonical] = argv[i];
- }
-
- else
- throw std::string ("Unrecognized argument '" + name + "'.");
- }
-
- // Or a positional.
- else
- {
- _positionals.push_back (argv[i]);
- if (_limit != -1 &&
- static_cast <int> (_positionals.size ()) > _limit)
- throw std::string ("Too many positional arguments.");
- }
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Args::getOption (const std::string& name) const
-{
- if (_options.find (name) == _options.end ())
- return false;
-
- return _options.at (name);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Args::getOptionCount (const std::string& name) const
-{
- if (_optionCount.find (name) == _optionCount.end ())
- return false;
-
- return _optionCount.at (name);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Args::getNamed (const std::string& name) const
-{
- if (_named.find (name) == _named.end ())
- return "";
-
- return _named.at (name);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Args::getPositionalCount () const
-{
- return static_cast <int> (_positionals.size ());
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Args::getPositional (int n) const
-{
- return _positionals.at (n);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Assuming "abc" is a declared option, support the following canonicalization:
-//
-// abc --> abc (exact match always canonicalizes)
-// ab --> abc (if unique)
-// a --> abc (if unique)
-// noabc --> abc (exact negation match always canonicalizes)
-// noab --> abc (if unique)
-// noa --> abc (if unique)
-//
-bool Args::canonicalizeOption (const std::string& partial, std::string& canonical) const
-{
- bool negated = _negatives && partial.find ("no") == 0;
-
- // Look for exact positive or negative matches first, which should succeed
- // regardless of any longer partial matches.
- if (_options.find (partial) != _options.end ())
- {
- canonical = partial;
- return true;
- }
-
- if (negated &&
- _options.find (partial.substr (2)) != _options.end ())
- {
- canonical = partial.substr (2);
- return true;
- }
-
- // Iterate over all options, and look for partial matches. If there is only
- // one, we have canonicalization.
- std::vector <std::string> candidates;
- for (const auto& option : _options)
- {
- if (option.first.find (partial) == 0 ||
- (negated && option.first.find (partial, 2) == 2))
- {
- candidates.push_back (option.first);
- }
- }
-
- if (candidates.size () == 1)
- {
- canonical = candidates[0];
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Assuming "abc" is a declared name, support the following canonicalization:
-//
-// abc --> abc (exact match always canonicalizes)
-// ab --> abc (if unique)
-// a --> abc (if unique)
-//
-bool Args::canonicalizeNamed (const std::string& partial, std::string& canonical) const
-{
- // Look for exact positive or negative matches first, which should succeed
- // regardless of longer partial matches.
- if (_named.find (partial) != _named.end ())
- {
- canonical = partial;
- return true;
- }
-
- // Iterate over all options, and look for partial matches. If there is only
- // one, we have canonicalization.
- std::vector <std::string> candidates;
- for (const auto& name : _named)
- if (name.first.find (partial) == 0)
- candidates.push_back (name.first);
-
- if (candidates.size () == 1)
- {
- canonical = candidates[0];
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Args::dump () const
-{
- std::stringstream out;
- out << "Args\n"
- << " Options\n";
- for (const auto& arg : _options)
- out << " " << arg.first << " = " << arg.second << " (" << _optionCount.at (arg.first) << ")\n";
-
- out << " Named\n";
- for (const auto& arg : _named)
- out << " " << arg.first << " = " << arg.second << '\n';
-
- out << " Positionals\n"
- << " limit = " << _limit << '\n';
- for (const auto& arg : _positionals)
- out << " " << arg << '\n';
-
- out << " Negatives\n"
- << " enabled = " << _negatives << '\n';
-
- return out.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2012 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_ARGS
-#define INCLUDED_ARGS
-
-#include <string>
-#include <vector>
-#include <map>
-
-class Args
-{
-public:
- Args () = default;
-
- void addOption (const std::string&, bool defaultValue = true);
- void addNamed (const std::string&, const std::string& defaultValue = "");
- void limitPositionals (int);
- void enableNegatives ();
-
- void scan (int, const char**);
-
- bool getOption (const std::string&) const;
- int getOptionCount (const std::string&) const;
- std::string getNamed (const std::string&) const;
- int getPositionalCount () const;
- std::string getPositional (int) const;
-
- std::string dump () const;
-
-private:
- bool canonicalizeOption (const std::string&, std::string&) const;
- bool canonicalizeNamed (const std::string&, std::string&) const;
-
-private:
- std::map <std::string, bool> _options {};
- std::map <std::string, int> _optionCount {};
- std::map <std::string, std::string> _named {};
- std::vector <std::string> _positionals {};
- int _limit {-1};
- bool _negatives {false};
-};
-
-#endif
-
+++ /dev/null
-cmake_minimum_required (VERSION 2.8)
-include_directories (${CMAKE_SOURCE_DIR}
- ${CMAKE_SOURCE_DIR}/src)
-
-set (shared_HEADERS Args.h
- Color.h
- Composite.h
- Configuration.h
- Datetime.h
- Duration.h
- FS.h
- JSON.h
- Lexer.h
- Log.h
- Msg.h
- Packrat.h
- Palette.h
- PEG.h
- Pig.h
- RX.h
- Table.h
- Timer.h
- Tree.h
- shared.h
- format.h
- unicode.h
- utf8.h)
-
-set (shared_SRCS Args.cpp
- Color.cpp
- Composite.cpp
- Configuration.cpp
- Datetime.cpp
- Duration.cpp
- FS.cpp
- JSON.cpp
- Lexer.cpp
- Log.cpp
- Msg.cpp
- Packrat.cpp
- Palette.cpp
- PEG.cpp
- Pig.cpp
- RX.cpp
- SAX.cpp
- Table.cpp
- Timer.cpp
- Tree.cpp
- format.cpp
- ip.cpp
- shared.cpp
- unicode.cpp
- utf8.cpp
- wcwidth6.cpp
- ${shared_HEADERS})
-
-add_library (shared STATIC ${shared_SRCS})
-
-set (CMAKE_INSTALL_LIBDIR lib CACHE PATH "Output directory for libraries")
-install (TARGETS shared DESTINATION lib)
-install (FILES ${shared_HEADERS} DESTINATION include)
-
-add_executable (lex_executable lex.cpp)
-target_link_libraries (lex_executable shared)
-set_property (TARGET lex_executable PROPERTY OUTPUT_NAME "lex")
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <Color.h>
-#include <sstream>
-#include <vector>
-#include <cstdlib>
-#include <shared.h>
-#include <format.h>
-
-// uint to string lookup table for Color::_colorize()
-// _colorize() gets called _a lot_, having this lookup table is a cheap
-// performance optimization.
-const char *colorstring[] = {
- "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
- "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
- "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
- "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
- "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
- "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
- "60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
- "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
- "80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
- "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
- "100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
- "110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
- "120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
- "130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
- "140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
- "150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
- "160", "161", "162", "163", "164", "165", "166", "167", "168", "169",
- "170", "171", "172", "173", "174", "175", "176", "177", "178", "179",
- "180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
- "190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
- "200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
- "210", "211", "212", "213", "214", "215", "216", "217", "218", "219",
- "220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
- "230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
- "240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
- "250", "251", "252", "253", "254", "255"
-};
-
-////////////////////////////////////////////////////////////////////////////////
-static struct
-{
- Color::color_id id;
- std::string english_name;
- int index; // offset red=3 (therefore fg=33, bg=43)
-} allColors[] =
-{
- // Color.h enum English Index
- { Color::nocolor, "none", 0},
- { Color::black, "black", 1}, // fg 29+0 bg 39+0
- { Color::red, "red", 2},
- { Color::green, "green", 3},
- { Color::yellow, "yellow", 4},
- { Color::blue, "blue", 5},
- { Color::magenta, "magenta", 6},
- { Color::cyan, "cyan", 7},
- { Color::white, "white", 8},
-
-};
-
-#define NUM_COLORS (sizeof (allColors) / sizeof (allColors[0]))
-
-////////////////////////////////////////////////////////////////////////////////
-Color::Color ()
-: _value (0)
-{
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Color::Color (const Color& other)
-{
- _value = other._value;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Color::Color (unsigned int c)
-: _value (0)
-{
- if (!(c & _COLOR_HASFG)) _value &= ~_COLOR_FG;
- if (!(c & _COLOR_HASBG)) _value &= ~_COLOR_BG;
-
- _value = c & (_COLOR_256 | _COLOR_HASBG | _COLOR_HASFG |_COLOR_UNDERLINE |
- _COLOR_INVERSE | _COLOR_BOLD | _COLOR_BRIGHT | _COLOR_BG |
- _COLOR_FG);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Supports the following constructs:
-// [bright] [color] [on color] [bright] [underline]
-//
-// Where [color] is one of:
-// black
-// red
-// ...
-// grayN 0 <= N <= 23 fg 38;5;232 + N bg 48;5;232 + N
-// greyN 0 <= N <= 23 fg 38;5;232 + N bg 48;5;232 + N
-// colorN 0 <= N <= 255 fg 38;5;N bg 48;5;N
-// rgbRGB 0 <= R,G,B <= 5 fg 38;5;16 + R*36 + G*6 + B bg 48;5;16 + R*36 + G*6 + B
-Color::Color (const std::string& spec)
-: _value (0)
-{
- // Split spec into words.
- auto words = split (spec, ' ');
-
- // Construct the color as two separate colors, then blend them later. This
- // make it possible to declare a color such as "color1 on black", and have
- // the upgrade work properly.
- unsigned int fg_value = 0;
- unsigned int bg_value = 0;
-
- bool bg = false;
- int index;
- for (auto& word : words)
- {
- word = lowerCase (trim (word));
-
- if (word == "bold") fg_value |= _COLOR_BOLD;
- else if (word == "bright") bg_value |= _COLOR_BRIGHT;
- else if (word == "underline") fg_value |= _COLOR_UNDERLINE;
- else if (word == "inverse") fg_value |= _COLOR_INVERSE;
- else if (word == "on") bg = true;
-
- // X where X is one of black, red, blue ...
- else if ((index = find (word)) != -1)
- {
- if (index)
- {
- if (bg)
- {
- bg_value |= _COLOR_HASBG;
- bg_value |= index << 8;
- }
- else
- {
- fg_value |= _COLOR_HASFG;
- fg_value |= index;
- }
- }
- }
-
- // greyN/grayN, where 0 <= N <= 23.
- else if (! word.compare (0, 4, "grey", 4) ||
- ! word.compare (0, 4, "gray", 4))
- {
- index = strtol (word.substr (4).c_str (), nullptr, 10);
- if (index < 0 || index > 23)
- throw format ("The color '{1}' is not recognized.", word);
-
- if (bg)
- {
- bg_value |= _COLOR_HASBG;
- bg_value |= (index + 232) << 8;
- bg_value |= _COLOR_256;
- }
- else
- {
- fg_value |= _COLOR_HASFG;
- fg_value |= index + 232;
- fg_value |= _COLOR_256;
- }
- }
-
- // rgbRGB, where 0 <= R,G,B <= 5.
- else if (! word.compare (0, 3, "rgb", 3))
- {
- index = strtol (word.substr (3).c_str (), nullptr, 10);
- if (word.length () != 6 ||
- index < 0 || index > 555)
- throw format ("The color '{1}' is not recognized.", word);
-
- int r = strtol (word.substr (3, 1).c_str (), nullptr, 10);
- int g = strtol (word.substr (4, 1).c_str (), nullptr, 10);
- int b = strtol (word.substr (5, 1).c_str (), nullptr, 10);
- if (r < 0 || r > 5 ||
- g < 0 || g > 5 ||
- b < 0 || b > 5)
- throw format ("The color '{1}' is not recognized.", word);
-
- index = 16 + r*36 + g*6 + b;
-
- if (bg)
- {
- bg_value |= _COLOR_HASBG;
- bg_value |= index << 8;
- bg_value |= _COLOR_256;
- }
- else
- {
- fg_value |= _COLOR_HASFG;
- fg_value |= index;
- fg_value |= _COLOR_256;
- }
- }
-
- // colorN, where 0 <= N <= 255.
- else if (! word.compare (0, 5, "color", 5))
- {
- index = strtol (word.substr (5).c_str (), nullptr, 10);
- if (index < 0 || index > 255)
- throw format ("The color '{1}' is not recognized.", word);
-
- upgrade ();
-
- if (bg)
- {
- bg_value |= _COLOR_HASBG;
- bg_value |= index << 8;
- bg_value |= _COLOR_256;
- }
- else
- {
- fg_value |= _COLOR_HASFG;
- fg_value |= index;
- fg_value |= _COLOR_256;
- }
- }
- else if (word != "")
- throw format ("The color '{1}' is not recognized.", word);
- }
-
- // Now combine the fg and bg into a single color.
- _value = fg_value;
- blend (Color (bg_value));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Color::Color (color_id fg)
-: _value (0)
-{
- if (fg != Color::nocolor)
- {
- _value |= _COLOR_HASFG;
- _value |= fg;
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Color::Color (color_id fg, color_id bg, bool underline, bool bold, bool bright)
-: _value (0)
-{
- _value |= ((underline ? 1 : 0) << 18)
- | ((bold ? 1 : 0) << 17)
- | ((bright ? 1 : 0) << 16);
-
- if (bg != Color::nocolor)
- {
- _value |= _COLOR_HASBG;
- _value |= (bg << 8);
- }
-
- if (fg != Color::nocolor)
- {
- _value |= _COLOR_HASFG;
- _value |= fg;
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Color::operator std::string () const
-{
- std::string description;
- if (_value & _COLOR_BOLD) description += "bold";
-
- if (_value & _COLOR_UNDERLINE)
- description += std::string (description.length () ? " " : "") + "underline";
-
- if (_value & _COLOR_INVERSE)
- description += std::string (description.length () ? " " : "") + "inverse";
-
- if (_value & _COLOR_HASFG)
- description += std::string (description.length () ? " " : "") + fg ();
-
- if (_value & _COLOR_HASBG)
- {
- description += std::string (description.length () ? " " : "") + "on";
-
- if (_value & _COLOR_BRIGHT)
- description += std::string (description.length () ? " " : "") + "bright";
-
- description += " " + bg ();
- }
-
- return description;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Color::operator int () const
-{
- return (int) _value;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// If 'other' has styles that are compatible, merge them into this. Colors in
-// other take precedence.
-void Color::blend (const Color& other)
-{
- if (!other.nontrivial ())
- return;
-
- Color c (other);
- _value |= (c._value & _COLOR_UNDERLINE); // Always inherit underline.
- _value |= (c._value & _COLOR_INVERSE); // Always inherit inverse.
-
- // 16 <-- 16.
- if (!(_value & _COLOR_256) &&
- !(c._value & _COLOR_256))
- {
- _value |= (c._value & _COLOR_BOLD); // Inherit bold.
- _value |= (c._value & _COLOR_BRIGHT); // Inherit bright.
-
- if (c._value & _COLOR_HASFG)
- {
- _value |= _COLOR_HASFG; // There is now a color.
- _value &= ~_COLOR_FG; // Remove previous color.
- _value |= (c._value & _COLOR_FG); // Apply other color.
- }
-
- if (c._value & _COLOR_HASBG)
- {
- _value |= _COLOR_HASBG; // There is now a color.
- _value &= ~_COLOR_BG; // Remove previous color.
- _value |= (c._value & _COLOR_BG); // Apply other color.
- }
-
- return;
- }
- else
- {
- // Upgrade either color, if necessary.
- if (!(_value & _COLOR_256)) upgrade ();
- if (!(c._value & _COLOR_256)) c.upgrade ();
-
- // 256 <-- 256.
- if (c._value & _COLOR_HASFG)
- {
- _value |= _COLOR_HASFG; // There is now a color.
- _value &= ~_COLOR_FG; // Remove previous color.
- _value |= (c._value & _COLOR_FG); // Apply other color.
- }
-
- if (c._value & _COLOR_HASBG)
- {
- _value |= _COLOR_HASBG; // There is now a color.
- _value &= ~_COLOR_BG; // Remove previous color.
- _value |= (c._value & _COLOR_BG); // Apply other color.
- }
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Color::upgrade ()
-{
- if (!(_value & _COLOR_256))
- {
- if (_value & _COLOR_HASFG)
- {
- bool bold = _value & _COLOR_BOLD;
- unsigned int fg = _value & _COLOR_FG;
- _value &= ~_COLOR_FG;
- _value &= ~_COLOR_BOLD;
- _value |= (bold ? fg + 7 : fg - 1);
- }
-
- if (_value & _COLOR_HASBG)
- {
- bool bright = _value & _COLOR_BRIGHT;
- unsigned int bg = (_value & _COLOR_BG) >> 8;
- _value &= ~_COLOR_BG;
- _value &= ~_COLOR_BRIGHT;
- _value |= (bright ? bg + 7 : bg - 1) << 8;
- }
-
- _value |= _COLOR_256;
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Color::colorize (const std::string& input) const
-{
- std::string result;
- _colorize (result, input);
- return result;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Sample color codes:
-// red \033[31m
-// bold red \033[91m
-// underline red \033[4;31m
-// bold underline red \033[1;4;31m
-//
-// on red \033[41m
-// on bright red \033[101m
-//
-// 256 fg \033[38;5;Nm
-// 256 bg \033[48;5;Nm
-void Color::_colorize (std::string &result, const std::string& input) const
-{
- if (!nontrivial ())
- {
- result += input;
- return;
- }
-
- int count = 0;
-
- // 256 color
- if (_value & _COLOR_256)
- {
- if (_value & _COLOR_UNDERLINE)
- result += "\033[4m";
-
- if (_value & _COLOR_INVERSE)
- result += "\033[7m";
-
- if (_value & _COLOR_HASFG)
- {
- result += "\033[38;5;";
- result += colorstring[(_value & _COLOR_FG)];
- result += 'm';
- }
-
- if (_value & _COLOR_HASBG)
- {
- result += "\033[48;5;";
- result += colorstring[((_value & _COLOR_BG) >> 8)];
- result += 'm';
- }
-
- result += input;
- result += "\033[0m";
- }
-
- // 16 color
- else
- {
- result += "\033[";
-
- if (_value & _COLOR_BOLD)
- {
- if (count++) result += ';';
- result += '1';
- }
-
- if (_value & _COLOR_UNDERLINE)
- {
- if (count++) result += ';';
- result += '4';
- }
-
- if (_value & _COLOR_INVERSE)
- {
- if (count++) result += ';';
- result += '7';
- }
-
- if (_value & _COLOR_HASFG)
- {
- if (count++) result += ';';
- result += colorstring[(29 + (_value & _COLOR_FG))];
- }
-
- if (_value & _COLOR_HASBG)
- {
- if (count++) result += ';';
- result += colorstring[((_value & _COLOR_BRIGHT ? 99 : 39) + ((_value & _COLOR_BG) >> 8))];
- }
-
- result += 'm';
- result += input;
- result += "\033[0m";
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Remove color codes from a string.
-std::string Color::strip (const std::string& input)
-{
- int length = input.length ();
- bool inside = false;
- std::string output;
- for (int i = 0; i < length; ++i)
- {
- if (inside)
- {
- if (input[i] == 'm')
- inside = false;
- }
- else
- {
- if (input[i] == 033)
- inside = true;
- else
- output += input[i];
- }
- }
-
- return output;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Color::colorize (const std::string& input, const std::string& spec)
-{
- Color c (spec);
- return c.colorize (input);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Color::code () const
-{
- if (! nontrivial ())
- return "";
-
- std::string result;
-
- // 256 color
- if (_value & _COLOR_256)
- {
- if (_value & _COLOR_UNDERLINE)
- result += "\033[4m";
-
- if (_value & _COLOR_INVERSE)
- result += "\033[7m";
-
- if (_value & _COLOR_HASFG)
- {
- result += "\033[38;5;";
- result += colorstring[(_value & _COLOR_FG)];
- result += 'm';
- }
-
- if (_value & _COLOR_HASBG)
- {
- result += "\033[48;5;";
- result += colorstring[((_value & _COLOR_BG) >> 8)];
- result += 'm';
- }
- }
-
- // 16 color
- else
- {
- int count = 0;
- result += "\033[";
-
- if (_value & _COLOR_BOLD)
- {
- if (count++) result += ';';
- result += '1';
- }
-
- if (_value & _COLOR_UNDERLINE)
- {
- if (count++) result += ';';
- result += '4';
- }
-
- if (_value & _COLOR_INVERSE)
- {
- if (count++) result += ';';
- result += '7';
- }
-
- if (_value & _COLOR_HASFG)
- {
- if (count++) result += ';';
- result += colorstring[(29 + (_value & _COLOR_FG))];
- }
-
- if (_value & _COLOR_HASBG)
- {
- if (count++) result += ';';
- result += colorstring[((_value & _COLOR_BRIGHT ? 99 : 39) + ((_value & _COLOR_BG) >> 8))];
- }
-
- result += 'm';
- }
-
- return result;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Color::end () const
-{
- if (nontrivial ())
- return "\033[0m";
-
- return "";
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Color::nontrivial () const
-{
- return _value != 0 ? true : false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Color::find (const std::string& input)
-{
- for (unsigned int i = 0; i < NUM_COLORS; ++i)
- if (allColors[i].english_name == input)
- return (int) i;
-
- return -1;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Color::fg () const
-{
- int index = _value & _COLOR_FG;
-
- if (_value & _COLOR_256)
- {
- if (_value & _COLOR_HASFG)
- {
- std::stringstream s;
- s << "color" << (_value & _COLOR_FG);
- return s.str ();
- }
- }
- else
- {
- for (unsigned int i = 0; i < NUM_COLORS; ++i)
- if (allColors[i].index == index)
- return allColors[i].english_name;
- }
-
- return "";
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Color::bg () const
-{
- int index = (_value & _COLOR_BG) >> 8;
-
- if (_value & _COLOR_256)
- {
- if (_value & _COLOR_HASBG)
- {
- std::stringstream s;
- s << "color" << ((_value & _COLOR_BG) >> 8);
- return s.str ();
- }
- }
- else
- {
- for (unsigned int i = 0; i < NUM_COLORS; ++i)
- if (allColors[i].index == index)
- return allColors[i].english_name;
- }
-
- return "";
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_COLOR
-#define INCLUDED_COLOR
-
-#include <string>
-
-#define _COLOR_INVERSE 0x00400000 // Inverse attribute.
-#define _COLOR_256 0x00200000 // 256-color mode.
-#define _COLOR_HASBG 0x00100000 // Has background color (all values taken).
-#define _COLOR_HASFG 0x00080000 // Has foreground color (all values taken).
-#define _COLOR_UNDERLINE 0x00040000 // General underline attribute.
-#define _COLOR_BOLD 0x00020000 // 16-color bold attribute.
-#define _COLOR_BRIGHT 0x00010000 // 16-color bright background attribute.
-#define _COLOR_BG 0x0000FF00 // 8-bit background color index.
-#define _COLOR_FG 0x000000FF // 8-bit foreground color index.
-
-class Color
-{
-public:
- enum color_id {nocolor = 0, black, red, green, yellow, blue, magenta, cyan, white};
-
- Color ();
- Color (const Color&);
- Color (unsigned int); // 256 | INVERSE | UNDERLINE | BOLD | BRIGHT | (BG << 8) | FG
- Color (const std::string&); // "red on bright black"
- Color (color_id); // fg.
- Color (color_id, color_id, bool, bool, bool); // fg, bg, underline, bold, bright
- operator std::string () const;
- operator int () const;
-
- void upgrade ();
- void blend (const Color&);
-
- std::string colorize (const std::string&) const;
- static std::string colorize (const std::string&, const std::string&);
- void _colorize (std::string&, const std::string&) const;
- static std::string strip (const std::string&);
-
- std::string code () const;
- std::string end () const;
-
- bool nontrivial () const;
-
-private:
- int find (const std::string&);
- std::string fg () const;
- std::string bg () const;
-
-private:
- unsigned int _value;
-};
-
-#endif
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2015 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <Composite.h>
-#include <utf8.h>
-#include <sstream>
-#include <stack>
-
-////////////////////////////////////////////////////////////////////////////////
-// Initially assume no text, but infinite virtual space.
-//
-// Ållow overlay placement of arbitrary text at any offset, real or virtual, and
-// using a specific color.
-//
-// For example:
-// Composite c;
-// c.add ("aaaaaaaaaa", 2, Color ("...")); // Layer 1
-// c.add ("bbbbb", 5, Color ("...")); // Layer 2
-// c.add ("c", 15, Color ("...")); // Layer 3
-//
-// _layers = { std::make_tuple ("aaaaaaaaaa", 2, Color ("...")),
-// std::make_tuple ("bbbbb", 5, Color ("...")),
-// std::make_tuple ("c", 15, Color ("..."))};
-//
-void Composite::add (
- const std::string& text,
- std::string::size_type offset,
- const Color& color)
-{
- _layers.push_back (std::make_tuple (text, offset, color));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Merge the layers of text and color into one string.
-//
-// For example:
-// Composite c;
-// c.add ("aaaaaaaaaa", 2, Color ("...")); // Layer 1
-// c.add ("bbbbb", 5, Color ("...")); // Layer 2
-// c.add ("c", 15, Color ("...")); // Layer 3
-//
-// _layers = { std::make_tuple ("aaaaaaaaaa", 2, Color ("...")),
-// std::make_tuple ("bbbbb", 5, Color ("...")),
-// std::make_tuple ("c", 15, Color ("..."))};
-//
-// Arrange strings conceptually:
-// 111111
-// 0123456789012345 // Position
-//
-// aaaaaaaaaa // Layer 1
-// bbbbb // Layer 2
-// c // Layer 3
-//
-// Walk all strings left to right, selecting the character and color from the
-// highest numbered layer. Emit color codes only on edge detection.
-//
-std::string Composite::str () const
-{
- // The strings are broken into a vector of int, for UTF8 support.
- std::vector <int> characters;
- std::vector <int> colors;
- for (unsigned int layer = 0; layer < _layers.size (); ++layer)
- {
- auto text = std::get <0> (_layers[layer]);
- auto offset = std::get <1> (_layers[layer]);
- auto len = utf8_text_length (text);
-
- // Make sure the vectors are large enough to support a write operator[].
- if (characters.size () < offset + len)
- {
- characters.resize (offset + len, 32);
- colors.resize (offset + len, 0);
- }
-
- // Copy in the layer characters and color indexes.
- std::string::size_type cursor = 0;
- int character;
- int count = 0;
- while ((character = utf8_next_char (text, cursor)))
- {
- characters[offset + count] = character;
- colors [offset + count] = layer + 1;
- ++count;
- }
- }
-
- // Now walk the character and color vector, emitting every character and
- // every detected color change.
- std::stringstream out;
- int prev_color = 0;
- for (unsigned int i = 0; i < characters.size (); ++i)
- {
- // A change in color triggers a code emit.
- if (prev_color != colors[i])
- {
- if (prev_color)
- out << std::get <2> (_layers[prev_color - 1]).end ();
-
- if (colors[i])
- out << std::get <2> (_layers[colors[i] - 1]).code ();
- else
- out << std::get <2> (_layers[prev_color - 1]).end ();
-
- prev_color = colors[i];
- }
-
- out << utf8_character (characters[i]);
- }
-
- // Terminate the color codes, if necessary.
- if (prev_color)
- out << std::get <2> (_layers[prev_color - 1]).end ();
-
- return out.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// So the same instance can be reused.
-void Composite::clear ()
-{
- _layers.clear ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2015 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_COMPOSITE
-#define INCLUDED_COMPOSITE
-
-#include <Color.h>
-#include <vector>
-#include <string>
-#include <tuple>
-
-class Composite
-{
-public:
- Composite () = default;
- void add (const std::string&, std::string::size_type, const Color&);
- std::string str () const;
- void clear ();
-
-private:
- std::vector <std::tuple <std::string, std::string::size_type, Color>> _layers;
-};
-
-#endif
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <Configuration.h>
-#include <inttypes.h>
-#include <stdlib.h>
-#include <FS.h>
-#include <JSON.h>
-#include <shared.h>
-#include <format.h>
-
-////////////////////////////////////////////////////////////////////////////////
-bool setVariableInFile (
- const std::string& file,
- const std::string& name,
- const std::string& value)
-{
- // Read the file contents.
- std::vector <std::string> contents;
- File::read (file, contents);
-
- bool found = false;
- bool change = false;
-
- for (auto& line : contents)
- {
- // If there is a comment on the line, it must follow the pattern.
- auto comment = line.find ('#');
- auto pos = line.find (name + '=');
-
- if (pos != std::string::npos &&
- (comment == std::string::npos ||
- comment > pos))
- {
- found = true;
- if (comment != std::string::npos)
- line = name + '=' + value + ' ' + line.substr (comment);
- else
- line = name + '=' + value;
-
- change = true;
- }
- }
-
- // Not found, so append instead.
- if (! found)
- {
- contents.push_back (name + '=' + value);
- change = true;
- }
-
- if (change)
- File::write (file, contents);
-
- return change;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool unsetVariableInFile (
- const std::string& file,
- const std::string& name)
-{
- // Read configuration file.
- std::vector <std::string> contents;
- File::read (file, contents);
-
- bool change = false;
-
- for (auto line = contents.begin (); line != contents.end (); )
- {
- bool lineDeleted = false;
-
- // If there is a comment on the line, it must follow the pattern.
- auto comment = line->find ('#');
- auto pos = line->find (name + '=');
-
- if (pos != std::string::npos &&
- (comment == std::string::npos ||
- comment > pos))
- {
- // vector::erase method returns a valid iterator to the next object
- line = contents.erase (line);
- lineDeleted = true;
- change = true;
- }
-
- if (! lineDeleted)
- line++;
- }
-
- if (change)
- File::write (file, contents);
-
- return change;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Read the Configuration file and populate the *this map. The file format is
-// simply lines with name=value pairs. Whitespace between name, = and value is
-// not tolerated, but blank lines and comments starting with # are allowed.
-//
-// Nested files are now supported, with the following construct:
-// include /absolute/path/to/file
-//
-void Configuration::load (const std::string& file, int nest /* = 1 */)
-{
- if (nest > 10)
- throw std::string ("Configuration files may only be nested to 10 levels.");
-
- // Read the file, then parse the contents.
- File config (file);
-
- if (nest == 1)
- _original_file = config;
-
- if (config.exists () &&
- config.readable ())
- {
- std::string contents;
- if (File::read (file, contents) && contents.length ())
- parse (contents, nest);
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Write the Configuration file.
-void Configuration::save ()
-{
- std::string contents;
- for (const auto& i : *this)
- contents += i.first + "=" + i.second + '\n';
-
- File::write (_original_file, contents);
- _dirty = false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Configuration::parse (const std::string& input, int nest /* = 1 */)
-{
- // Shortcut case for default constructor.
- if (input.length () == 0)
- return;
-
- // Parse each line.
- for (auto& line : split (input, '\n'))
- {
- // Remove comments.
- auto pound = line.find ('#');
- if (pound != std::string::npos)
- line = line.substr (0, pound);
-
- // Skip empty lines.
- line = trim (line);
- if (line.length () > 0)
- {
- auto equal = line.find ('=');
- if (equal != std::string::npos)
- {
- std::string key = trim (line.substr (0, equal));
- std::string value = trim (line.substr (equal+1, line.length () - equal));
-
- (*this)[key] = json::decode (value);
- }
- else
- {
- auto include = line.find ("include");
- if (include != std::string::npos)
- {
- Path included (trim (line.substr (include + 7)));
- if (included.is_absolute ())
- {
- if (included.readable ())
- load (included, nest + 1);
- else
- throw format ("Could not read include file '{1}'.", included._data);
- }
- else
- throw format ("Can only include files with absolute paths, not '{1}'", included._data);
- }
- else
- throw format ("Malformed entry '{1}' in config file.", line);
- }
- }
- }
-
- _dirty = true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Configuration::has (const std::string& key) const
-{
- return (*this).find (key) != (*this).end ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Return the configuration value given the specified key.
-std::string Configuration::get (const std::string& key) const
-{
- auto found = find (key);
- if (found != end ())
- return found->second;
-
- return "";
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Configuration::getInteger (const std::string& key) const
-{
- auto found = find (key);
- if (found != end ())
- return strtoimax (found->second.c_str (), nullptr, 10);
-
- return 0;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-double Configuration::getReal (const std::string& key) const
-{
- auto found = find (key);
- if (found != end ())
- return strtod (found->second.c_str (), nullptr);
-
- return 0.0;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Configuration::getBoolean (const std::string& key) const
-{
- auto found = find (key);
- if (found != end ())
- {
- auto value = lowerCase (found->second);
- if (value == "true" ||
- value == "1" ||
- value == "y" ||
- value == "yes" ||
- value == "on")
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Configuration::set (const std::string& key, const int value)
-{
- (*this)[key] = format (value);
- _dirty = true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Configuration::set (const std::string& key, const double value)
-{
- (*this)[key] = format (value, 1, 8);
- _dirty = true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Configuration::set (const std::string& key, const std::string& value)
-{
- (*this)[key] = value;
- _dirty = true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Autovivification is ok here.
-void Configuration::setIfBlank (const std::string& key, const std::string& value)
-{
- if ((*this)[key] == "")
- {
- (*this)[key] = value;
- _dirty = true;
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Provide a vector of all configuration keys.
-std::vector <std::string> Configuration::all () const
-{
- std::vector <std::string> items;
- for (const auto& it : *this)
- items.push_back (it.first);
-
- return items;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Configuration::file () const
-{
- return _original_file._data;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Configuration::dirty ()
-{
- return _dirty;
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_CONFIGURATION
-#define INCLUDED_CONFIGURATION
-
-#include <map>
-#include <vector>
-#include <string>
-#include <FS.h>
-
-bool setVariableInFile (const std::string&, const std::string&, const std::string&);
-bool unsetVariableInFile (const std::string&, const std::string&);
-
-class Configuration : public std::map <std::string, std::string>
-{
-public:
- void load (const std::string&, int nest = 1);
- void save ();
- void parse (const std::string&, int nest = 1);
-
- bool has (const std::string&) const;
- std::string get (const std::string&) const;
- int getInteger (const std::string&) const;
- double getReal (const std::string&) const;
- bool getBoolean (const std::string&) const;
-
- void set (const std::string&, const int);
- void set (const std::string&, const double);
- void set (const std::string&, const std::string&);
- void setIfBlank (const std::string&, const std::string&);
- std::vector <std::string> all () const;
-
- std::string file () const;
-
- bool dirty ();
-
-private:
- File _original_file {};
- bool _dirty {false};
-};
-
-#endif
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <Datetime.h>
-#include <algorithm>
-#include <iostream>
-#include <sstream>
-#include <iomanip>
-#include <cassert>
-#include <stdlib.h>
-#include <shared.h>
-#include <format.h>
-#include <unicode.h>
-#include <utf8.h>
-
-static std::vector <std::string> dayNames {
- "sunday",
- "monday",
- "tuesday",
- "wednesday",
- "thursday",
- "friday",
- "saturday"};
-
-static std::vector <std::string> monthNames {
- "january",
- "february",
- "march",
- "april",
- "may",
- "june",
- "july",
- "august",
- "september",
- "october",
- "november",
- "december"};
-
-int Datetime::weekstart = 1; // Monday, per ISO-8601.
-int Datetime::minimumMatchLength = 3;
-bool Datetime::isoEnabled = true;
-bool Datetime::standaloneDateEnabled = true;
-bool Datetime::standaloneTimeEnabled = true;
-
-////////////////////////////////////////////////////////////////////////////////
-Datetime::Datetime ()
-{
- clear ();
- _date = time (nullptr);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Datetime::Datetime (const std::string& input, const std::string& format)
-{
- clear ();
- std::string::size_type start = 0;
- if (! parse (input, start, format))
- throw ::format ("'{1}' is not a valid date in the '{2}' format.", input, format);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Datetime::Datetime (const time_t t)
-{
- clear ();
- _date = t;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Datetime::Datetime (const int y, const int m, const int d)
-{
- // Protect against arguments being passed in the wrong order.
- assert (y >= 1969 && y < 2100);
- assert (m >= 1 && m <= 12);
- assert (d >= 1 && d <= 31);
-
- clear ();
-
- // Error if not valid.
- struct tm t {};
- t.tm_isdst = -1; // Requests that mktime determine summer time effect.
- t.tm_mday = d;
- t.tm_mon = m - 1;
- t.tm_year = y - 1900;
-
- _date = mktime (&t);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Datetime::Datetime (const int y, const int m, const int d,
- const int hr, const int mi, const int se)
-{
- // Protect against arguments being passed in the wrong order.
- assert (y >= 1969 && y < 2100);
- assert (m >= 1 && m <= 12);
- assert (d >= 1 && d <= 31);
- assert (hr >= 0 && hr <= 24);
- assert (mi >= 0 && mi < 60);
- assert (se >= 0 && se < 60);
-
- clear ();
-
- // Error if not valid.
- struct tm t {};
- t.tm_isdst = -1; // Requests that mktime determine summer time effect.
- t.tm_mday = d;
- t.tm_mon = m - 1;
- t.tm_year = y - 1900;
- t.tm_hour = hr;
- t.tm_min = mi;
- t.tm_sec = se;
-
- _date = mktime (&t);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::parse (
- const std::string& input,
- std::string::size_type& start,
- const std::string& format)
-{
- auto i = start;
- Pig pig (input);
- if (i)
- pig.skipN (static_cast <int> (i));
-
- auto checkpoint = pig.cursor ();
-
- // Parse epoch first, as it's the most common scenario.
- if (parse_epoch (pig))
- {
- // ::validate and ::resolve are not needed in this case.
- start = pig.cursor ();
- return true;
- }
-
- if (parse_formatted (pig, format))
- {
- // Check the values and determine time_t.
- if (validate ())
- {
- start = pig.cursor ();
- resolve ();
- return true;
- }
- }
-
- // Allow parse_date_time and parse_date_time_ext regardless of
- // Datetime::isoEnabled setting, because these formats are relied upon by
- // the 'import' command, JSON parser and hook system.
- if (parse_date_time_ext (pig) || // Strictest first.
- parse_date_time (pig) ||
- (Datetime::isoEnabled &&
- ( parse_date_ext (pig) ||
- (Datetime::standaloneDateEnabled && parse_date (pig)) ||
- parse_time_utc_ext (pig) ||
- parse_time_utc (pig) ||
- parse_time_off_ext (pig) ||
- parse_time_off (pig) ||
- parse_time_ext (pig) ||
- (Datetime::standaloneTimeEnabled && parse_time (pig)) // Time last, as it is the most permissive.
- )
- )
- )
- {
- // Check the values and determine time_t.
- if (validate ())
- {
- start = pig.cursor ();
- resolve ();
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
-
- if (parse_named (pig))
- {
- // ::validate and ::resolve are not needed in this case.
- start = pig.cursor ();
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Datetime::clear ()
-{
- _year = 0;
- _month = 0;
- _week = 0;
- _weekday = 0;
- _julian = 0;
- _day = 0;
- _seconds = 0;
- _offset = 0;
- _utc = false;
- _date = 0;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::parse_formatted (Pig& pig, const std::string& format)
-{
- // Short-circuit on missing format.
- if (format == "")
- return false;
-
- auto checkpoint = pig.cursor ();
-
- int month {-1}; // So we can check later.
- int day {-1};
- int year {-1};
- int hour {-1};
- int minute {-1};
- int second {-1};
-
- // For parsing, unused.
- int wday {-1};
- int week {-1};
-
- for (unsigned int f = 0; f < format.length (); ++f)
- {
- switch (format[f])
- {
- case 'm':
- if (pig.getDigit (month))
- {
- if (month == 0)
- pig.getDigit (month);
-
- if (month == 1)
- if (pig.getDigit (month))
- month += 10;
- }
- else
- {
- pig.restoreTo (checkpoint);
- return false;
- }
- break;
-
- case 'M':
- if (! pig.getDigit2 (month))
- {
- pig.restoreTo (checkpoint);
- return false;
- }
- break;
-
- case 'd':
- if (pig.getDigit (day))
- {
- if (day == 0)
- pig.getDigit (day);
-
- if (day == 1 || day == 2 || day == 3)
- {
- int tens = day;
- if (pig.getDigit (day))
- day += 10 * tens;
- }
- }
- else
- {
- pig.restoreTo (checkpoint);
- return false;
- }
- break;
-
- case 'D':
- if (! pig.getDigit2 (day))
- {
- pig.restoreTo (checkpoint);
- return false;
- }
- break;
-
- case 'y':
- if (! pig.getDigit2 (year))
- {
- pig.restoreTo (checkpoint);
- return false;
- }
- year += 2000;
- break;
-
- case 'Y':
- if (! pig.getDigit4 (year))
- {
- pig.restoreTo (checkpoint);
- return false;
- }
- break;
-
- case 'h':
- if (pig.getDigit (hour))
- {
- if (hour == 0)
- pig.getDigit (hour);
-
- if (hour == 1 || hour == 2)
- {
- int tens = hour;
- if (pig.getDigit (hour))
- hour += 10 * tens;
- }
- }
- else
- {
- pig.restoreTo (checkpoint);
- return false;
- }
- break;
-
- case 'H':
- if (! pig.getDigit2 (hour))
- {
- pig.restoreTo (checkpoint);
- return false;
- }
- break;
-
- case 'n':
- if (pig.getDigit (minute))
- {
- if (minute == 0)
- pig.getDigit (minute);
-
- if (minute < 6)
- {
- int tens = minute;
- if (pig.getDigit (minute))
- minute += 10 * tens;
- }
- }
- else
- {
- pig.restoreTo (checkpoint);
- return false;
- }
- break;
-
- case 'N':
- if (! pig.getDigit2 (minute))
- {
- pig.restoreTo (checkpoint);
- return false;
- }
- break;
-
- case 's':
- if (pig.getDigit (second))
- {
- if (second == 0)
- pig.getDigit (second);
-
- if (second < 6)
- {
- int tens = second;
- if (pig.getDigit (second))
- second += 10 * tens;
- }
- }
- else
- {
- pig.restoreTo (checkpoint);
- return false;
- }
- break;
-
- case 'S':
- if (! pig.getDigit2 (second))
- {
- pig.restoreTo (checkpoint);
- return false;
- }
- break;
-
- case 'v':
- if (pig.getDigit (week))
- {
- if (week == 0)
- pig.getDigit (week);
-
- if (week < 6)
- {
- int tens = week;
- if (pig.getDigit (week))
- week += 10 * tens;
- }
- }
- else
- {
- pig.restoreTo (checkpoint);
- return false;
- }
- break;
-
- case 'V':
- if (! pig.getDigit2 (week))
- {
- pig.restoreTo (checkpoint);
- return false;
- }
- break;
-
- case 'a':
- wday = Datetime::dayOfWeek (pig.str ().substr (0, 3));
- if (wday == -1)
- {
- pig.restoreTo (checkpoint);
- return false;
- }
-
- pig.skipN (3);
- break;
-
- case 'A':
- {
- std::string dayName;
- if (pig.getUntil (format[f + 1], dayName))
- {
- wday = Datetime::dayOfWeek (dayName);
- if (wday == -1)
- {
- pig.restoreTo (checkpoint);
- return false;
- }
- }
- }
- break;
-
- case 'b':
- month = Datetime::monthOfYear (pig.str ().substr (0, 3));
- if (month == -1)
- {
- pig.restoreTo (checkpoint);
- return false;
- }
-
- pig.skipN (3);
- break;
-
- case 'B':
- {
- std::string monthName;
- if (pig.getUntil (format[f + 1], monthName))
- {
- month = Datetime::monthOfYear (monthName);
- if (month == -1)
- {
- pig.restoreTo (checkpoint);
- return false;
- }
- }
- }
- break;
-
- default:
- if (! pig.skip (format[f]))
- {
- pig.restoreTo (checkpoint);
- return false;
- }
- break;
- }
- }
-
- // It is possible that the format='Y-M-D', and the input is Y-M-DTH:N:SZ, and
- // this should not be considered a match.
- if (! pig.eos () && ! unicodeWhitespace (pig.peek ()))
- {
- pig.restoreTo (checkpoint);
- return false;
- }
-
- // Missing values are filled in from the current date.
- if (year == -1)
- {
- Datetime now;
- year = now.year ();
- if (month == -1)
- {
- month = now.month ();
- if (day == -1)
- {
- day = now.day ();
- if (hour == -1)
- {
- hour = now.hour ();
- if (minute == -1)
- {
- minute = now.minute ();
- if (second == -1)
- second = now.second ();
- }
- }
- }
- }
- }
-
- // Any remaining undefined values are assigned defaults.
- if (month == -1) month = 1;
- if (day == -1) day = 1;
- if (hour == -1) hour = 0;
- if (minute == -1) minute = 0;
- if (second == -1) second = 0;
-
- _year = year;
- _month = month;
- _day = day;
- _seconds = (hour * 3600) + (minute * 60) + second;
-
- return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Note how these are all single words.
-//
-// Examples and descriptions, assuming now == 2017-03-05T12:34:56.
-//
-// Example Notes
-// ------------------- ------------------
-// now 2017-03-05T12:34:56 Unaffected
-// yesterday 2017-03-04T00:00:00 Unaffected
-// today 2017-03-05T00:00:00 Unaffected
-// tomorrow 2017-03-06T00:00:00 Unaffected
-// <ordinal> 12th 2017-03-12T00:00:00
-// <day> monday 2017-03-06T00:00:00
-// <month> april 2017-04-01T00:00:00
-// later 2038-01-18T00:00:00 Unaffected
-// someday 2038-01-18T00:00:00 Unaffected
-// sopd 2017-03-04T00:00:00 Unaffected
-// sod 2017-03-05T00:00:00 Unaffected
-// sond 2017-03-06T00:00:00 Unaffected
-// eopd 2017-03-05T00:00:00 Unaffected
-// eod 2017-03-06T00:00:00 Unaffected
-// eond 2017-03-07T00:00:00 Unaffected
-// sopw 2017-02-26T00:00:00 Unaffected
-// sow 2017-03-05T00:00:00 Unaffected
-// sonw 2017-03-12T00:00:00 Unaffected
-// eopw 2017-03-05T00:00:00 Unaffected
-// eow 2017-03-12T00:00:00 Unaffected
-// eonw 2017-03-19T00:00:00 Unaffected
-// sopww 2017-02-27T00:00:00 Unaffected
-// soww 2017-03-06T00:00:00
-// sonww 2017-03-06T00:00:00 Unaffected
-// eopww 2017-03-03T00:00:00 Unaffected
-// eoww 2017-03-10T00:00:00
-// eonww 2017-03-17T00:00:00 Unaffected
-// sopm 2017-02-01T00:00:00 Unaffected
-// som 2017-03-01T00:00:00 Unaffected
-// sonm 2017-04-01T00:00:00 Unaffected
-// eopm 2017-03-01T00:00:00 Unaffected
-// eom 2017-04-01T00:00:00 Unaffected
-// eonm 2017-05-01T00:00:00 Unaffected
-// sopq 2017-10-01T00:00:00 Unaffected
-// soq 2017-01-01T00:00:00 Unaffected
-// sonq 2017-04-01T00:00:00 Unaffected
-// eopq 2017-01-01T00:00:00 Unaffected
-// eoq 2017-04-01T00:00:00 Unaffected
-// eonq 2017-07-01T00:00:00 Unaffected
-// sopy 2016-01-01T00:00:00 Unaffected
-// soy 2017-01-01T00:00:00 Unaffected
-// sony 2018-01-01T00:00:00 Unaffected
-// eopy 2017-01-01T00:00:00 Unaffected
-// eoy 2018-01-01T00:00:00 Unaffected
-// eony 2019-01-01T00:00:00 Unaffected
-// easter 2017-04-16T00:00:00
-// eastermonday 2017-04-16T00:00:00
-// ascension 2017-05-25T00:00:00
-// pentecost 2017-06-04T00:00:00
-// goodfriday 2017-04-14T00:00:00
-// midsommar 2017-06-24T00:00:00 midnight, 1st Saturday after 20th June
-// midsommarafton 2017-06-23T00:00:00 midnight, 1st Friday after 19th June
-// juhannus 2017-06-23T00:00:00 midnight, 1st Friday after 19th June
-//
-bool Datetime::parse_named (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- // Experimental handling of date phrases, such as "first monday in march".
- // Note that this requires that phrases are deliminted by EOS or WS.
- std::string token;
- std::vector <std::string> tokens;
- while (pig.getUntilWS (token))
- {
- tokens.push_back (token);
- if (! pig.skipWS ())
- break;
- }
-
-/*
- // This grpoup contains "1st monday ..." which must be processed before
- // initializeOrdinal below.
- if (initializeNthDayInMonth (tokens))
- {
- return true;
- }
-*/
-
- // Restoration necessary because of the tokenization.
- pig.restoreTo (checkpoint);
-
- if (initializeNow (pig) ||
- initializeYesterday (pig) ||
- initializeToday (pig) ||
- initializeTomorrow (pig) ||
- initializeOrdinal (pig) ||
- initializeDayName (pig) ||
- initializeMonthName (pig) ||
- initializeLater (pig) ||
- initializeSopd (pig) ||
- initializeSod (pig) ||
- initializeSond (pig) ||
- initializeEopd (pig) ||
- initializeEod (pig) ||
- initializeEond (pig) ||
- initializeSopw (pig) ||
- initializeSow (pig) ||
- initializeSonw (pig) ||
- initializeEopw (pig) ||
- initializeEow (pig) ||
- initializeEonw (pig) ||
- initializeSopww (pig) || // Must appear after sopw
- initializeSonww (pig) || // Must appear after sonw
- initializeSoww (pig) || // Must appear after sow
- initializeEopww (pig) || // Must appear after eopw
- initializeEonww (pig) || // Must appear after eonw
- initializeEoww (pig) || // Must appear after eow
- initializeSopm (pig) ||
- initializeSom (pig) ||
- initializeSonm (pig) ||
- initializeEopm (pig) ||
- initializeEom (pig) ||
- initializeEonm (pig) ||
- initializeSopq (pig) ||
- initializeSoq (pig) ||
- initializeSonq (pig) ||
- initializeEopq (pig) ||
- initializeEoq (pig) ||
- initializeEonq (pig) ||
- initializeSopy (pig) ||
- initializeSoy (pig) ||
- initializeSony (pig) ||
- initializeEopy (pig) ||
- initializeEoy (pig) ||
- initializeEony (pig) ||
- initializeEaster (pig) ||
- initializeMidsommar (pig) ||
- initializeMidsommarafton (pig) ||
- initializeInformalTime (pig))
- {
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Valid epoch values are unsigned integers after 1980-01-01T00:00:00Z. This
-// restriction means that '12' will not be identified as an epoch date.
-bool Datetime::parse_epoch (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- int epoch {};
- if (pig.getDigits (epoch) &&
- ! unicodeLatinAlpha (pig.peek ()) &&
- epoch >= 315532800)
- {
- _date = static_cast <time_t> (epoch);
- //std::cout << "# parse_epoch \e[33msucceed\e[0m " << pig.dump () << "\n";
- return true;
- }
-
- //std::cout << "# parse_epoch fail\n";
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// date_ext 'T' time_utc_ext 'Z'
-// date_ext 'T' time_off_ext
-// date_ext 'T' time_ext
-bool Datetime::parse_date_time_ext (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (parse_date_ext (pig) &&
- pig.skip ('T') &&
- (parse_time_utc_ext (pig) ||
- parse_time_off_ext (pig) ||
- parse_time_ext (pig)))
- {
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// YYYY-MM-DD
-// YYYY-MM
-// YYYY-DDD
-// YYYY-Www-D
-// YYYY-Www
-bool Datetime::parse_date_ext (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- int year {};
- if (parse_year (pig, year) &&
- pig.skip ('-'))
- {
- auto checkpointYear = pig.cursor ();
-
- int month {};
- int day {};
- int julian {};
-
- if (pig.skip ('W') &&
- parse_week (pig, _week))
- {
- if (pig.skip ('-') &&
- pig.getDigit (_weekday))
- {
- // What is happening here - must be something to do?
- }
-
- if (! unicodeLatinDigit (pig.peek ()))
- {
- _year = year;
- return true;
- }
- }
-
- pig.restoreTo (checkpointYear);
-
- if (parse_month (pig, month) &&
- pig.skip ('-') &&
- parse_day (pig, day) &&
- ! unicodeLatinDigit (pig.peek ()))
- {
- _year = year;
- _month = month;
- _day = day;
- return true;
- }
-
- pig.restoreTo (checkpointYear);
-
- if (parse_julian (pig, julian) &&
- ! unicodeLatinDigit (pig.peek ()))
- {
- _year = year;
- _julian = julian;
- return true;
- }
-
- pig.restoreTo (checkpointYear);
-
- if (parse_month (pig, month) &&
- pig.peek () != '-' &&
- ! unicodeLatinDigit (pig.peek ()))
- {
- _year = year;
- _month = month;
- _day = 1;
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// ±hh[:mm]
-bool Datetime::parse_off_ext (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- int sign = pig.peek ();
- if (sign == '+' || sign == '-')
- {
- pig.skipN (1);
-
- int hour {0};
- int minute {0};
-
- if (parse_off_hour (pig, hour))
- {
- if (pig.skip (':'))
- {
- if (! parse_off_minute (pig, minute))
- {
- pig.restoreTo (checkpoint);
- return false;
- }
- }
-
- _offset = (hour * 3600) + (minute * 60);
- if (sign == '-')
- _offset = - _offset;
-
- if (! unicodeLatinDigit (pig.peek ()))
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// hh:mm[:ss]
-bool Datetime::parse_time_ext (Pig& pig, bool terminated)
-{
- auto checkpoint = pig.cursor ();
-
- int hour {};
- int minute {};
- if (parse_hour (pig, hour) &&
- pig.skip (':') &&
- parse_minute (pig, minute))
- {
- if (pig.skip (':'))
- {
- int second {};
- if (parse_second (pig, second) &&
- ! unicodeLatinDigit (pig.peek ()) &&
- (! terminated || (pig.peek () != '-' && pig.peek () != '+')))
- {
- _seconds = (hour * 3600) + (minute * 60) + second;
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
- }
-
- auto following = pig.peek ();
- if (! unicodeLatinDigit (following) &&
- (! terminated || (following != '+' && following != '-')) &&
- following != 'A' &&
- following != 'a' &&
- following != 'P' &&
- following != 'p')
- {
- _seconds = (hour * 3600) + (minute * 60);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// time-ext 'Z'
-bool Datetime::parse_time_utc_ext (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (parse_time_ext (pig, false) &&
- pig.skip ('Z'))
- {
- if (! unicodeLatinDigit (pig.peek ()))
- {
- _utc = true;
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// time-ext off-ext
-bool Datetime::parse_time_off_ext (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (parse_time_ext (pig, false) &&
- parse_off_ext (pig))
- {
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// YYYYMMDDTHHMMSSZ
-// YYYYMMDDTHHMMSS
-bool Datetime::parse_date_time (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (parse_date (pig) &&
- pig.skip ('T') &&
- (parse_time_utc (pig) ||
- parse_time_off (pig) ||
- parse_time (pig)))
- {
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// YYYYWww
-// YYYYDDD
-// YYYYMMDD
-// YYYYMM
-bool Datetime::parse_date (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- int year {};
- int month {};
- int julian {};
- int week {};
- int weekday {};
- int day {};
- if (parse_year (pig, year))
- {
- auto checkpointYear = pig.cursor ();
-
- if (pig.skip ('W') &&
- parse_week (pig, week))
- {
- if (pig.getDigit (weekday))
- _weekday = weekday;
-
- if (! unicodeLatinDigit (pig.peek ()))
- {
- _year = year;
- _week = week;
- return true;
- }
- }
-
- pig.restoreTo (checkpointYear);
-
- if (parse_julian (pig, julian) &&
- ! unicodeLatinDigit (pig.peek ()))
- {
- _year = year;
- _julian = julian;
- return true;
- }
-
- pig.restoreTo (checkpointYear);
-
- if (parse_month (pig, month))
- {
- if (parse_day (pig, day))
- {
- if (! unicodeLatinDigit (pig.peek ()))
- {
- _year = year;
- _month = month;
- _day = day;
- return true;
- }
- }
- else
- {
- if (! unicodeLatinDigit (pig.peek ()))
- {
- _year = year;
- _month = month;
- _day = 1;
- return true;
- }
- }
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// <time> Z
-bool Datetime::parse_time_utc (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (parse_time (pig, false) &&
- pig.skip ('Z'))
- {
- _utc = true;
- if (! unicodeLatinDigit (pig.peek ()))
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// <time> <off>
-bool Datetime::parse_time_off (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (parse_time (pig, false) &&
- parse_off (pig))
- {
- auto terminator = pig.peek ();
- if (terminator != '-' && ! unicodeLatinDigit (terminator))
- {
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// hhmmss
-// hhmm
-bool Datetime::parse_time (Pig& pig, bool terminated)
-{
- auto checkpoint = pig.cursor ();
-
- int hour {};
- int minute {};
- if (parse_hour (pig, hour) &&
- parse_minute (pig, minute))
- {
- int second {};
- parse_second (pig, second);
-
- auto terminator = pig.peek ();
- if (! terminated ||
- (! unicodeLatinDigit (terminator) && terminator != '-' && terminator != '+'))
- {
- _seconds = (hour * 3600) + (minute * 60) + second;
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// ±hhmm
-// ±hh
-bool Datetime::parse_off (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- int sign = pig.peek ();
- if (sign == '+' || sign == '-')
- {
- pig.skipN (1);
-
- int hour {};
- if (parse_off_hour (pig, hour))
- {
- int minute {};
- parse_off_minute (pig, minute);
-
- if (! unicodeLatinDigit (pig.peek ()))
- {
- _offset = (hour * 3600) + (minute * 60);
- if (sign == '-')
- _offset = - _offset;
-
- return true;
- }
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::parse_year (Pig& pig, int& value)
-{
- auto checkpoint = pig.cursor ();
-
- int year;
- if (pig.getDigit4 (year) &&
- year > 1969)
- {
- value = year;
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::parse_month (Pig& pig, int& value)
-{
- auto checkpoint = pig.cursor ();
-
- int month;
- if (pig.getDigit2 (month) &&
- month > 0 &&
- month <= 12)
- {
- value = month;
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::parse_week (Pig& pig, int& value)
-{
- auto checkpoint = pig.cursor ();
-
- int week;
- if (pig.getDigit2 (week) &&
- week > 0 &&
- week <= 53)
- {
- value = week;
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::parse_julian (Pig& pig, int& value)
-{
- auto checkpoint = pig.cursor ();
-
- int julian;
- if (pig.getDigit3 (julian) &&
- julian > 0 &&
- julian <= 366)
- {
- value = julian;
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::parse_day (Pig& pig, int& value)
-{
- auto checkpoint = pig.cursor ();
-
- int day;
- if (pig.getDigit2 (day) &&
- day > 0 &&
- day <= 31)
- {
- value = day;
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::parse_weekday (Pig& pig, int& value)
-{
- auto checkpoint = pig.cursor ();
-
- int weekday;
- if (pig.getDigit (weekday) &&
- weekday >= 1 &&
- weekday <= 7)
- {
- value = weekday;
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::parse_hour (Pig& pig, int& value)
-{
- auto checkpoint = pig.cursor ();
-
- int hour;
- if (pig.getDigit2 (hour) &&
- hour >= 0 &&
- hour < 24)
- {
- value = hour;
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::parse_minute (Pig& pig, int& value)
-{
- auto checkpoint = pig.cursor ();
-
- int minute;
- if (pig.getDigit2 (minute) &&
- minute >= 0 &&
- minute < 60)
- {
- value = minute;
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::parse_second (Pig& pig, int& value)
-{
- auto checkpoint = pig.cursor ();
-
- int second;
- if (pig.getDigit2 (second) &&
- second >= 0 &&
- second < 60)
- {
- value = second;
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::parse_off_hour (Pig& pig, int& value)
-{
- auto checkpoint = pig.cursor ();
-
- int hour;
- if (pig.getDigit2 (hour) &&
- hour >= 0 &&
- hour <= 12)
- {
- value = hour;
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::parse_off_minute (Pig& pig, int& value)
-{
- auto checkpoint = pig.cursor ();
-
- int minute;
- if (pig.getDigit2 (minute) &&
- minute >= 0 &&
- minute < 60)
- {
- value = minute;
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// now [ !<alpha> && !<digit> ]
-bool Datetime::initializeNow (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("now"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- _date = time (nullptr);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// yesterday/abbrev [ !<alpha> && !<digit> ]
-bool Datetime::initializeYesterday (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- std::string token;
- if (pig.skipPartial ("yesterday", token) &&
- token.length () >= static_cast <std::string::size_type> (Datetime::minimumMatchLength))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_isdst = -1;
- t->tm_mday -= 1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// today/abbrev [ !<alpha> && !<digit> ]
-bool Datetime::initializeToday (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- std::string token;
- if (pig.skipPartial ("today", token) &&
- token.length () >= static_cast <std::string::size_type> (Datetime::minimumMatchLength))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_isdst = -1;
- _date = mktime (t);
-
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// tomcorrow/abbrev [ !<alpha> && !<digit> ]
-bool Datetime::initializeTomorrow (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- std::string token;
- if (pig.skipPartial ("tomorrow", token) &&
- token.length () >= static_cast <std::string::size_type> (Datetime::minimumMatchLength))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_mday++;
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// <digit>+ [ "st" | "nd" | "rd" | "th" ] [ !<alpha> && !<digit> ]
-bool Datetime::initializeOrdinal (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- int number = 0;
- if (pig.getDigits (number) &&
- number > 0 &&
- number <= 31)
- {
- int character1;
- int character2;
- if (pig.getCharacter (character1) &&
- pig.getCharacter (character2) &&
- ! unicodeLatinAlpha (pig.peek ()) &&
- ! unicodeLatinDigit (pig.peek ()))
- {
- int remainder1 = number % 10;
- int remainder2 = number % 100;
- if ((remainder2 != 11 && remainder1 == 1 && character1 == 's' && character2 == 't') ||
- (remainder2 != 12 && remainder1 == 2 && character1 == 'n' && character2 == 'd') ||
- (remainder2 != 13 && remainder1 == 3 && character1 == 'r' && character2 == 'd') ||
- ((remainder2 == 11 ||
- remainder2 == 12 ||
- remainder2 == 13 ||
- remainder1 == 0 ||
- remainder1 > 3) && character1 == 't' && character2 == 'h'))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- int y = t->tm_year + 1900;
- int m = t->tm_mon + 1;
- int d = t->tm_mday;
-
- // If it is this month.
- if (d < number &&
- number <= daysInMonth (y, m))
- {
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_mon = m - 1;
- t->tm_mday = number;
- t->tm_year = y - 1900;
- t->tm_isdst = -1;
- _date = mktime (t);
- }
- else
- {
- if (++m > 12)
- {
- m = 1;
- y++;
- }
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_mon = m - 1;
- t->tm_mday = number;
- t->tm_year = y - 1900;
- t->tm_isdst = -1;
- _date = mktime (t);
- }
-
- return true;
- }
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// sunday/abbrev [ !<alpha> && !<digit> && !: && != ]
-bool Datetime::initializeDayName (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- std::string token;
- for (int day = 0; day <= 7; ++day) // Deliberate <= so that 'sunday' is either 0 or 7.
- {
- if (pig.skipPartial (dayNames[day % 7], token) &&
- token.length () >= static_cast <std::string::size_type> (Datetime::minimumMatchLength))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following) &&
- following != ':' &&
- following != '=')
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- if (t->tm_wday >= day)
- t->tm_mday += day - t->tm_wday + 7;
- else
- t->tm_mday += day - t->tm_wday;
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// january/abbrev [ !<alpha> && !<digit> && !: && != ]
-bool Datetime::initializeMonthName (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- std::string token;
- for (int month = 0; month < 12; ++month)
- {
- if (pig.skipPartial (monthNames[month], token) &&
- token.length () >= static_cast <std::string::size_type> (Datetime::minimumMatchLength))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following) &&
- following != ':' &&
- following != '=')
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- if (t->tm_mon >= month)
- t->tm_year++;
-
- t->tm_mon = month;
- t->tm_mday = 1;
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// later/abbrev [ !<alpha> && !<digit> ]
-// someday/abbrev [ !<alpha> && !<digit> ]
-bool Datetime::initializeLater (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- std::string token;
- if ((pig.skipPartial ("later", token) &&
- token.length () >= static_cast <std::string::size_type> (Datetime::minimumMatchLength))
-
- ||
-
- (pig.skipPartial ("someday", token) &&
- token.length () >= static_cast <std::string::size_type> (std::max (Datetime::minimumMatchLength, 4))))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_year = 138;
- t->tm_mon = 0;
- t->tm_mday = 18;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// sopd [ !<alpha> && !<digit> ]
-bool Datetime::initializeSopd (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("sopd"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_isdst = -1;
- t->tm_mday -= 1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// sod [ !<alpha> && !<digit> ]
-bool Datetime::initializeSod (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("sod"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// sond [ !<alpha> && !<digit> ]
-bool Datetime::initializeSond (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("sond"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_mday++;
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// eopd [ !<alpha> && !<digit> ]
-bool Datetime::initializeEopd (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("eopd"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// eod [ !<alpha> && !<digit> ]
-bool Datetime::initializeEod (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("eod"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_mday++;
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// eond [ !<alpha> && !<digit> ]
-bool Datetime::initializeEond (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("eond"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_mday += 2;
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// sopw [ !<alpha> && !<digit> ]
-bool Datetime::initializeSopw (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("sopw"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
- t->tm_hour = t->tm_min = t->tm_sec = 0;
-
- int extra = (t->tm_wday + 6) % 7;
- t->tm_mday -= extra;
- t->tm_mday -= 7;
-
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// sow [ !<alpha> && !<digit> ]
-bool Datetime::initializeSow (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("sow"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
- t->tm_hour = t->tm_min = t->tm_sec = 0;
-
- int extra = (t->tm_wday + 6) % 7;
- t->tm_mday -= extra;
-
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// sonw [ !<alpha> && !<digit> ]
-bool Datetime::initializeSonw (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("sonw"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
- t->tm_hour = t->tm_min = t->tm_sec = 0;
-
- int extra = (t->tm_wday + 6) % 7;
- t->tm_mday -= extra;
- t->tm_mday += 7;
-
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// eopw [ !<alpha> && !<digit> ]
-bool Datetime::initializeEopw (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("eopw"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
- t->tm_hour = t->tm_min = t->tm_sec = 0;
-
- int extra = (t->tm_wday + 6) % 7;
- t->tm_mday -= extra;
-
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// eow [ !<alpha> && !<digit> ]
-bool Datetime::initializeEow (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("eow"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
- t->tm_hour = t->tm_min = t->tm_sec = 0;
-
- int extra = (t->tm_wday + 6) % 7;
- t->tm_mday -= extra;
- t->tm_mday += 7;
-
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// eonw [ !<alpha> && !<digit> ]
-bool Datetime::initializeEonw (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("eonw"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_mday += 15 - t->tm_wday;
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// sopww [ !<alpha> && !<digit> ]
-bool Datetime::initializeSopww (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("sopww"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_mday += -6 - t->tm_wday;
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// soww [ !<alpha> && !<digit> ]
-bool Datetime::initializeSoww (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("soww"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_mday += 8 - t->tm_wday;
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// sonww [ !<alpha> && !<digit> ]
-bool Datetime::initializeSonww (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("sonww"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_mday += 8 - t->tm_wday;
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// eopww [ !<alpha> && !<digit> ]
-bool Datetime::initializeEopww (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("eopww"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_mday -= (t->tm_wday + 1) % 7;
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// eoww [ !<alpha> && !<digit> ]
-bool Datetime::initializeEoww (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("eoww"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_mday += 6 - t->tm_wday;
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// eonww [ !<alpha> && !<digit> ]
-bool Datetime::initializeEonww (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("eonww"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_mday += 13 - t->tm_wday;
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// sopm [ !<alpha> && !<digit> ]
-bool Datetime::initializeSopm (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("sopm"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
-
- if (t->tm_mon == 0)
- {
- t->tm_year--;
- t->tm_mon = 11;
- }
- else
- t->tm_mon--;
-
- t->tm_mday = 1;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// som [ !<alpha> && !<digit> ]
-bool Datetime::initializeSom (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("som"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_mday = 1;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// sonm [ !<alpha> && !<digit> ]
-bool Datetime::initializeSonm (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("sonm"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
-
- t->tm_mon++;
- if (t->tm_mon > 11)
- {
- t->tm_year++;
- t->tm_mon = 0;
- }
-
- t->tm_mday = 1;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// eopm [ !<alpha> && !<digit> ]
-bool Datetime::initializeEopm (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("eopm"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_mday = 1;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// eom [ !<alpha> && !<digit> ]
-bool Datetime::initializeEom (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("eom"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
-
- t->tm_mon++;
- if (t->tm_mon > 11)
- {
- t->tm_year++;
- t->tm_mon = 0;
- }
-
- t->tm_mday = 1;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// eonm [ !<alpha> && !<digit> ]
-bool Datetime::initializeEonm (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("eonm"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_mday = 1;
- t->tm_mon += 2;
- if (t->tm_mon > 11)
- {
- t->tm_year++;
- t->tm_mon -= 12;
- }
-
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// sopq [ !<alpha> && !<digit> ]
-bool Datetime::initializeSopq (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("sopq"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_mon -= t->tm_mon % 3;
- t->tm_mon -= 3;
- if (t->tm_mon < 0)
- {
- t->tm_mon += 12;
- t->tm_year--;
- }
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_mday = 1;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// soq [ !<alpha> && !<digit> ]
-bool Datetime::initializeSoq (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("soq"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_mon -= t->tm_mon % 3;
- t->tm_mday = 1;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// sonq [ !<alpha> && !<digit> ]
-bool Datetime::initializeSonq (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("sonq"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_mon += 3 - (t->tm_mon % 3);
- if (t->tm_mon > 11)
- {
- t->tm_mon -= 12;
- ++t->tm_year;
- }
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_mday = 1;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// eopq [ !<alpha> && !<digit> ]
-bool Datetime::initializeEopq (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("eopq"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_mon -= t->tm_mon % 3;
- t->tm_mday = 1;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// eoq [ !<alpha> && !<digit> ]
-bool Datetime::initializeEoq (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("eoq"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_mon += 3 - (t->tm_mon % 3);
- if (t->tm_mon > 11)
- {
- t->tm_mon -= 12;
- ++t->tm_year;
- }
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_mday = 1;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// eonq [ !<alpha> && !<digit> ]
-bool Datetime::initializeEonq (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("eonq"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_mon += 6 - (t->tm_mon % 3);
- if (t->tm_mon > 11)
- {
- t->tm_mon -= 12;
- ++t->tm_year;
- }
-
- t->tm_mday = 1;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// sopy [ !<alpha> && !<digit> ]
-bool Datetime::initializeSopy (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("sopy"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_mon = 0;
- t->tm_mday = 1;
- t->tm_year--;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// soy [ !<alpha> && !<digit> ]
-bool Datetime::initializeSoy (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("soy"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_mon = 0;
- t->tm_mday = 1;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// sony [ !<alpha> && !<digit> ]
-bool Datetime::initializeSony (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("sony"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_mon = 0;
- t->tm_mday = 1;
- t->tm_year++;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// eopy [ !<alpha> && !<digit> ]
-bool Datetime::initializeEopy (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("eopy"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_mon = 0;
- t->tm_mday = 1;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// eoy [ !<alpha> && !<digit> ]
-bool Datetime::initializeEoy (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("eoy"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_mon = 0;
- t->tm_mday = 1;
- t->tm_year++;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// eony [ !<alpha> && !<digit> ]
-bool Datetime::initializeEony (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("eony"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- t->tm_hour = t->tm_min = t->tm_sec = 0;
- t->tm_mon = 0;
- t->tm_mday = 1;
- t->tm_year += 2;
- t->tm_isdst = -1;
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// easter [ !<alpha> && !<digit> ]
-// eastermonday [ !<alpha> && !<digit> ]
-// ascension [ !<alpha> && !<digit> ]
-// pentecost [ !<alpha> && !<digit> ]
-// goodfriday [ !<alpha> && !<digit> ]
-bool Datetime::initializeEaster (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- std::vector <std::string> holidays = {"eastermonday", "easter", "ascension", "pentecost", "goodfriday"};
- std::vector <int> offsets = { 1, 0, 39, 49, -2};
-
- std::string token;
- for (int holiday = 0; holiday < 5; ++holiday)
- {
- if (pig.skipLiteral (holidays[holiday]) &&
- ! unicodeLatinAlpha (pig.peek ()) &&
- ! unicodeLatinDigit (pig.peek ()))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- easter (t);
- _date = mktime (t);
-
- // If the result is earlier this year, then recalc for next year.
- if (_date < now)
- {
- t = localtime (&now);
- t->tm_year++;
- easter (t);
- }
-
- // Adjust according to holiday-specific offsets.
- t->tm_mday += offsets[holiday];
-
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// midsommar [ !<alpha> && !<digit> ]
-bool Datetime::initializeMidsommar (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("midsommar"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
- midsommar (t);
- _date = mktime (t);
-
- // If the result is earlier this year, then recalc for next year.
- if (_date < now)
- {
- t = localtime (&now);
- t->tm_year++;
- midsommar (t);
- }
-
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// midsommarafton [ !<alpha> && !<digit> ]
-// juhannus [ !<alpha> && !<digit> ]
-bool Datetime::initializeMidsommarafton (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skipLiteral ("midsommarafton") ||
- pig.skipLiteral ("juhannus"))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
- midsommarafton (t);
- _date = mktime (t);
-
- // If the result is earlier this year, then recalc for next year.
- if (_date < now)
- {
- t = localtime (&now);
- t->tm_year++;
- midsommarafton (t);
- }
-
- _date = mktime (t);
- return true;
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// 8am
-// 8a
-// 8:30am
-// 8:30a
-// 8:30
-//
-// \d+ [ : \d{2} ] [ am | a | pm | p ] [ !<alpha> && !<digit> && !: && !+ && !- ]
-//
-bool Datetime::initializeInformalTime (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- int digit = 0;
- bool needDesignator = true; // Require am/pm.
- bool haveDesignator = false; // Provided am/pm.
- if (pig.getDigit (digit))
- {
- int hours = digit;
- if (pig.getDigit (digit))
- hours = 10 * hours + digit;
-
- int minutes = 0;
- int seconds = 0;
- if (pig.skip (':'))
- {
- if (! pig.getDigit2 (minutes))
- {
- pig.restoreTo (checkpoint);
- return false;
- }
-
- if (pig.skip (':'))
- {
- if (! pig.getDigits (seconds))
- {
- pig.restoreTo (checkpoint);
- return false;
- }
- }
-
- needDesignator = false;
- }
-
- if (pig.skipLiteral ("am") ||
- pig.skipLiteral ("a"))
- {
- haveDesignator = true;
- if (hours == 12)
- hours = 0;
- }
-
- else if (pig.skipLiteral ("pm") ||
- pig.skipLiteral ("p"))
- {
- // Note: '12pm is an exception:
- // 12am = 0h
- // 11am = 11h + 12h
- // 12pm = 12h
- // 1pm = 1h + 12h
- if (hours != 12)
- hours += 12;
-
- haveDesignator = true;
- }
-
- // Informal time needs to be terminated.
- auto following = pig.peek ();
- if (unicodeLatinAlpha (following) ||
- unicodeLatinDigit (following) ||
- following == ':' ||
- following == '-' ||
- following == '+')
- {
- pig.restoreTo (checkpoint);
- return false;
- }
-
- if (haveDesignator || ! needDesignator)
- {
- // Midnight today + hours:minutes:seconds.
- time_t now = time (nullptr);
- struct tm* t = localtime (&now);
-
- int now_seconds = (t->tm_hour * 3600) + (t->tm_min * 60) + t->tm_sec;
- int calc_seconds = (hours * 3600) + (minutes * 60) + seconds;
-
- if (calc_seconds < now_seconds)
- ++t->tm_mday;
-
- // Basic validation.
- if (hours >= 0 && hours < 24 &&
- minutes >= 0 && minutes < 60 &&
- seconds >= 0 && seconds < 60)
- {
- t->tm_hour = hours;
- t->tm_min = minutes;
- t->tm_sec = seconds;
- t->tm_isdst = -1;
- _date = mktime (t);
-
- return true;
- }
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Datetime::easter (struct tm* t) const
-{
- int Y = t->tm_year + 1900;
- int a = Y % 19;
- int b = Y / 100;
- int c = Y % 100;
- int d = b / 4;
- int e = b % 4;
- int f = (b + 8) / 25;
- int g = (b - f + 1) / 3;
- int h = (19 * a + b - d - g + 15) % 30;
- int i = c / 4;
- int k = c % 4;
- int L = (32 + 2 * e + 2 * i - h - k) % 7;
- int m = (a + 11 * h + 22 * L) / 451;
- int month = (h + L - 7 * m + 114) / 31;
- int day = ((h + L - 7 * m + 114) % 31) + 1;
-
- t->tm_isdst = -1; // Requests that mktime determine summer time effect.
- t->tm_mday = day;
- t->tm_mon = month - 1;
- t->tm_year = Y - 1900;
- t->tm_isdst = -1;
- t->tm_hour = t->tm_min = t->tm_sec = 0;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Datetime::midsommar (struct tm* t) const
-{
- t->tm_mon = 5; // June.
- t->tm_mday = 20; // Saturday after 20th.
- t->tm_hour = t->tm_min = t->tm_sec = 0; // Midnight.
- t->tm_isdst = -1; // Probably DST, but check.
-
- time_t then = mktime (t); // Obtain the weekday of June 20th.
- struct tm* mid = localtime (&then);
- t->tm_mday += 6 - mid->tm_wday; // How many days after 20th.
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Datetime::midsommarafton (struct tm* t) const
-{
- t->tm_mon = 5; // June.
- t->tm_mday = 19; // Saturday after 20th.
- t->tm_hour = t->tm_min = t->tm_sec = 0; // Midnight.
- t->tm_isdst = -1; // Probably DST, but check.
-
- time_t then = mktime (t); // Obtain the weekday of June 19th.
- struct tm* mid = localtime (&then);
- t->tm_mday += 5 - mid->tm_wday; // How many days after 19th.
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Suggested date expressions:
-// {ordinal} {day} in|of {month}
-// last|past|next|this {day}
-// last|past|next|this {month}
-// last|past|next|this week
-// last|past|next|this month
-// last|past|next|this weekend
-// last|past|next|this year
-// {day} last|past|next|this week
-// {day} [at] {time}
-// {time} {day}
-//
-// Candidates:
-// <dayname> <time>
-// <time>
-// tue 9am
-// Friday before easter
-// 3 days before eom
-// in the morning
-// am|pm
-// 4pm
-// noon
-// midnight
-// tomorrow in one year
-// in two weeks
-// 2 weeks from now
-// 2 weeks ago tuesday
-// thursday in 2 weeks
-// last day next month
-// 10 days from today
-// thursday before last weekend in may
-// friday last full week in may
-// 3rd wednesday this month
-// 3 weeks after 2nd tuesday next month
-// 100 days from the beginning of the month
-// 10 days after last monday
-// sunday in the evening
-// in 6 hours
-// 6 in the morning
-// kl 18
-// feb 11
-// 11 feb
-// 2011-02-08
-// 11/19/2011
-// next business day
-// new moon
-// full moon
-// in 28 days
-// 3rd quarter
-// week 23
-// {number} {unit}
-// - {number} {unit}
-// {ordinal} {unit} in {larger-unit}
-// end of day tomorrow
-// end of {day}
-// by {day}
-// first thing {day}
-//
-
-////////////////////////////////////////////////////////////////////////////////
-// <ordinal> <weekday> in|of <month>
-bool Datetime::initializeNthDayInMonth (const std::vector <std::string>& tokens)
-{
- if (tokens.size () == 4)
- {
- int ordinal {0};
- if (isOrdinal (tokens[0], ordinal))
- {
- auto day = Datetime::dayOfWeek (tokens[1]);
- if (day != -1)
- {
- if (tokens[2] == "in" ||
- tokens[2] == "of")
- {
- auto month = Datetime::monthOfYear (tokens[3]);
- if (month != -1)
- {
- std::cout << "# ordinal=" << ordinal << " day=" << day << " in month=" << month << '\n';
-
- // TODO Assume 1st of the month
- // TODO Assume current year
- // TODO Determine the day
- // TODO Project forwards/backwards, to the desired day
- // TODO Add ((ordinal - 1) * 7) days
-
- return true;
- }
- }
- }
- }
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::isOrdinal (const std::string& token, int& ordinal)
-{
- Pig p (token);
- int number;
- std::string suffix;
- if (p.getDigits (number) &&
- p.getRemainder (suffix))
- {
- if (((number >= 11 || number <= 13) && suffix == "th") ||
- (number % 10 == 1 && suffix == "st") ||
- (number % 10 == 2 && suffix == "nd") ||
- (number % 10 == 3 && suffix == "rd") ||
- ( suffix == "th"))
- {
- ordinal = number;
- return true;
- }
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Validation via simple range checking.
-bool Datetime::validate ()
-{
- // _year;
- if ((_year && (_year < 1900 || _year > 2200)) ||
- (_month && (_month < 1 || _month > 12)) ||
- (_week && (_week < 1 || _week > 53)) ||
- (_weekday && (_weekday < 0 || _weekday > 6)) ||
- (_julian && (_julian < 1 || _julian > Datetime::daysInYear (_year))) ||
- (_day && (_day < 1 || _day > Datetime::daysInMonth (_year, _month))) ||
- (_seconds && (_seconds < 1 || _seconds > 86400)) ||
- (_offset && (_offset < -86400 || _offset > 86400)))
- return false;
-
- return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// int tm_sec; seconds (0 - 60)
-// int tm_min; minutes (0 - 59)
-// int tm_hour; hours (0 - 23)
-// int tm_mday; day of month (1 - 31)
-// int tm_mon; month of year (0 - 11)
-// int tm_year; year - 1900
-// int tm_wday; day of week (Sunday = 0)
-// int tm_yday; day of year (0 - 365)
-// int tm_isdst; is summer time in effect?
-// char *tm_zone; abbreviation of timezone name
-// long tm_gmtoff; offset from UTC in seconds
-void Datetime::resolve ()
-{
- // Don't touch the original values.
- int year = _year;
- int month = _month;
- int week = _week;
- int weekday = _weekday;
- int julian = _julian;
- int day = _day;
- int seconds = _seconds;
- int offset = _offset;
- bool utc = _utc;
-
- // Get current time.
- time_t now = time (nullptr);
-
- // A UTC offset needs to be accommodated. Once the offset is subtracted,
- // only local and UTC times remain.
- if (offset)
- {
- seconds -= offset;
- now -= offset;
- utc = true;
- }
-
- // Get 'now' in the relevant location.
- struct tm* t_now = utc ? gmtime (&now) : localtime (&now);
-
- int seconds_now = (t_now->tm_hour * 3600) +
- (t_now->tm_min * 60) +
- t_now->tm_sec;
-
- // Project forward one day if the specified seconds are earlier in the day
- // than the current seconds.
- // TODO This does not cover the inverse case of subtracting 86400.
- if (year == 0 &&
- month == 0 &&
- day == 0 &&
- week == 0 &&
- weekday == 0 &&
- seconds < seconds_now)
- {
- seconds += 86400;
- }
-
- // Convert week + weekday --> julian.
- if (week)
- {
- julian = (week * 7) + weekday - dayOfWeek (year, 1, 4) - 3;
- }
-
- // Provide default values for year, month, day.
- else
- {
- // Default values for year, month, day:
- //
- // y m d --> y m d
- // y m - --> y m 1
- // y - - --> y 1 1
- // - - - --> now now now
- //
- if (year == 0)
- {
- year = t_now->tm_year + 1900;
- month = t_now->tm_mon + 1;
- day = t_now->tm_mday;
- }
- else
- {
- if (month == 0)
- {
- month = 1;
- day = 1;
- }
- else if (day == 0)
- day = 1;
- }
- }
-
- if (julian)
- {
- month = 1;
- day = julian;
- }
-
- struct tm t {};
- t.tm_isdst = -1; // Requests that mktime/gmtime determine summer time effect.
- t.tm_year = year - 1900;
- t.tm_mon = month - 1;
- t.tm_mday = day;
-
- if (seconds > 86400)
- {
- int days = seconds / 86400;
- t.tm_mday += days;
- seconds %= 86400;
- }
-
- t.tm_hour = seconds / 3600;
- t.tm_min = (seconds % 3600) / 60;
- t.tm_sec = seconds % 60;
-
- _date = utc ? timegm (&t) : mktime (&t);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-time_t Datetime::toEpoch () const
-{
- return _date;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Datetime::toEpochString () const
-{
- return format ("{1}", _date);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// 19980119T070000Z = YYYYMMDDThhmmssZ
-std::string Datetime::toISO () const
-{
- struct tm* t = gmtime (&_date);
-
- std::stringstream iso;
- iso << std::setw (4) << std::setfill ('0') << t->tm_year + 1900
- << std::setw (2) << std::setfill ('0') << t->tm_mon + 1
- << std::setw (2) << std::setfill ('0') << t->tm_mday
- << 'T'
- << std::setw (2) << std::setfill ('0') << t->tm_hour
- << std::setw (2) << std::setfill ('0') << t->tm_min
- << std::setw (2) << std::setfill ('0') << t->tm_sec
- << 'Z';
-
- return iso.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// 1998-01-19T07:00:00 = YYYY-MM-DDThh:mm:ss
-std::string Datetime::toISOLocalExtended () const
-{
- struct tm* t = localtime (&_date);
-
- std::stringstream iso;
- iso << std::setw (4) << std::setfill ('0') << t->tm_year + 1900
- << '-'
- << std::setw (2) << std::setfill ('0') << t->tm_mon + 1
- << '-'
- << std::setw (2) << std::setfill ('0') << t->tm_mday
- << 'T'
- << std::setw (2) << std::setfill ('0') << t->tm_hour
- << ':'
- << std::setw (2) << std::setfill ('0') << t->tm_min
- << ':'
- << std::setw (2) << std::setfill ('0') << t->tm_sec;
-
- return iso.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-double Datetime::toJulian () const
-{
- return (_date / 86400.0) + 2440587.5;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Datetime::toYMD (int& y, int& m, int& d) const
-{
- struct tm* t = localtime (&_date);
-
- m = t->tm_mon + 1;
- d = t->tm_mday;
- y = t->tm_year + 1900;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-const std::string Datetime::toString (const std::string& format) const
-{
- std::stringstream formatted;
- for (unsigned int i = 0; i < format.length (); ++i)
- {
- int c = format[i];
- switch (c)
- {
- case 'm': formatted << month (); break;
- case 'M': formatted << std::setw (2) << std::setfill ('0') << month (); break;
- case 'd': formatted << day (); break;
- case 'D': formatted << std::setw (2) << std::setfill ('0') << day (); break;
- case 'y': formatted << std::setw (2) << std::setfill ('0') << (year () % 100); break;
- case 'Y': formatted << year (); break;
- case 'a': formatted << Datetime::dayNameShort (dayOfWeek ()); break;
- case 'A': formatted << Datetime::dayName (dayOfWeek ()); break;
- case 'b': formatted << Datetime::monthNameShort (month ()); break;
- case 'B': formatted << Datetime::monthName (month ()); break;
- case 'v': formatted << week (); break;
- case 'V': formatted << std::setw (2) << std::setfill ('0') << week (); break;
- case 'h': formatted << hour (); break;
- case 'H': formatted << std::setw (2) << std::setfill ('0') << hour (); break;
- case 'n': formatted << minute (); break;
- case 'N': formatted << std::setw (2) << std::setfill ('0') << minute (); break;
- case 's': formatted << second (); break;
- case 'S': formatted << std::setw (2) << std::setfill ('0') << second (); break;
- case 'j': formatted << dayOfYear (); break;
- case 'J': formatted << std::setw (3) << std::setfill ('0') << dayOfYear (); break;
- case 'w': formatted << dayOfWeek (); break;
- default: formatted << static_cast <char> (c); break;
- }
- }
-
- return formatted.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Datetime Datetime::startOfDay () const
-{
- return Datetime (year (), month (), day ());
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Datetime Datetime::startOfWeek () const
-{
- Datetime sow (_date);
- sow -= (dayOfWeek () * 86400);
- return Datetime (sow.year (), sow.month (), sow.day ());
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Datetime Datetime::startOfMonth () const
-{
- return Datetime (year (), month (), 1);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Datetime Datetime::startOfYear () const
-{
- return Datetime (year (), 1, 1);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::valid (const std::string& input, const std::string& format)
-{
- try
- {
- Datetime test (input, format);
- }
-
- catch (...)
- {
- return false;
- }
-
- return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::valid (
- const int y, const int m, const int d,
- const int hr, const int mi, const int se)
-{
- if (hr < 0 || hr > 24)
- return false;
-
- if (mi < 0 || mi > 59)
- return false;
-
- if (se < 0 || se > 59)
- return false;
-
- if (hr == 24 &&
- (mi != 0 ||
- se != 0))
- return false;
-
- return Datetime::valid (y, m, d);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::valid (const int y, const int m, const int d)
-{
- // Check that the year is valid.
- if (y < 0)
- return false;
-
- // Check that the month is valid.
- if (m < 1 || m > 12)
- return false;
-
- // Finally check that the days fall within the acceptable range for this
- // month, and whether or not this is a leap year.
- if (d < 1 || d > Datetime::daysInMonth (y, m))
- return false;
-
- return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Julian
-bool Datetime::valid (const int y, const int d)
-{
- // Check that the year is valid.
- if (y < 0)
- return false;
-
- if (d < 1 || d > Datetime::daysInYear (y))
- return false;
-
- return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Static
-bool Datetime::leapYear (int year)
-{
- return ((! (year % 4)) && (year % 100)) ||
- ! (year % 400);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Static
-int Datetime::daysInMonth (int year, int month)
-{
- // Protect against arguments being passed in the wrong order.
- assert (year >= 1969 && year < 2100);
- assert (month >= 1 && month <= 31);
-
- static int days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
-
- if (month == 2 && Datetime::leapYear (year))
- return 29;
-
- return days[month - 1];
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Static
-int Datetime::daysInYear (int year)
-{
- return Datetime::leapYear (year) ? 366 : 365;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Static
-std::string Datetime::monthName (int month)
-{
- assert (month > 0);
- assert (month <= 12);
- return upperCaseFirst (monthNames[month - 1]);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Static
-std::string Datetime::monthNameShort (int month)
-{
- assert (month > 0);
- assert (month <= 12);
- return upperCaseFirst (monthNames[month - 1]).substr (0, 3);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Static
-std::string Datetime::dayName (int dow)
-{
- assert (dow >= 0);
- assert (dow <= 6);
- return upperCaseFirst (dayNames[dow]);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Static
-std::string Datetime::dayNameShort (int dow)
-{
- assert (dow >= 0);
- assert (dow <= 6);
- return upperCaseFirst (dayNames[dow]).substr (0, 3);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Static
-int Datetime::dayOfWeek (const std::string& input)
-{
- if (Datetime::minimumMatchLength== 0)
- Datetime::minimumMatchLength = 3;
-
- for (unsigned int i = 0; i < dayNames.size (); ++i)
- if (closeEnough (dayNames[i], input, Datetime::minimumMatchLength))
- return i;
-
- return -1;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Using Zeller's Congruence.
-// Static
-int Datetime::dayOfWeek (int year, int month, int day)
-{
- int adj = (14 - month) / 12;
- int m = month + 12 * adj - 2;
- int y = year - adj;
- return (day + (13 * m - 1) / 5 + y + y / 4 - y / 100 + y / 400) % 7;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Static
-int Datetime::monthOfYear (const std::string& input)
-{
- if (Datetime::minimumMatchLength== 0)
- Datetime::minimumMatchLength = 3;
-
- for (unsigned int i = 0; i < monthNames.size (); ++i)
- if (closeEnough (monthNames[i], input, Datetime::minimumMatchLength))
- return i + 1;
-
- return -1;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Static
-int Datetime::length (const std::string& format)
-{
- int len = 0;
- for (auto& i : format)
- {
- switch (i)
- {
- case 'm':
- case 'M':
- case 'd':
- case 'D':
- case 'y':
- case 'v':
- case 'V':
- case 'h':
- case 'H':
- case 'n':
- case 'N':
- case 's':
- case 'S': len += 2; break;
- case 'b':
- case 'j':
- case 'J':
- case 'a': len += 3; break;
- case 'Y': len += 4; break;
- case 'A':
- case 'B': len += 10; break;
-
- // Calculate the width, don't assume a single character width.
- default: len += mk_wcwidth (i); break;
- }
- }
-
- return len;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Datetime::month () const
-{
- struct tm* t = localtime (&_date);
- return t->tm_mon + 1;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Datetime::week () const
-{
- struct tm* t = localtime (&_date);
-
- char weekStr[3];
- if (Datetime::weekstart == 0)
- strftime (weekStr, sizeof (weekStr), "%U", t);
- else if (Datetime::weekstart == 1)
- strftime (weekStr, sizeof (weekStr), "%V", t);
- else
- throw std::string ("The week may only start on a Sunday or Monday.");
-
- int weekNumber = strtol (weekStr, nullptr, 10);
- if (weekstart == 0)
- weekNumber += 1;
-
- return weekNumber;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Datetime::day () const
-{
- struct tm* t = localtime (&_date);
- return t->tm_mday;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Datetime::year () const
-{
- struct tm* t = localtime (&_date);
- return t->tm_year + 1900;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Datetime::dayOfWeek () const
-{
- struct tm* t = localtime (&_date);
- return t->tm_wday;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Datetime::dayOfYear () const
-{
- struct tm* t = localtime (&_date);
- return t->tm_yday + 1;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Datetime::hour () const
-{
- struct tm* t = localtime (&_date);
- return t->tm_hour;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Datetime::minute () const
-{
- struct tm* t = localtime (&_date);
- return t->tm_min;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Datetime::second () const
-{
- struct tm* t = localtime (&_date);
- return t->tm_sec;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::operator== (const Datetime& rhs) const
-{
- return rhs._date == _date;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::operator!= (const Datetime& rhs) const
-{
- return rhs._date != _date;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::operator< (const Datetime& rhs) const
-{
- return _date < rhs._date;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::operator> (const Datetime& rhs) const
-{
- return _date > rhs._date;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::operator<= (const Datetime& rhs) const
-{
- return _date <= rhs._date;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::operator>= (const Datetime& rhs) const
-{
- return _date >= rhs._date;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::sameHour (const Datetime& rhs) const
-{
- return year () == rhs.year () &&
- month () == rhs.month () &&
- day () == rhs.day () &&
- hour () == rhs.hour ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::sameDay (const Datetime& rhs) const
-{
- return year () == rhs.year () &&
- month () == rhs.month () &&
- day () == rhs.day ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::sameWeek (const Datetime& rhs) const
-{
- return year () == rhs.year () &&
- week () == rhs.week ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::sameMonth (const Datetime& rhs) const
-{
- return year () == rhs.year () &&
- month () == rhs.month ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::sameQuarter (const Datetime& rhs) const
-{
- return year () == rhs.year () &&
- ((month () - 1) / 3) == ((rhs.month () - 1) / 3);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Datetime::sameYear (const Datetime& rhs) const
-{
- return year () == rhs.year ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Datetime Datetime::operator+ (const int delta)
-{
- return Datetime (_date + delta);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Datetime Datetime::operator- (const int delta)
-{
- return Datetime (_date - delta);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Datetime& Datetime::operator+= (const int delta)
-{
- _date += (time_t) delta;
- return *this;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Datetime& Datetime::operator-= (const int delta)
-{
- _date -= (time_t) delta;
- return *this;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-time_t Datetime::operator- (const Datetime& rhs)
-{
- return _date - rhs._date;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Prefix decrement by one day.
-void Datetime::operator-- ()
-{
- Datetime yesterday = startOfDay () - 1;
- yesterday = Datetime (yesterday.year (),
- yesterday.month (),
- yesterday.day (),
- hour (),
- minute (),
- second ());
- _date = yesterday._date;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Postfix decrement by one day.
-void Datetime::operator-- (int)
-{
- Datetime yesterday = startOfDay () - 1;
- yesterday = Datetime (yesterday.year (),
- yesterday.month (),
- yesterday.day (),
- hour (),
- minute (),
- second ());
- _date = yesterday._date;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Prefix increment by one day.
-void Datetime::operator++ ()
-{
- Datetime tomorrow = (startOfDay () + 90001).startOfDay ();
- tomorrow = Datetime (tomorrow.year (),
- tomorrow.month (),
- tomorrow.day (),
- hour (),
- minute (),
- second ());
- _date = tomorrow._date;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Postfix increment by one day.
-void Datetime::operator++ (int)
-{
- Datetime tomorrow = (startOfDay () + 90001).startOfDay ();
- tomorrow = Datetime (tomorrow.year (),
- tomorrow.month (),
- tomorrow.day (),
- hour (),
- minute (),
- second ());
- _date = tomorrow._date;
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_DATETIME
-#define INCLUDED_DATETIME
-
-#include <string>
-#include <ctime>
-#include <Pig.h>
-
-class Datetime
-{
-public:
- static int weekstart;
- static int minimumMatchLength;
- static bool isoEnabled;
- static bool standaloneDateEnabled;
- static bool standaloneTimeEnabled;
-
- Datetime ();
- Datetime (const std::string&, const std::string& format = "");
- Datetime (time_t);
- Datetime (const int, const int, const int);
- Datetime (const int, const int, const int, const int, const int, const int);
- bool parse (const std::string&, std::string::size_type&, const std::string& format = "");
- time_t toEpoch () const;
- std::string toEpochString () const;
- std::string toISO () const;
- std::string toISOLocalExtended () const;
- double toJulian () const;
- void toYMD (int&, int&, int&) const;
- const std::string toString (const std::string& format = "Y-M-D") const;
-
- Datetime startOfDay () const;
- Datetime startOfWeek () const;
- Datetime startOfMonth () const;
- Datetime startOfYear () const;
-
- static bool valid (const std::string&, const std::string& format = "");
- static bool valid (const int, const int, const int, const int, const int, const int);
- static bool valid (const int, const int, const int);
- static bool valid (const int, const int);
- static bool leapYear (int);
- static int daysInMonth (int, int);
- static int daysInYear (int);
- static std::string monthName (int);
- static std::string monthNameShort (int);
- static std::string dayName (int);
- static std::string dayNameShort (int);
- static int dayOfWeek (const std::string&);
- static int dayOfWeek (int, int, int);
- static int monthOfYear (const std::string&);
- static int length (const std::string&);
-
- int month () const;
- int week () const;
- int day () const;
- int year () const;
- int dayOfWeek () const;
- int dayOfYear () const;
- int hour () const;
- int minute () const;
- int second () const;
-
- bool operator== (const Datetime&) const;
- bool operator!= (const Datetime&) const;
- bool operator< (const Datetime&) const;
- bool operator> (const Datetime&) const;
- bool operator<= (const Datetime&) const;
- bool operator>= (const Datetime&) const;
- bool sameHour (const Datetime&) const;
- bool sameDay (const Datetime&) const;
- bool sameWeek (const Datetime&) const;
- bool sameMonth (const Datetime&) const;
- bool sameQuarter (const Datetime&) const;
- bool sameYear (const Datetime&) const;
- Datetime operator+ (const int);
- Datetime operator- (const int);
- Datetime& operator+= (const int);
- Datetime& operator-= (const int);
- time_t operator- (const Datetime&);
- void operator-- (); // Prefix
- void operator-- (int); // Postfix
- void operator++ (); // Prefix
- void operator++ (int); // Postfix
-
-private:
- void clear ();
- bool parse_formatted (Pig&, const std::string&);
- bool parse_named (Pig&);
- bool parse_epoch (Pig&);
- bool parse_date_time_ext (Pig&);
- bool parse_date_ext (Pig&);
- bool parse_off_ext (Pig&);
- bool parse_time_ext (Pig&, bool terminated = true);
- bool parse_time_utc_ext (Pig&);
- bool parse_time_off_ext (Pig&);
- bool parse_date_time (Pig&);
- bool parse_date (Pig&);
- bool parse_time_utc (Pig&);
- bool parse_time_off (Pig&);
- bool parse_time (Pig&, bool terminated = true);
- bool parse_off (Pig&);
-
- bool parse_year (Pig&, int&);
- bool parse_month (Pig&, int&);
- bool parse_week (Pig&, int&);
- bool parse_julian (Pig&, int&);
- bool parse_day (Pig&, int&);
- bool parse_weekday (Pig&, int&);
- bool parse_hour (Pig&, int&);
- bool parse_minute (Pig&, int&);
- bool parse_second (Pig&, int&);
- bool parse_off_hour (Pig&, int&);
- bool parse_off_minute (Pig&, int&);
-
- bool initializeNow (Pig&);
- bool initializeYesterday (Pig&);
- bool initializeToday (Pig&);
- bool initializeTomorrow (Pig&);
- bool initializeOrdinal (Pig&);
- bool initializeDayName (Pig&);
- bool initializeMonthName (Pig&);
- bool initializeLater (Pig&);
- bool initializeSopd (Pig&);
- bool initializeSod (Pig&);
- bool initializeSond (Pig&);
- bool initializeEopd (Pig&);
- bool initializeEod (Pig&);
- bool initializeEond (Pig&);
- bool initializeSopw (Pig&);
- bool initializeSow (Pig&);
- bool initializeSonw (Pig&);
- bool initializeEopw (Pig&);
- bool initializeEow (Pig&);
- bool initializeEonw (Pig&);
- bool initializeSopww (Pig&);
- bool initializeSonww (Pig&);
- bool initializeSoww (Pig&);
- bool initializeEopww (Pig&);
- bool initializeEonww (Pig&);
- bool initializeEoww (Pig&);
- bool initializeSopm (Pig&);
- bool initializeSom (Pig&);
- bool initializeSonm (Pig&);
- bool initializeEopm (Pig&);
- bool initializeEom (Pig&);
- bool initializeEonm (Pig&);
- bool initializeSopq (Pig&);
- bool initializeSoq (Pig&);
- bool initializeSonq (Pig&);
- bool initializeEopq (Pig&);
- bool initializeEoq (Pig&);
- bool initializeEonq (Pig&);
- bool initializeSopy (Pig&);
- bool initializeSoy (Pig&);
- bool initializeSony (Pig&);
- bool initializeEopy (Pig&);
- bool initializeEoy (Pig&);
- bool initializeEony (Pig&);
- bool initializeEaster (Pig&);
- bool initializeMidsommar (Pig&);
- bool initializeMidsommarafton (Pig&);
- bool initializeInformalTime (Pig&);
- void easter (struct tm*) const;
- void midsommar (struct tm*) const;
- void midsommarafton (struct tm*) const;
-
- bool initializeNthDayInMonth (const std::vector <std::string>&);
-
- bool isOrdinal (const std::string&, int&);
-
- bool validate ();
- void resolve ();
- std::string dump () const;
-
-public:
- int _year {0};
- int _month {0};
- int _week {0};
- int _weekday {0};
- int _julian {0};
- int _day {0};
- int _seconds {0};
- int _offset {0};
- bool _utc {false};
- time_t _date {0};
-};
-
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <Duration.h>
-#include <unicode.h>
-#include <sstream>
-#include <iomanip>
-#include <vector>
-
-bool Duration::standaloneSecondsEnabled = true;
-
-#define DAY 86400
-#define HOUR 3600
-#define MINUTE 60
-#define SECOND 1
-
-static struct
-{
- std::string unit;
- int seconds;
- bool standalone;
-} durations[] =
-{
- // These are sorted by first character, then length, so that Pig::getOneOf
- // returns a maximal match.
- {"annual", 365 * DAY, true },
- {"biannual", 730 * DAY, true },
- {"bimonthly", 61 * DAY, true },
- {"biweekly", 14 * DAY, true },
- {"biyearly", 730 * DAY, true },
- {"daily", 1 * DAY, true },
- {"days", 1 * DAY, false},
- {"day", 1 * DAY, true },
- {"d", 1 * DAY, false},
- {"fortnight", 14 * DAY, true },
- {"hours", 1 * HOUR, false},
- {"hour", 1 * HOUR, true },
- {"hrs", 1 * HOUR, false},
- {"hr", 1 * HOUR, true },
- {"h", 1 * HOUR, false},
- {"minutes", 1 * MINUTE, false},
- {"minute", 1 * MINUTE, true },
- {"mins", 1 * MINUTE, false},
- {"min", 1 * MINUTE, true },
- {"monthly", 30 * DAY, true },
- {"months", 30 * DAY, false},
- {"month", 30 * DAY, true },
- {"mnths", 30 * DAY, false},
- {"mths", 30 * DAY, false},
- {"mth", 30 * DAY, true },
- {"mos", 30 * DAY, false},
- {"mo", 30 * DAY, true },
- {"m", 30 * DAY, false},
- {"quarterly", 91 * DAY, true },
- {"quarters", 91 * DAY, false},
- {"quarter", 91 * DAY, true },
- {"qrtrs", 91 * DAY, false},
- {"qrtr", 91 * DAY, true },
- {"qtrs", 91 * DAY, false},
- {"qtr", 91 * DAY, true },
- {"q", 91 * DAY, false},
- {"semiannual", 183 * DAY, true },
- {"sennight", 14 * DAY, false},
- {"seconds", 1 * SECOND, false},
- {"second", 1 * SECOND, true },
- {"secs", 1 * SECOND, false},
- {"sec", 1 * SECOND, true },
- {"s", 1 * SECOND, false},
- {"weekdays", 1 * DAY, true },
- {"weekly", 7 * DAY, true },
- {"weeks", 7 * DAY, false},
- {"week", 7 * DAY, true },
- {"wks", 7 * DAY, false},
- {"wk", 7 * DAY, true },
- {"w", 7 * DAY, false},
- {"yearly", 365 * DAY, true },
- {"years", 365 * DAY, false},
- {"year", 365 * DAY, true },
- {"yrs", 365 * DAY, false},
- {"yr", 365 * DAY, true },
- {"y", 365 * DAY, false},
-};
-
-#define NUM_DURATIONS (sizeof (durations) / sizeof (durations[0]))
-
-////////////////////////////////////////////////////////////////////////////////
-Duration::Duration ()
-{
- clear ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Duration::Duration (const std::string& input)
-{
- clear ();
- std::string::size_type idx = 0;
- parse (input, idx);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Duration::Duration (time_t input)
-{
- clear ();
- _period = input;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Duration::operator< (const Duration& other)
-{
- return _period < other._period;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Duration::operator> (const Duration& other)
-{
- return _period > other._period;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Duration::operator<= (const Duration& other)
-{
- return _period <= other._period;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Duration::operator>= (const Duration& other)
-{
- return _period >= other._period;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Duration::toString () const
-{
- std::stringstream s;
- s << _period;
- return s.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-time_t Duration::toTime_t () const
-{
- return _period;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Duration::parse (const std::string& input, std::string::size_type& start)
-{
- auto i = start;
- Pig pig (input);
- if (i)
- pig.skipN (static_cast <int> (i));
-
- if (Duration::standaloneSecondsEnabled && parse_seconds (pig))
- {
- // ::resolve is not needed in this case.
- start = pig.cursor ();
- return true;
- }
-
- else if (parse_designated (pig) ||
- parse_weeks (pig) ||
- parse_units (pig))
- {
- start = pig.cursor ();
- resolve ();
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Duration::parse_seconds (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- int epoch {};
- if (pig.getDigits (epoch) &&
- ! unicodeLatinAlpha (pig.peek ()) &&
- (epoch == 0 ||
- epoch > 60))
- {
- _period = static_cast <time_t> (epoch);
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// 'P' [nn 'Y'] [nn 'M'] [nn 'D'] ['T' [nn 'H'] [nn 'M'] [nn 'S']]
-bool Duration::parse_designated (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skip ('P') &&
- ! pig.eos ())
- {
- int value;
- pig.save ();
- if (pig.getDigits (value) && pig.skip ('Y'))
- _year = value;
- else
- pig.restore ();
-
- pig.save ();
- if (pig.getDigits (value) && pig.skip ('M'))
- _month = value;
- else
- pig.restore ();
-
- pig.save ();
- if (pig.getDigits (value) && pig.skip ('D'))
- _day = value;
- else
- pig.restore ();
-
- if (pig.skip ('T') &&
- ! pig.eos ())
- {
- pig.save ();
- if (pig.getDigits (value) && pig.skip ('H'))
- _hours = value;
- else
- pig.restore ();
-
- pig.save ();
- if (pig.getDigits (value) && pig.skip ('M'))
- _minutes = value;
- else
- pig.restore ();
-
- pig.save ();
- if (pig.getDigits (value) && pig.skip ('S'))
- _seconds = value;
- else
- pig.restore ();
- }
-
- auto following = pig.peek ();
- if (pig.cursor () - checkpoint >= 3 &&
- ! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// 'P' [nn 'W']
-bool Duration::parse_weeks (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.skip ('P') &&
- ! pig.eos ())
- {
- int value;
- pig.save ();
- if (pig.getDigits (value) && pig.skip ('W'))
- _weeks = value;
- else
- pig.restore ();
-
- auto following = pig.peek ();
- if (pig.cursor () - checkpoint >= 3 &&
- ! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Duration::parse_units (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- // Static and so preserved between calls.
- static std::vector <std::string> units;
- if (units.size () == 0)
- for (unsigned int i = 0; i < NUM_DURATIONS; i++)
- units.push_back (durations[i].unit);
-
- double number;
- std::string unit;
- if (pig.getOneOf (units, unit))
- {
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- for (unsigned int i = 0; i < NUM_DURATIONS; i++)
- {
- if (durations[i].unit == unit &&
- durations[i].standalone)
- {
- _period = static_cast <int> (durations[i].seconds);
- return true;
- }
- }
- }
- else
- pig.restoreTo (checkpoint);
- }
-
- else if (pig.getDecimal (number))
- {
- pig.skipWS ();
- if (pig.getOneOf (units, unit))
- {
- // The "d" unit is a special case, because it is the only one that can
- // legitimately occur at the beginning of a UUID, and be followed by an
- // operator:
- //
- // 1111111d-0000-0000-0000-000000000000
- //
- // Because Lexer::isDuration is higher precedence than Lexer::isUUID,
- // the above UUID looks like:
- //
- // <1111111d> <-> ...
- // duration op ...
- //
- // So as a special case, durations, with units of "d" are rejected if the
- // quantity exceeds 10000.
- //
- if (unit == "d" && number > 10000.0)
- {
- pig.restoreTo (checkpoint);
- return false;
- }
-
- auto following = pig.peek ();
- if (! unicodeLatinAlpha (following) &&
- ! unicodeLatinDigit (following))
- {
- // Linear lookup - should instead be logarithmic.
- double seconds = 1;
- for (unsigned int i = 0; i < NUM_DURATIONS; i++)
- {
- if (durations[i].unit == unit)
- {
- seconds = durations[i].seconds;
- _period = static_cast <int> (number * static_cast <double> (seconds));
- return true;
- }
- }
- }
- }
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Duration::clear ()
-{
- _year = 0;
- _month = 0;
- _weeks = 0;
- _day = 0;
- _hours = 0;
- _minutes = 0;
- _seconds = 0;
- _period = 0;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-const std::string Duration::format () const
-{
- if (_period)
- {
- time_t t = _period;
- int seconds = t % 60; t /= 60;
- int minutes = t % 60; t /= 60;
- int hours = t % 24; t /= 24;
- int days = t;
-
- std::stringstream s;
- if (days)
- s << days << "d ";
-
- s << hours
- << ':'
- << std::setw (2) << std::setfill ('0') << minutes
- << ':'
- << std::setw (2) << std::setfill ('0') << seconds;
-
- return s.str ();
- }
- else
- {
- return "0:00:00";
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-const std::string Duration::formatHours () const
-{
- if (_period)
- {
- time_t t = _period;
- int seconds = t % 60; t /= 60;
- int minutes = t % 60; t /= 60;
- int hours = t;
-
- std::stringstream s;
- s << hours
- << ':'
- << std::setw (2) << std::setfill ('0') << minutes
- << ':'
- << std::setw (2) << std::setfill ('0') << seconds;
-
- return s.str ();
- }
- else
- {
- return "0:00:00";
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-const std::string Duration::formatISO () const
-{
- if (_period)
- {
- time_t t = _period;
- int seconds = t % 60; t /= 60;
- int minutes = t % 60; t /= 60;
- int hours = t % 24; t /= 24;
- int days = t;
-
- std::stringstream s;
- s << 'P';
- if (days) s << days << 'D';
-
- if (hours || minutes || seconds)
- {
- s << 'T';
- if (hours) s << hours << 'H';
- if (minutes) s << minutes << 'M';
- if (seconds) s << seconds << 'S';
- }
-
- return s.str ();
- }
- else
- {
- return "PT0S";
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Range Representation
-// --------- ---------------------
-// >= 365d {n.n}y
-// >= 90d {n}mo
-// >= 14d {n}w
-// >= 1d {n}d
-// >= 1h {n}h
-// >= 1min {n}min
-// {n}s
-//
-const std::string Duration::formatVague (bool padding) const
-{
- float days = (float) _period / 86400.0;
-
- std::stringstream formatted;
- if (_period >= 86400 * 365) formatted << std::fixed << std::setprecision (1) << (days / 365) << (padding ? "y " : "y");
- else if (_period >= 86400 * 90) formatted << static_cast <int> (days / 30) << (padding ? "mo " : "mo");
- else if (_period >= 86400 * 14) formatted << static_cast <int> (days / 7) << (padding ? "w " : "w");
- else if (_period >= 86400) formatted << static_cast <int> (days) << (padding ? "d " : "d");
- else if (_period >= 3600) formatted << static_cast <int> (_period / 3600) << (padding ? "h " : "h");
- else if (_period >= 60) formatted << static_cast <int> (_period / 60) << "min"; // Longest suffix - no padding
- else if (_period >= 1) formatted << static_cast <int> (_period) << (padding ? "s " : "s");
-
- return formatted.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Duration::days () const
-{
- return _period / 86400;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Duration::hours () const
-{
- return _period / 3600;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Duration::minutes () const
-{
- return _period / 60;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Duration::seconds () const
-{
- return _period;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Allow un-normalized values.
-void Duration::resolve ()
-{
- if (! _period)
- {
- if (_weeks)
- _period = (_weeks * 7 * 86400);
- else
- _period = (_year * 365 * 86400) +
- (_month * 30 * 86400) +
- (_day * 86400) +
- (_hours * 3600) +
- (_minutes * 60) +
- _seconds;
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_DURATION
-#define INCLUDED_DURATION
-
-#include <Pig.h>
-#include <string>
-#include <time.h>
-
-class Duration
-{
-public:
- static bool standaloneSecondsEnabled;
-
- Duration ();
- Duration (const std::string&);
- Duration (time_t);
- bool operator< (const Duration&);
- bool operator> (const Duration&);
- bool operator<= (const Duration&);
- bool operator>= (const Duration&);
- std::string toString () const;
- time_t toTime_t () const;
- bool parse (const std::string&, std::string::size_type&);
- bool parse_seconds (Pig&);
- bool parse_designated (Pig&);
- bool parse_weeks (Pig&);
- bool parse_units (Pig&);
- const std::string format () const;
- const std::string formatHours () const;
- const std::string formatISO () const;
- const std::string formatVague (bool padding = false) const;
-
- int days () const;
- int hours () const;
- int minutes () const;
- int seconds () const;
-
-private:
- void clear ();
- void resolve ();
- std::string dump () const;
-
-public:
- int _year {0};
- int _month {0};
- int _weeks {0};
- int _day {0};
- int _hours {0};
- int _minutes {0};
- int _seconds {0};
- time_t _period {0};
-};
-
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <FS.h>
-#include <fstream>
-#include <glob.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdlib.h>
-#include <pwd.h>
-#include <cstdio>
-#include <unistd.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <errno.h>
-#include <string.h>
-#include <shared.h>
-#include <format.h>
-
-#if defined SOLARIS || defined NETBSD || defined FREEBSD || !defined(__GLIBC__)
-#include <limits.h>
-#endif
-
-#if defined __APPLE__
-#include <sys/syslimits.h>
-#endif
-
-// Fixes build with musl libc.
-#ifndef GLOB_TILDE
-#define GLOB_TILDE 0
-#endif
-
-#ifndef GLOB_BRACE
-#define GLOB_BRACE 0
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-Path::Path ()
-{
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Path::Path (const Path& other)
-{
- if (this != &other)
- {
- _original = other._original;
- _data = other._data;
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Path::Path (const std::string& in)
-{
- _original = in;
- _data = expand (in);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Path& Path::operator= (const Path& other)
-{
- if (this != &other)
- {
- this->_original = other._original;
- this->_data = other._data;
- }
-
- return *this;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Path::operator== (const Path& other)
-{
- return _data == other._data;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Path& Path::operator+= (const std::string& dir)
-{
- _data += '/' + dir;
- return *this;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Path::operator std::string () const
-{
- return _data;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Path::name () const
-{
- if (_data.length ())
- {
- auto slash = _data.rfind ('/');
- if (slash != std::string::npos)
- return _data.substr (slash + 1, std::string::npos);
- }
-
- return _data;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Path::parent () const
-{
- if (_data.length ())
- {
- auto slash = _data.rfind ('/');
- if (slash != std::string::npos)
- return _data.substr (0, slash);
- }
-
- return "";
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Path::extension () const
-{
- if (_data.length ())
- {
- auto dot = _data.rfind ('.');
- if (dot != std::string::npos)
- return _data.substr (dot + 1, std::string::npos);
- }
-
- return "";
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Path::exists () const
-{
- return access (_data.c_str (), F_OK) ? false : true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Path::is_directory () const
-{
- if (exists ())
- {
- struct stat s {};
- if (stat (_data.c_str (), &s))
- throw format ("stat error {1}: {2}", errno, strerror (errno));
-
- return S_ISDIR (s.st_mode);
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Path::is_absolute () const
-{
- if (_data.length () && _data[0] == '/')
- return true;
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Path::is_link () const
-{
- struct stat s {};
- if (lstat (_data.c_str (), &s))
- throw format ("lstat error {1}: {2}", errno, strerror (errno));
-
- return S_ISLNK (s.st_mode);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// EACCES is a permissions problem which is exactly what this method is trying
-// to determine.
-bool Path::readable () const
-{
- auto status = access (_data.c_str (), R_OK);
- if (status == -1 && errno != EACCES)
- throw format ("access error {1}: {2}", errno, strerror (errno));
-
- return status ? false : true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// EACCES is a permissions problem which is exactly what this method is trying
-// to determine.
-bool Path::writable () const
-{
- auto status = access (_data.c_str (), W_OK);
- if (status == -1 && errno != EACCES)
- throw format ("access error {1}: {2}", errno, strerror (errno));
-
- return status ? false : true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// EACCES is a permissions problem which is exactly what this method is trying
-// to determine.
-bool Path::executable () const
-{
- auto status = access (_data.c_str (), X_OK);
- if (status == -1 && errno != EACCES)
- throw format ("access error {1}: {2}", errno, strerror (errno));
-
- return status ? false : true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Path::rename (const std::string& new_name)
-{
- auto expanded = expand (new_name);
- if (_data != expanded)
- {
- if (std::rename (_data.c_str (), expanded.c_str ()) == 0)
- {
- _data = expanded;
- return true;
- }
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// ~ --> /home/user
-// ~foo/x --> /home/foo/s
-// ~/x --> /home/foo/x
-// ./x --> $PWD/x
-// x --> $PWD/x
-std::string Path::expand (const std::string& in)
-{
- std::string copy = in;
-
- auto tilde = copy.find ('~');
- std::string::size_type slash;
-
- if (tilde != std::string::npos)
- {
- const char *home = getenv("HOME");
- if (home == nullptr)
- {
- struct passwd* pw = getpwuid (getuid ());
- home = pw->pw_dir;
- }
-
- // Convert: ~ --> /home/user
- if (copy.length () == 1)
- copy = home;
-
- // Convert: ~/x --> /home/user/x
- else if (copy.length () > tilde + 1 &&
- copy[tilde + 1] == '/')
- {
- copy.replace (tilde, 1, home);
- }
-
- // Convert: ~foo/x --> /home/foo/x
- else if ((slash = copy.find ('/', tilde)) != std::string::npos)
- {
- std::string name = copy.substr (tilde + 1, slash - tilde - 1);
- struct passwd* pw = getpwnam (name.c_str ());
- if (pw)
- copy.replace (tilde, slash - tilde, pw->pw_dir);
- }
- }
-
- // Relative paths
- else if (in.length () > 2 &&
- in.substr (0, 2) == "./")
- {
- copy = Directory::cwd () + in.substr (1);
- }
- else if (in.length () > 1 &&
- in[0] != '.' &&
- in[0] != '/')
- {
- copy = Directory::cwd () + '/' + in;
- }
-
- return copy;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::vector <std::string> Path::glob (const std::string& pattern)
-{
- std::vector <std::string> results;
-
- glob_t g;
-#ifdef SOLARIS
- if (!::glob (pattern.c_str (), GLOB_ERR, nullptr, &g))
-#else
- if (!::glob (pattern.c_str (), GLOB_ERR | GLOB_BRACE | GLOB_TILDE, nullptr, &g))
-#endif
- for (int i = 0; i < (int) g.gl_pathc; ++i)
- results.push_back (g.gl_pathv[i]);
-
- globfree (&g);
- return results;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-File::File ()
-: Path::Path ()
-, _fh (nullptr)
-, _h (-1)
-, _locked (false)
-{
-}
-
-////////////////////////////////////////////////////////////////////////////////
-File::File (const Path& other)
-: Path::Path (other)
-, _fh (nullptr)
-, _h (-1)
-, _locked (false)
-{
-}
-
-////////////////////////////////////////////////////////////////////////////////
-File::File (const File& other)
-: Path::Path (other)
-, _fh (nullptr)
-, _h (-1)
-, _locked (false)
-{
-}
-
-////////////////////////////////////////////////////////////////////////////////
-File::File (const std::string& in)
-: Path::Path (in)
-, _fh (nullptr)
-, _h (-1)
-, _locked (false)
-{
-}
-
-////////////////////////////////////////////////////////////////////////////////
-File::~File ()
-{
- if (_fh)
- close ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-File& File::operator= (const File& other)
-{
- if (this != &other)
- Path::operator= (other);
-
- _locked = false;
- return *this;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool File::create (int mode /* = 0640 */)
-{
- if (open ())
- {
- fchmod (_h, mode);
- close ();
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool File::remove () const
-{
- return unlink (_data.c_str ()) == 0 ? true : false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string File::removeBOM (const std::string& input)
-{
- if (input[0] && input[0] == '\xEF' &&
- input[1] && input[1] == '\xBB' &&
- input[2] && input[2] == '\xBF')
- return input.substr (3);
-
- return input;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool File::open ()
-{
- if (_data != "")
- {
- if (! _fh)
- {
- bool already_exists = exists ();
- if (already_exists)
- if (!readable () || !writable ())
- throw std::string (format ("Insufficient permissions for '{1}'.", _data));
-
- _fh = fopen (_data.c_str (), (already_exists ? "r+" : "w+"));
- if (_fh)
- {
- _h = fileno (_fh);
- _locked = false;
- return true;
- }
- else
- throw format ("fopen error {1}: {2}", errno, strerror (errno));
- }
- else
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void File::close ()
-{
- if (_fh)
- {
- if (_locked)
- unlock ();
-
- if (fclose (_fh))
- throw format ("fclose error {1}: {2}", errno, strerror (errno));
-
- _fh = nullptr;
- _h = -1;
- _locked = false;
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool File::lock ()
-{
- _locked = false;
- if (_fh && _h != -1)
- {
-#ifdef FREEBSD
- // l_type l_whence l_start l_len l_pid l_sysid
- struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0, 0 };
-#else
- // l_type l_whence l_start l_len l_pid
- struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0 };
-#endif
- fl.l_pid = getpid ();
- if (fcntl (_h, F_SETLKW, &fl) == 0)
- _locked = true;
- }
-
- return _locked;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void File::unlock ()
-{
- if (_locked)
- {
-#ifdef FREEBSD
- // l_type l_whence l_start l_len l_pid l_sysid
- struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0, 0 };
-#else
- // l_type l_whence l_start l_len l_pid
- struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0 };
-#endif
- fl.l_pid = getpid ();
-
- fcntl (_h, F_SETLK, &fl);
- _locked = false;
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Opens if necessary.
-void File::read (std::string& contents)
-{
- contents = "";
- contents.reserve (size ());
-
- std::ifstream in (_data.c_str ());
- if (in.good ())
- {
- bool first = true;
- std::string line;
- line.reserve (512 * 1024);
- while (getline (in, line))
- {
- // Detect forbidden BOM on first line.
- if (first)
- {
- line = File::removeBOM (line);
- first = false;
- }
-
- contents += line + '\n';
- }
-
- in.close ();
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Opens if necessary.
-void File::read (std::vector <std::string>& contents)
-{
- contents.clear ();
-
- std::ifstream in (_data.c_str ());
- if (in.good ())
- {
- bool first = true;
- std::string line;
- line.reserve (512 * 1024);
- while (getline (in, line))
- {
- // Detect forbidden BOM on first line.
- if (first)
- {
- line = File::removeBOM (line);
- first = false;
- }
-
- contents.push_back (line);
- }
-
- in.close ();
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Opens if necessary.
-void File::append (const std::string& line)
-{
- if (!_fh)
- open ();
-
- if (_fh)
- {
- fseek (_fh, 0, SEEK_END);
-
- if (fputs (line.c_str (), _fh) == EOF)
- throw format ("fputs error {1}: {2}", errno, strerror (errno));
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Opens if necessary.
-void File::append (const std::vector <std::string>& lines)
-{
- if (!_fh)
- open ();
-
- if (_fh)
- {
- fseek (_fh, 0, SEEK_END);
-
- for (auto& line : lines)
- if (fputs (line.c_str (), _fh) == EOF)
- throw format ("fputs error {1}: {2}", errno, strerror (errno));
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void File::write_raw (const std::string& line)
-{
- if (!_fh)
- open ();
-
- if (_fh)
- if (fputs (line.c_str (), _fh) == EOF)
- throw format ("fputs error {1}: {2}", errno, strerror (errno));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void File::truncate ()
-{
- if (!_fh)
- open ();
-
- if (_fh)
- if (ftruncate (_h, 0))
- throw format ("ftruncate error {1}: {2}", errno, strerror (errno));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// S_IFMT 0170000 type of file
-// S_IFIFO 0010000 named pipe (fifo)
-// S_IFCHR 0020000 character special
-// S_IFDIR 0040000 directory
-// S_IFBLK 0060000 block special
-// S_IFREG 0100000 regular
-// S_IFLNK 0120000 symbolic link
-// S_IFSOCK 0140000 socket
-// S_IFWHT 0160000 whiteout
-// S_ISUID 0004000 set user id on execution
-// S_ISGID 0002000 set group id on execution
-// S_ISVTX 0001000 save swapped text even after use
-// S_IRUSR 0000400 read permission, owner
-// S_IWUSR 0000200 write permission, owner
-// S_IXUSR 0000100 execute/search permission, owner
-mode_t File::mode ()
-{
- struct stat s;
- if (stat (_data.c_str (), &s))
- throw format ("stat error {1}: {2}", errno, strerror (errno));
-
- return s.st_mode;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-size_t File::size () const
-{
- struct stat s;
- if (stat (_data.c_str (), &s))
- throw format ("stat error {1}: {2}", errno, strerror (errno));
-
- return s.st_size;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-time_t File::mtime () const
-{
- struct stat s;
- if (stat (_data.c_str (), &s))
- throw format ("stat error {1}: {2}", errno, strerror (errno));
-
- return s.st_mtime;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-time_t File::ctime () const
-{
- struct stat s;
- if (stat (_data.c_str (), &s))
- throw format ("stat error {1}: {2}", errno, strerror (errno));
-
- return s.st_ctime;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-time_t File::btime () const
-{
- struct stat s;
- if (stat (_data.c_str (), &s))
- throw format ("stat error {1}: {2}", errno, strerror (errno));
-
-#ifdef HAVE_ST_BIRTHTIME
- return s.st_birthtime;
-#else
- return s.st_ctime;
-#endif
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool File::create (const std::string& name, int mode /* = 0640 */)
-{
- std::string full_name = expand (name);
- std::ofstream out (full_name.c_str ());
- if (out.good ())
- {
- out.close ();
- if (chmod (full_name.c_str (), mode))
- throw format ("chmod error {1}: {2}", errno, strerror (errno));
-
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool File::read (const std::string& name, std::string& contents)
-{
- contents = "";
-
- std::ifstream in (name.c_str ());
- if (in.good ())
- {
- bool first = true;
- std::string line;
- line.reserve (1024);
- while (getline (in, line))
- {
- // Detect forbidden BOM on first line.
- if (first)
- {
- line = File::removeBOM (line);
- first = false;
- }
-
- contents += line + '\n';
- }
-
- in.close ();
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool File::read (const std::string& name, std::vector <std::string>& contents)
-{
- contents.clear ();
-
- std::ifstream in (name.c_str ());
- if (in.good ())
- {
- bool first = true;
- std::string line;
- line.reserve (1024);
- while (getline (in, line))
- {
- // Detect forbidden BOM on first line.
- if (first)
- {
- line = File::removeBOM (line);
- first = false;
- }
-
- contents.push_back (line);
- }
-
- in.close ();
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool File::write (const std::string& name, const std::string& contents)
-{
- std::ofstream out (expand (name).c_str (),
- std::ios_base::out | std::ios_base::trunc);
- if (out.good ())
- {
- out << contents;
- out.close ();
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool File::write (
- const std::string& name,
- const std::vector <std::string>& lines,
- bool addNewlines /* = true */)
-{
- std::ofstream out (expand (name).c_str (),
- std::ios_base::out | std::ios_base::trunc);
- if (out.good ())
- {
- for (auto& line : lines)
- {
- out << line;
-
- if (addNewlines)
- out << '\n';
- }
-
- out.close ();
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool File::remove (const std::string& name)
-{
- return unlink (expand (name).c_str ()) == 0 ? true : false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool File::copy (const std::string& from, const std::string& to)
-{
- // 'from' must exist.
- if (! access (from.c_str (), F_OK))
- {
- std::ifstream src (from, std::ios::binary);
- std::ofstream dst (to, std::ios::binary);
-
- dst << src.rdbuf ();
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool File::move (const std::string& from, const std::string& to)
-{
- auto expanded = expand (to);
- if (from != expanded)
- if (std::rename (from.c_str (), to.c_str ()) == 0)
- return true;
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Directory::Directory ()
-{
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Directory::Directory (const Directory& other)
-: File::File (other)
-{
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Directory::Directory (const File& other)
-: File::File (other)
-{
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Directory::Directory (const Path& other)
-: File::File (other)
-{
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Directory::Directory (const std::string& in)
-: File::File (in)
-{
-}
-
-////////////////////////////////////////////////////////////////////////////////
-Directory& Directory::operator= (const Directory& other)
-{
- if (this != &other)
- File::operator= (other);
-
- return *this;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Directory::create (int mode /* = 0755 */)
-{
- // No error handling because we want failure to be silent, somewhat emulating
- // "mkdir -p".
- return mkdir (_data.c_str (), mode) == 0 ? true : false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Directory::remove () const
-{
- return remove_directory (_data);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Directory::remove_directory (const std::string& dir) const
-{
- DIR* dp = opendir (dir.c_str ());
- if (dp != nullptr)
- {
- struct dirent* de;
- while ((de = readdir (dp)) != nullptr)
- {
- if (! strcmp (de->d_name, ".") ||
- ! strcmp (de->d_name, ".."))
- continue;
-
-#if defined (SOLARIS) || defined (HAIKU)
- struct stat s;
- if (lstat ((dir + '/' + de->d_name).c_str (), &s))
- throw format ("lstat error {1}: {2}", errno, strerror (errno));
-
- if (S_ISDIR (s.st_mode))
- remove_directory (dir + '/' + de->d_name);
- else
- unlink ((dir + '/' + de->d_name).c_str ());
-#else
- if (de->d_type == DT_UNKNOWN)
- {
- struct stat s;
- if (lstat ((dir + '/' + de->d_name).c_str (), &s))
- throw format ("lstat error {1}: {2}", errno, strerror (errno));
-
- if (S_ISDIR (s.st_mode))
- de->d_type = DT_DIR;
- }
- if (de->d_type == DT_DIR)
- remove_directory (dir + '/' + de->d_name);
- else
- unlink ((dir + '/' + de->d_name).c_str ());
-#endif
- }
-
- closedir (dp);
- }
-
- return rmdir (dir.c_str ()) ? false : true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::vector <std::string> Directory::list ()
-{
- std::vector <std::string> files;
- list (_data, files, false);
- return files;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::vector <std::string> Directory::listRecursive ()
-{
- std::vector <std::string> files;
- list (_data, files, true);
- return files;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Directory::cwd ()
-{
-#ifdef HAVE_GET_CURRENT_DIR_NAME
- char *buf = get_current_dir_name ();
- if (buf == nullptr)
- throw std::bad_alloc ();
- std::string result (buf);
- free (buf);
- return result;
-#else
- char buf[PATH_MAX];
- getcwd (buf, PATH_MAX - 1);
- return std::string (buf);
-#endif
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Directory::up ()
-{
- if (_data == "/")
- return false;
-
- auto slash = _data.rfind ('/');
- if (slash == 0)
- {
- _data = "/"; // Root dir should retain the slash.
- return true;
- }
- else if (slash != std::string::npos)
- {
- _data = _data.substr (0, slash);
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Directory::cd () const
-{
- return chdir (_data.c_str ()) == 0 ? true : false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Directory::list (
- const std::string& base,
- std::vector <std::string>& results,
- bool recursive)
-{
- DIR* dp = opendir (base.c_str ());
- if (dp != nullptr)
- {
- struct dirent* de;
- while ((de = readdir (dp)) != nullptr)
- {
- if (!strcmp (de->d_name, ".") ||
- !strcmp (de->d_name, ".."))
- continue;
-
-#if defined (SOLARIS) || defined (HAIKU)
- struct stat s;
- if (stat ((base + '/' + de->d_name).c_str (), &s))
- throw format ("stat error {1}: {2}", errno, strerror (errno));
-
- if (recursive && S_ISDIR (s.st_mode))
- list (base + '/' + de->d_name, results, recursive);
- else
- results.push_back (base + '/' + de->d_name);
-#else
- if (recursive && de->d_type == DT_UNKNOWN)
- {
- struct stat s;
- if (lstat ((base + '/' + de->d_name).c_str (), &s))
- throw format ("lstat error {1}: {2}", errno, strerror (errno));
-
- if (S_ISDIR (s.st_mode))
- de->d_type = DT_DIR;
- }
- if (recursive && de->d_type == DT_DIR)
- list (base + '/' + de->d_name, results, recursive);
- else
- results.push_back (base + '/' + de->d_name);
-#endif
- }
-
- closedir (dp);
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_FS
-#define INCLUDED_FS
-
-#include <stdio.h>
-#include <string>
-#include <vector>
-#include <sys/stat.h>
-
-#ifndef PATH_MAX
-#define PATH_MAX 4096
-#endif
-
-class Path
-{
-public:
- Path ();
- explicit Path (const Path&);
- Path (const std::string&);
-
- Path& operator= (const Path&);
- bool operator== (const Path&);
- Path& operator+= (const std::string&);
- operator std::string () const;
-
- std::string name () const;
- std::string parent () const;
- std::string extension () const;
- bool exists () const;
- bool is_directory () const;
- bool is_absolute () const;
- bool is_link () const;
- bool readable () const;
- bool writable () const;
- bool executable () const;
- bool rename (const std::string&);
-
- // Statics
- static std::string expand (const std::string&);
- static std::vector<std::string> glob (const std::string&);
-
-public:
- std::string _original;
- std::string _data;
-};
-
-class File : public Path
-{
-public:
- File ();
- explicit File (const Path&);
- explicit File (const File&);
- File (const std::string&);
- virtual ~File ();
-
- File& operator= (const File&);
-
- virtual bool create (int mode = 0640);
- virtual bool remove () const;
-
- bool open ();
- void close ();
-
- bool lock ();
- void unlock ();
-
- void read (std::string&);
- void read (std::vector <std::string>&);
-
- void append (const std::string&);
- void append (const std::vector <std::string>&);
- void write_raw (const std::string&);
-
- void truncate ();
-
- virtual mode_t mode ();
- virtual size_t size () const;
- virtual time_t mtime () const;
- virtual time_t ctime () const;
- virtual time_t btime () const;
-
- static bool create (const std::string&, int mode = 0640);
- static bool read (const std::string&, std::string&);
- static bool read (const std::string&, std::vector <std::string>&);
- static bool write (const std::string&, const std::string&);
- static bool write (const std::string&, const std::vector <std::string>&, bool addNewlines = true);
- static bool remove (const std::string&);
- static bool copy (const std::string&, const std::string&);
- static bool move (const std::string&, const std::string&);
- static std::string removeBOM (const std::string&);
-
-private:
- FILE* _fh;
- int _h;
- bool _locked;
-};
-
-class Directory : public File
-{
-public:
- Directory ();
- explicit Directory (const Directory&);
- explicit Directory (const File&);
- explicit Directory (const Path&);
- Directory (const std::string&);
-
- Directory& operator= (const Directory&);
-
- virtual bool create (int mode = 0755);
- virtual bool remove () const;
-
- std::vector <std::string> list ();
- std::vector <std::string> listRecursive ();
-
- static std::string cwd ();
- bool up ();
- bool cd () const;
-
-private:
- void list (const std::string&, std::vector <std::string>&, bool);
- bool remove_directory (const std::string&) const;
-};
-
-#endif
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <JSON.h>
-#include <shared.h>
-#include <format.h>
-#include <utf8.h>
-
-const char *json_encode[] = {
- "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07",
- "\\b", "\\t", "\\n", "\x0b", "\\f", "\\r", "\x0e", "\x0f",
- "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17",
- "\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f",
- "\x20", "\x21", "\\\"", "\x23", "\x24", "\x25", "\x26", "\x27",
- "\x28", "\x29", "\x2a", "\x2b", "\x2c", "\x2d", "\x2e", "\\/",
- "\x30", "\x31", "\x32", "\x33", "\x34", "\x35", "\x36", "\x37",
- "\x38", "\x39", "\x3a", "\x3b", "\x3c", "\x3d", "\x3e", "\x3f",
- "\x40", "\x41", "\x42", "\x43", "\x44", "\x45", "\x46", "\x47",
- "\x48", "\x49", "\x4a", "\x4b", "\x4c", "\x4d", "\x4e", "\x4f",
- "\x50", "\x51", "\x52", "\x53", "\x54", "\x55", "\x56", "\x57",
- "\x58", "\x59", "\x5a", "\x5b", "\\\\", "\x5d", "\x5e", "\x5f",
- "\x60", "\x61", "\x62", "\x63", "\x64", "\x65", "\x66", "\x67",
- "\x68", "\x69", "\x6a", "\x6b", "\x6c", "\x6d", "\x6e", "\x6f",
- "\x70", "\x71", "\x72", "\x73", "\x74", "\x75", "\x76", "\x77",
- "\x78", "\x79", "\x7a", "\x7b", "\x7c", "\x7d", "\x7e", "\x7f",
- "\x80", "\x81", "\x82", "\x83", "\x84", "\x85", "\x86", "\x87",
- "\x88", "\x89", "\x8a", "\x8b", "\x8c", "\x8d", "\x8e", "\x8f",
- "\x90", "\x91", "\x92", "\x93", "\x94", "\x95", "\x96", "\x97",
- "\x98", "\x99", "\x9a", "\x9b", "\x9c", "\x9d", "\x9e", "\x9f",
- "\xa0", "\xa1", "\xa2", "\xa3", "\xa4", "\xa5", "\xa6", "\xa7",
- "\xa8", "\xa9", "\xaa", "\xab", "\xac", "\xad", "\xae", "\xaf",
- "\xb0", "\xb1", "\xb2", "\xb3", "\xb4", "\xb5", "\xb6", "\xb7",
- "\xb8", "\xb9", "\xba", "\xbb", "\xbc", "\xbd", "\xbe", "\xbf",
- "\xc0", "\xc1", "\xc2", "\xc3", "\xc4", "\xc5", "\xc6", "\xc7",
- "\xc8", "\xc9", "\xca", "\xcb", "\xcc", "\xcd", "\xce", "\xcf",
- "\xd0", "\xd1", "\xd2", "\xd3", "\xd4", "\xd5", "\xd6", "\xd7",
- "\xd8", "\xd9", "\xda", "\xdb", "\xdc", "\xdd", "\xde", "\xdf",
- "\xe0", "\xe1", "\xe2", "\xe3", "\xe4", "\xe5", "\xe6", "\xe7",
- "\xe8", "\xe9", "\xea", "\xeb", "\xec", "\xed", "\xee", "\xef",
- "\xf0", "\xf1", "\xf2", "\xf3", "\xf4", "\xf5", "\xf6", "\xf7",
- "\xf8", "\xf9", "\xfa", "\xfb", "\xfc", "\xfd", "\xfe", "\xff"
-};
-
-////////////////////////////////////////////////////////////////////////////////
-json::value* json::value::parse (Pig& pig)
-{
- json::value* v;
- if ((v = json::object::parse (pig)) ||
- (v = json::array::parse (pig)) ||
- (v = json::string::parse (pig)) ||
- (v = json::number::parse (pig)) ||
- (v = json::literal::parse (pig)))
- return v;
-
- return NULL;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-json::jtype json::value::type ()
-{
- return json::j_value;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string json::value::dump () const
-{
- return "<value>";
-}
-
-////////////////////////////////////////////////////////////////////////////////
-json::string::string (const std::string& other)
-{
- _data = other;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-json::string* json::string::parse (Pig& pig)
-{
- std::string value;
- if (pig.getQuoted ('"', value))
- {
- json::string* s = new json::string ();
- s->_data = value;
- return s;
- }
-
- return NULL;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-json::jtype json::string::type ()
-{
- return json::j_string;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string json::string::dump () const
-{
- return std::string ("\"") + _data + '"';
-}
-
-////////////////////////////////////////////////////////////////////////////////
-json::number* json::number::parse (Pig& pig)
-{
- double d;
- if (pig.getNumber (d))
- {
- json::number* s = new json::number ();
- s->_dvalue = d;
- return s;
- }
-
- return NULL;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-json::jtype json::number::type ()
-{
- return json::j_number;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string json::number::dump () const
-{
- return format (_dvalue);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-json::number::operator double () const
-{
- return _dvalue;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-json::literal* json::literal::parse (Pig& pig)
-{
- if (pig.skipLiteral ("null"))
- {
- json::literal* s = new json::literal ();
- s->_lvalue = nullvalue;
- return s;
- }
- else if (pig.skipLiteral ("false"))
- {
- json::literal* s = new json::literal ();
- s->_lvalue = falsevalue;
- return s;
- }
- else if (pig.skipLiteral ("true"))
- {
- json::literal* s = new json::literal ();
- s->_lvalue = truevalue;
- return s;
- }
-
- return NULL;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-json::jtype json::literal::type ()
-{
- return json::j_literal;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string json::literal::dump () const
-{
- if (_lvalue == nullvalue) return "null";
- else if (_lvalue == falsevalue) return "false";
- else return "true";
-}
-
-////////////////////////////////////////////////////////////////////////////////
-json::array::~array ()
-{
- for (auto& i : _data)
- delete i;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-json::array* json::array::parse (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- pig.skipWS ();
- if (pig.skip ('['))
- {
- pig.skipWS ();
-
- json::array* arr = new json::array ();
-
- json::value* value;
- if ((value = json::value::parse (pig)))
- {
- arr->_data.push_back (value);
- value = NULL; // Not a leak. Looks like a leak.
- pig.skipWS ();
- while (pig.skip (','))
- {
- pig.skipWS ();
-
- if ((value = json::value::parse (pig)))
- {
- arr->_data.push_back (value);
- pig.skipWS ();
- }
- else
- {
- delete arr;
- throw format ("Error: missing value after ',' at position {1}", (int) pig.cursor ());
- }
- }
- }
-
- if (pig.skip (']'))
- return arr;
- else
- throw format ("Error: missing ']' at position {1}", (int) pig.cursor ());
-
- delete arr;
- }
-
- pig.restoreTo (checkpoint);
- return NULL;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-json::jtype json::array::type ()
-{
- return json::j_array;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string json::array::dump () const
-{
- std::string output;
- output += '[';
-
- for (auto i = _data.begin (); i != _data.end (); ++i)
- {
- if (i != _data.begin ())
- output += ',';
-
- output += (*i)->dump ();
- }
-
- output += ']';
- return output;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-json::object::~object ()
-{
- for (auto& i : _data)
- delete i.second;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-json::object* json::object::parse (Pig& pig)
-{
- auto checkpoint = pig.cursor ();
-
- pig.skipWS ();
- if (pig.skip ('{'))
- {
- pig.skipWS ();
-
- json::object* obj = new json::object ();
-
- std::string name;
- json::value* value;
- if (json::object::parse_pair (pig, name, value))
- {
- obj->_data.insert (std::pair <std::string, json::value*> (name, value));
- value = NULL; // Not a leak. Looks like a leak.
-
- pig.skipWS ();
- while (pig.skip (','))
- {
- pig.skipWS ();
-
- if (json::object::parse_pair (pig, name, value))
- {
- obj->_data.insert (std::pair <std::string, json::value*> (name, value));
- pig.skipWS ();
- }
- else
- {
- delete obj;
- throw format ("Error: missing value after ',' at position {1}", (int) pig.cursor ());
- }
- }
- }
-
- if (pig.skip ('}'))
- return obj;
- else
- throw format ("Error: missing '}' at position {1}", (int) pig.cursor ());
-
- delete obj;
- }
-
- pig.restoreTo (checkpoint);
- return NULL;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool json::object::parse_pair (
- Pig& pig,
- std::string& name,
- json::value*& val)
-{
- auto checkpoint = pig.cursor ();
-
- if (pig.getQuoted ('"', name))
- {
- pig.skipWS ();
- if (pig.skip (':'))
- {
- pig.skipWS ();
- if ((val = json::value::parse (pig)))
- return true;
- else
- throw format ("Error: missing value at position {1}", (int) pig.cursor ());
- }
- else
- throw format ("Error: missing ':' at position {1}", (int) pig.cursor ());
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-json::jtype json::object::type ()
-{
- return json::j_object;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string json::object::dump () const
-{
- std::string output;
- output += '{';
-
- for (auto i = _data.begin (); i != _data.end (); ++i)
- {
- if (i != _data.begin ())
- output += ',';
-
- output += '"' + i->first + "\":";
- output += i->second->dump ();
- }
-
- output += '}';
- return output;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-json::value* json::parse (const std::string& input)
-{
- json::value* root = NULL;
-
- Pig n (input);
- n.skipWS ();
-
- if (n.peek () == '{') root = json::object::parse (n);
- else if (n.peek () == '[') root = json::array::parse (n);
- else
- throw format ("Error: expected '{' or '[' at position {1}", (int) n.cursor ());
-
- // Check for end condition.
- n.skipWS ();
- if (!n.eos ())
- {
- delete root;
- throw format ("Error: extra characters found at position {1}", (int) n.cursor ());
- }
-
- return root;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string json::encode (const std::string& input)
-{
- std::string output;
- output.reserve ((input.size () * 6) / 5); // 20% increase.
-
- auto last = input.begin ();
- for (auto i = input.begin (); i != input.end (); ++i)
- {
- switch (*i)
- {
- // Simple translations.
- case '"':
- case '\\':
- case '/':
- case '\b':
- case '\f':
- case '\n':
- case '\r':
- case '\t':
- output.append (last, i);
- output += json_encode[(unsigned char)(*i)];
- last = i + 1;
-
- // Default NOP.
- }
- }
-
- output.append (last, input.end ());
-
- return output;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string json::decode (const std::string& input)
-{
- std::string output;
- output.reserve (input.size ()); // Same size.
-
- size_t pos = 0;
-
- while (pos < input.length ())
- {
- if (input[pos] == '\\')
- {
- ++pos;
- switch (input[pos])
- {
- // Simple translations.
- case '"': output += '"'; break;
- case '\\': output += '\\'; break;
- case '/': output += '/'; break;
- case 'b': output += '\b'; break;
- case 'f': output += '\f'; break;
- case 'n': output += '\n'; break;
- case 'r': output += '\r'; break;
- case 't': output += '\t'; break;
-
- // Compose a UTF8 unicode character.
- case 'u':
- output += utf8_character (utf8_codepoint (input.substr (++pos)));
- pos += 3;
- break;
-
- // If it is an unrecognized sequence, do nothing.
- default:
- output += '\\';
- output += input[pos];
- break;
- }
- ++pos;
- }
- else
- {
- size_t next_backslash = input.find ('\\', pos);
- output.append (input, pos, next_backslash - pos);
- pos = next_backslash;
- }
- }
-
- return output;
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_JSON
-#define INCLUDED_JSON
-
-#include <map>
-#include <vector>
-#include <string>
-#include <Pig.h>
-
-namespace json
-{
- enum jtype
- {
- j_value, // 0
- j_object, // 1
- j_array, // 2
- j_string, // 3
- j_number, // 4
- j_literal // 5
- };
-
- class value
- {
- public:
- value () {}
- virtual ~value () {}
- static value* parse (Pig&);
- virtual jtype type ();
- virtual std::string dump () const;
- };
-
- class string : public value
- {
- public:
- string () {}
- string (const std::string&);
- ~string () {}
- static string* parse (Pig&);
- jtype type ();
- std::string dump () const;
-
- public:
- std::string _data;
- };
-
- class number : public value
- {
- public:
- number () : _dvalue (0.0) {}
- ~number () {}
- static number* parse (Pig&);
- jtype type ();
- std::string dump () const;
- operator double () const;
-
- public:
- double _dvalue;
- };
-
- class literal : public value
- {
- public:
- literal () : _lvalue (none) {}
- ~literal () {}
- static literal* parse (Pig&);
- jtype type ();
- std::string dump () const;
-
- public:
- enum literal_value {none, nullvalue, falsevalue, truevalue};
- literal_value _lvalue;
- };
-
- class array : public value
- {
- public:
- array () {}
- ~array ();
- static array* parse (Pig&);
- jtype type ();
- std::string dump () const;
-
- public:
- std::vector <value*> _data;
- };
-
- class object : public value
- {
- public:
- object () {}
- ~object ();
- static object* parse (Pig&);
- static bool parse_pair (Pig&, std::string&, value*&);
- jtype type ();
- std::string dump () const;
-
- public:
- std::map <std::string, value*> _data;
- };
-
- // Parser entry point.
- value* parse (const std::string&);
-
- // Encode/decode for JSON entities.
- std::string encode (const std::string&);
- std::string decode (const std::string&);
-
- class SAX
- {
- public:
- class Sink
- {
- public:
- virtual void eventDocStart () {}
- virtual void eventDocEnd () {}
- virtual void eventObjectStart () {}
- virtual void eventObjectEnd (int) {}
- virtual void eventArrayStart () {}
- virtual void eventArrayEnd (int) {}
- virtual void eventName (const std::string&) {}
- virtual void eventValueNull () {}
- virtual void eventValueBool (bool) {}
- virtual void eventValueInt (int64_t) {}
- virtual void eventValueUint (uint64_t) {}
- virtual void eventValueDouble (double) {}
- virtual void eventValueString (const std::string&) {}
- };
-
- bool parse (const std::string&, json::SAX::Sink&);
-
- private:
- void ignoreWhitespace (const std::string&, std::string::size_type&);
- bool isObject (const std::string&, std::string::size_type&, SAX::Sink&);
- bool isArray (const std::string&, std::string::size_type&, SAX::Sink&);
- bool isPair (const std::string&, std::string::size_type&, SAX::Sink&);
- bool isValue (const std::string&, std::string::size_type&, SAX::Sink&);
- bool isKey (const std::string&, std::string::size_type&, SAX::Sink&);
- bool isString (const std::string&, std::string::size_type&, SAX::Sink&);
- bool isStringValue (const std::string&, std::string::size_type&, std::string&);
- bool isNumber (const std::string&, std::string::size_type&, SAX::Sink&);
- bool isInt (const std::string&, std::string::size_type&, std::string&);
- bool isFrac (const std::string&, std::string::size_type&, std::string&);
- bool isDigits (const std::string&, std::string::size_type&);
- bool isDecDigit (int);
- bool isHexDigit (int);
- int hexToInt (int);
- int hexToInt (int, int, int, int);
- bool isExp (const std::string&, std::string::size_type&, std::string&);
- bool isE (const std::string&, std::string::size_type&);
- bool isBool (const std::string&, std::string::size_type&, SAX::Sink&);
- bool isNull (const std::string&, std::string::size_type&, SAX::Sink&);
- bool isLiteral (const std::string&, char, std::string::size_type&);
- void error (const std::string&, std::string::size_type);
- };
-}
-
-#endif
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2013 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <Lexer.h>
-#include <Datetime.h>
-#include <Duration.h>
-#include <algorithm>
-#include <tuple>
-#include <ctype.h>
-#include <unicode.h>
-#include <utf8.h>
-
-static const std::string uuid_pattern = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
-static const unsigned int uuid_min_length = 8;
-
-std::string Lexer::dateFormat = "";
-
-////////////////////////////////////////////////////////////////////////////////
-Lexer::Lexer (const std::string& text)
-: _text (text)
-, _eos (text.size ())
-{
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// When a Lexer object is constructed with a string, this method walks through
-// the stream of low-level tokens.
-bool Lexer::token (std::string& token, Lexer::Type& type)
-{
- // Eat white space.
- while (unicodeWhitespace (_text[_cursor]))
- utf8_next_char (_text, _cursor);
-
- // Terminate at EOS.
- if (isEOS ())
- return false;
-
- if (isString (token, type, "'\"") ||
- isUUID (token, type, true) ||
- isDate (token, type) ||
- isDuration (token, type) ||
- isURL (token, type) ||
- isHexNumber (token, type) ||
- isNumber (token, type) ||
- isPath (token, type) ||
- isPattern (token, type) ||
- isOperator (token, type) ||
- isWord (token, type))
- return true;
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::vector <std::tuple <std::string, Lexer::Type>> Lexer::tokenize (const std::string& input)
-{
- std::vector <std::tuple <std::string, Lexer::Type>> tokens;
-
- std::string token;
- Lexer::Type type;
- Lexer lexer (input);
- while (lexer.token (token, type))
- tokens.push_back (std::make_tuple (token, type));
-
- return tokens;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// No L10N - these are for internal purposes.
-const std::string Lexer::typeName (const Lexer::Type& type)
-{
- switch (type)
- {
- case Lexer::Type::uuid: return "uuid";
- case Lexer::Type::number: return "number";
- case Lexer::Type::hex: return "hex";
- case Lexer::Type::string: return "string";
- case Lexer::Type::url: return "url";
- case Lexer::Type::path: return "path";
- case Lexer::Type::pattern: return "pattern";
- case Lexer::Type::op: return "op";
- case Lexer::Type::word: return "word";
- case Lexer::Type::date: return "date";
- case Lexer::Type::duration: return "duration";
- }
-
- return "unknown";
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Lexer::Type::number
-// \d+
-// [ . \d+ ]
-// [ e|E [ +|- ] \d+ [ . \d+ ] ]
-// not followed by non-operator.
-bool Lexer::isNumber (std::string& token, Lexer::Type& type)
-{
- std::size_t marker = _cursor;
-
- if (unicodeLatinDigit (_text[marker]))
- {
- ++marker;
- while (unicodeLatinDigit (_text[marker]))
- utf8_next_char (_text, marker);
-
- if (_text[marker] == '.')
- {
- ++marker;
- if (unicodeLatinDigit (_text[marker]))
- {
- ++marker;
- while (unicodeLatinDigit (_text[marker]))
- utf8_next_char (_text, marker);
- }
- }
-
- if (_text[marker] == 'e' ||
- _text[marker] == 'E')
- {
- ++marker;
-
- if (_text[marker] == '+' ||
- _text[marker] == '-')
- ++marker;
-
- if (unicodeLatinDigit (_text[marker]))
- {
- ++marker;
- while (unicodeLatinDigit (_text[marker]))
- utf8_next_char (_text, marker);
-
- if (_text[marker] == '.')
- {
- ++marker;
- if (unicodeLatinDigit (_text[marker]))
- {
- ++marker;
- while (unicodeLatinDigit (_text[marker]))
- utf8_next_char (_text, marker);
- }
- }
- }
- }
-
- // Lookahread: !<unicodeWhitespace> | !<isSingleCharOperator>
- // If there is an immediately consecutive character, that is not an operator, fail.
- if (_eos > marker &&
- ! unicodeWhitespace (_text[marker]) &&
- ! isSingleCharOperator (_text[marker]))
- return false;
-
- token = _text.substr (_cursor, marker - _cursor);
- type = Lexer::Type::number;
- _cursor = marker;
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Lexer::Type::number
-// \d+
-bool Lexer::isInteger (std::string& token, Lexer::Type& type)
-{
- std::size_t marker = _cursor;
-
- if (unicodeLatinDigit (_text[marker]))
- {
- ++marker;
- while (unicodeLatinDigit (_text[marker]))
- utf8_next_char (_text, marker);
-
- token = _text.substr (_cursor, marker - _cursor);
- type = Lexer::Type::number;
- _cursor = marker;
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Lexer::isSingleCharOperator (int c)
-{
- return c == '+' || // Addition
- c == '-' || // Subtraction or unary minus = ambiguous
- c == '*' || // Multiplication
- c == '/' || // Diviѕion
- c == '(' || // Precedence open parenthesis
- c == ')' || // Precedence close parenthesis
- c == '<' || // Less than
- c == '>' || // Greater than
- c == '^' || // Exponent
- c == '!' || // Unary not
- c == '%' || // Modulus
- c == '=' || // Partial match
- c == '~'; // Pattern match
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Lexer::isDoubleCharOperator (int c0, int c1, int c2)
-{
- return (c0 == '=' && c1 == '=') ||
- (c0 == '!' && c1 == '=') ||
- (c0 == '<' && c1 == '=') ||
- (c0 == '>' && c1 == '=') ||
- (c0 == 'o' && c1 == 'r' && isBoundary (c1, c2)) ||
- (c0 == '|' && c1 == '|') ||
- (c0 == '&' && c1 == '&') ||
- (c0 == '!' && c1 == '~');
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Lexer::isTripleCharOperator (int c0, int c1, int c2, int c3)
-{
- return (c0 == 'a' && c1 == 'n' && c2 == 'd' && isBoundary (c2, c3)) ||
- (c0 == 'x' && c1 == 'o' && c2 == 'r' && isBoundary (c2, c3)) ||
- (c0 == '!' && c1 == '=' && c2 == '=');
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Lexer::isBoundary (int left, int right)
-{
- // EOS
- if (right == '\0') return true;
-
- // XOR
- if (unicodeLatinAlpha (left) != unicodeLatinAlpha (right)) return true;
- if (unicodeLatinDigit (left) != unicodeLatinDigit (right)) return true;
- if (unicodeWhitespace (left) != unicodeWhitespace (right)) return true;
-
- // OR
- if (isPunctuation (left) || isPunctuation (right)) return true;
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Lexer::isHardBoundary (int left, int right)
-{
- // EOS
- if (right == '\0')
- return true;
-
- // FILTER operators that don't need to be surrounded by whitespace.
- if (left == '(' ||
- left == ')' ||
- right == '(' ||
- right == ')')
- return true;
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Lexer::isPunctuation (int c)
-{
- return isprint (c) &&
- c != ' ' &&
- c != '@' &&
- c != '#' &&
- c != '$' &&
- c != '_' &&
- ! unicodeLatinDigit (c) &&
- ! unicodeLatinAlpha (c);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Assumes that quotes is a string containing a non-trivial set of quote
-// characters.
-std::string Lexer::dequote (const std::string& input, const std::string& quotes)
-{
- if (input.length () > 1)
- {
- int quote = input[0];
- if (quotes.find (quote) != std::string::npos)
- {
- size_t len = input.length ();
- if (quote == input[len - 1])
- return input.substr (1, len - 2);
- }
- }
-
- return input;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Detects characters in an input string that indicate quotes were required, or
-// escapes, to get them past the shell.
-bool Lexer::wasQuoted (const std::string& input)
-{
- if (input.find_first_of (" \t()<>&~") != std::string::npos)
- return true;
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Lexer::isEOS () const
-{
- return _cursor >= _eos;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Converts '0' -> 0
-// '9' -> 9
-// 'a'/'A' -> 10
-// 'f'/'F' -> 15
-int Lexer::hexToInt (int c)
-{
- if (c >= '0' && c <= '9') return (c - '0');
- else if (c >= 'a' && c <= 'f') return (c - 'a' + 10);
- else return (c - 'A' + 10);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Lexer::hexToInt (int c0, int c1)
-{
- return (hexToInt (c0) << 4) + hexToInt (c1);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Lexer::hexToInt (int c0, int c1, int c2, int c3)
-{
- return (hexToInt (c0) << 12) +
- (hexToInt (c1) << 8) +
- (hexToInt (c2) << 4) +
- hexToInt (c3);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Lexer::trimLeft (const std::string& in, const std::string& t /*= " "*/)
-{
- std::string::size_type ws = in.find_first_not_of (t);
- if (ws > 0)
- {
- std::string out {in};
- return out.erase (0, ws);
- }
-
- return in;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Lexer::trimRight (const std::string& in, const std::string& t /*= " "*/)
-{
- std::string out {in};
- return out.erase (in.find_last_not_of (t) + 1);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Lexer::trim (const std::string& in, const std::string& t /*= " "*/)
-{
- return trimLeft (trimRight (in, t), t);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Lexer::Type::string
-// '|"
-// [ U+XXXX | \uXXXX | \" | \' | \\ | \/ | \b | \f | \n | \r | \t | . ]
-// '|"
-bool Lexer::isString (std::string& token, Lexer::Type& type, const std::string& quotes)
-{
- if (_enableString)
- {
- std::size_t marker = _cursor;
- if (readWord (_text, quotes, marker, token))
- {
- type = Lexer::Type::string;
- _cursor = marker;
- return true;
- }
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Lexer::Type::date
-// <Datetime> (followed by eos, WS, operator)
-bool Lexer::isDate (std::string& token, Lexer::Type& type)
-{
- if (_enableDate)
- {
- // Try an ISO date parse.
- std::size_t i = _cursor;
- Datetime d;
- if (d.parse (_text, i, Lexer::dateFormat) &&
- (i >= _eos ||
- unicodeWhitespace (_text[i]) ||
- isSingleCharOperator (_text[i])))
- {
- type = Lexer::Type::date;
- token = _text.substr (_cursor, i - _cursor);
- _cursor = i;
- return true;
- }
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Lexer::Type::duration
-// <Duration> (followed by eos, WS, operator)
-bool Lexer::isDuration (std::string& token, Lexer::Type& type)
-{
- if (_enableDuration)
- {
- std::size_t marker = _cursor;
-
- std::string extractedToken;
- Lexer::Type extractedType;
- if (isOperator(extractedToken, extractedType))
- {
- _cursor = marker;
- return false;
- }
-
- marker = _cursor;
- Duration dur;
- if (dur.parse (_text, marker) &&
- (marker >= _eos ||
- unicodeWhitespace (_text[marker]) ||
- isSingleCharOperator (_text[marker])))
- {
- type = Lexer::Type::duration;
- token = _text.substr (_cursor, marker - _cursor);
- _cursor = marker;
- return true;
- }
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Lexer::Type::uuid
-// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
-// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX
-// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX
-// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXX
-// ...
-// XXXXXXXX-XX
-// XXXXXXXX-X
-// XXXXXXXX-
-// XXXXXXXX
-// Followed only by EOS, whitespace, or single character operator.
-bool Lexer::isUUID (std::string& token, Lexer::Type& type, bool endBoundary)
-{
- if (_enableUUID)
- {
- std::size_t marker = _cursor;
-
- // Greedy.
- std::size_t i = 0;
- for (; i < 36 && marker + i < _eos; i++)
- {
- if (uuid_pattern[i] == 'x')
- {
- if (! unicodeHexDigit (_text[marker + i]))
- break;
- }
- else if (uuid_pattern[i] != _text[marker + i])
- break;
- }
-
- if (i >= uuid_min_length &&
- (! endBoundary ||
- ! _text[marker + i] ||
- unicodeWhitespace (_text[marker + i]) ||
- isSingleCharOperator (_text[marker + i])))
- {
- token = _text.substr (_cursor, i);
- type = Lexer::Type::uuid;
- _cursor += i;
- return true;
- }
-
- }
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Lexer::Type::hex
-// 0xX+
-bool Lexer::isHexNumber (std::string& token, Lexer::Type& type)
-{
- if (_enableHexNumber)
- {
- std::size_t marker = _cursor;
-
- if (_eos - marker >= 3 &&
- _text[marker + 0] == '0' &&
- _text[marker + 1] == 'x')
- {
- marker += 2;
-
- while (unicodeHexDigit (_text[marker]))
- ++marker;
-
- if (marker - _cursor > 2)
- {
- token = _text.substr (_cursor, marker - _cursor);
- type = Lexer::Type::hex;
- _cursor = marker;
- return true;
- }
- }
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Lexer::Type::word
-// [^\s]+
-bool Lexer::isWord (std::string& token, Lexer::Type& type)
-{
- if (_enableWord)
- {
- std::size_t marker = _cursor;
-
- while (_text[marker] &&
- ! unicodeWhitespace (_text[marker]) &&
- (! _enableOperator || ! isSingleCharOperator (_text[marker])))
- utf8_next_char (_text, marker);
-
- if (marker > _cursor)
- {
- token = _text.substr (_cursor, marker - _cursor);
- type = Lexer::Type::word;
- _cursor = marker;
- return true;
- }
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Lexer::Type::url
-// http [s] :// ...
-bool Lexer::isURL (std::string& token, Lexer::Type& type)
-{
- if (_enableURL)
- {
- std::size_t marker = _cursor;
-
- if (_eos - _cursor > 9 && // length 'https://*'
- (_text[marker + 0] == 'h' || _text[marker + 0] == 'H') &&
- (_text[marker + 1] == 't' || _text[marker + 1] == 'T') &&
- (_text[marker + 2] == 't' || _text[marker + 2] == 'T') &&
- (_text[marker + 3] == 'p' || _text[marker + 3] == 'P'))
- {
- marker += 4;
- if (_text[marker + 0] == 's' || _text[marker + 0] == 'S')
- ++marker;
-
- if (_text[marker + 0] == ':' &&
- _text[marker + 1] == '/' &&
- _text[marker + 2] == '/')
- {
- marker += 3;
-
- while (marker < _eos &&
- ! unicodeWhitespace (_text[marker]))
- utf8_next_char (_text, marker);
-
- token = _text.substr (_cursor, marker - _cursor);
- type = Lexer::Type::url;
- _cursor = marker;
- return true;
- }
- }
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Lexer::Type::path
-// ( / <non-slash, non-whitespace> )+
-bool Lexer::isPath (std::string& token, Lexer::Type& type)
-{
- if (_enablePath)
- {
- std::size_t marker = _cursor;
- int slashCount = 0;
-
- while (1)
- {
- if (_text[marker] == '/')
- {
- ++marker;
- ++slashCount;
- }
- else
- break;
-
- if (_text[marker] &&
- ! unicodeWhitespace (_text[marker]) &&
- _text[marker] != '/')
- {
- utf8_next_char (_text, marker);
- while (_text[marker] &&
- ! unicodeWhitespace (_text[marker]) &&
- _text[marker] != '/')
- utf8_next_char (_text, marker);
- }
- else
- break;
- }
-
- if (marker > _cursor &&
- slashCount > 3)
- {
- type = Lexer::Type::path;
- token = _text.substr (_cursor, marker - _cursor);
- _cursor = marker;
- return true;
- }
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Lexer::Type::pattern
-// / <unquoted-string> / <EOS> | <unicodeWhitespace>
-bool Lexer::isPattern (std::string& token, Lexer::Type& type)
-{
- if (_enablePattern)
- {
- std::size_t marker = _cursor;
-
- std::string word;
- if (readWord (_text, "/", _cursor, word) &&
- (isEOS () ||
- unicodeWhitespace (_text[_cursor])))
- {
- token = _text.substr (marker, _cursor - marker);
- type = Lexer::Type::pattern;
- return true;
- }
-
- _cursor = marker;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Lexer::Type::op
-// _hastag_ | _notag | _neg_ | _pos_ |
-// <isTripleCharOperator> |
-// <isDoubleCharOperator> |
-// <isSingleCharOperator> |
-bool Lexer::isOperator (std::string& token, Lexer::Type& type)
-{
- if (_enableOperator)
- {
- std::size_t marker = _cursor;
-
- if (_eos - marker >= 8 && _text.substr (marker, 8) == "_hastag_")
- {
- marker += 8;
- type = Lexer::Type::op;
- token = _text.substr (_cursor, marker - _cursor);
- _cursor = marker;
- return true;
- }
-
- else if (_eos - marker >= 7 && _text.substr (marker, 7) == "_notag_")
- {
- marker += 7;
- type = Lexer::Type::op;
- token = _text.substr (_cursor, marker - _cursor);
- _cursor = marker;
- return true;
- }
-
- else if (_eos - marker >= 5 && _text.substr (marker, 5) == "_neg_")
- {
- marker += 5;
- type = Lexer::Type::op;
- token = _text.substr (_cursor, marker - _cursor);
- _cursor = marker;
- return true;
- }
-
- else if (_eos - marker >= 5 && _text.substr (marker, 5) == "_pos_")
- {
- marker += 5;
- type = Lexer::Type::op;
- token = _text.substr (_cursor, marker - _cursor);
- _cursor = marker;
- return true;
- }
-
- else if (_eos - marker >= 3 &&
- isTripleCharOperator (_text[marker], _text[marker + 1], _text[marker + 2], _text[marker + 3]))
- {
- marker += 3;
- type = Lexer::Type::op;
- token = _text.substr (_cursor, marker - _cursor);
- _cursor = marker;
- return true;
- }
-
- else if (_eos - marker >= 2 &&
- isDoubleCharOperator (_text[marker], _text[marker + 1], _text[marker + 2]))
- {
- marker += 2;
- type = Lexer::Type::op;
- token = _text.substr (_cursor, marker - _cursor);
- _cursor = marker;
- return true;
- }
-
- else if (isSingleCharOperator (_text[marker]))
- {
- token = _text[marker];
- type = Lexer::Type::op;
- _cursor = ++marker;
- return true;
- }
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Static
-std::string Lexer::typeToString (Lexer::Type type)
-{
- if (type == Lexer::Type::string) return std::string ("\033[38;5;7m\033[48;5;3m") + "string" + "\033[0m";
- else if (type == Lexer::Type::uuid) return std::string ("\033[38;5;7m\033[48;5;10m") + "uuid" + "\033[0m";
- else if (type == Lexer::Type::hex) return std::string ("\033[38;5;7m\033[48;5;14m") + "hex" + "\033[0m";
- else if (type == Lexer::Type::number) return std::string ("\033[38;5;7m\033[48;5;6m") + "number" + "\033[0m";
- else if (type == Lexer::Type::url) return std::string ("\033[38;5;7m\033[48;5;4m") + "url" + "\033[0m";
- else if (type == Lexer::Type::path) return std::string ("\033[37;102m") + "path" + "\033[0m";
- else if (type == Lexer::Type::pattern) return std::string ("\033[37;42m") + "pattern" + "\033[0m";
- else if (type == Lexer::Type::op) return std::string ("\033[38;5;7m\033[48;5;203m") + "op" + "\033[0m";
- else if (type == Lexer::Type::word) return std::string ("\033[38;5;15m\033[48;5;236m") + "word" + "\033[0m";
- else if (type == Lexer::Type::date) return std::string ("\033[38;5;15m\033[48;5;34m") + "date" + "\033[0m";
- else if (type == Lexer::Type::duration) return std::string ("\033[38;5;15m\033[48;5;34m") + "duration" + "\033[0m";
- else return std::string ("\033[37;41m") + "unknown" + "\033[0m";
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Full implementation of a quoted word. Includes:
-// '\''
-// '"'
-// "'"
-// "\""
-// 'one two'
-// Result includes the quotes.
-bool Lexer::readWord (
- const std::string& text,
- const std::string& quotes,
- std::string::size_type& cursor,
- std::string& word)
-{
- if (quotes.find (text[cursor]) == std::string::npos)
- return false;
-
- std::string::size_type eos = text.length ();
- int quote = text[cursor++];
- word = quote;
-
- int c;
- while ((c = text[cursor]))
- {
- // Quoted word ends on a quote.
- if (quote && quote == c)
- {
- word += utf8_character (utf8_next_char (text, cursor));
- break;
- }
-
- // Unicode U+XXXX or \uXXXX codepoint.
- else if (eos - cursor >= 6 &&
- ((text[cursor + 0] == 'U' && text[cursor + 1] == '+') ||
- (text[cursor + 0] == '\\' && text[cursor + 1] == 'u')) &&
- unicodeHexDigit (text[cursor + 2]) &&
- unicodeHexDigit (text[cursor + 3]) &&
- unicodeHexDigit (text[cursor + 4]) &&
- unicodeHexDigit (text[cursor + 5]))
- {
- word += utf8_character (
- hexToInt (
- text[cursor + 2],
- text[cursor + 3],
- text[cursor + 4],
- text[cursor + 5]));
- cursor += 6;
- }
-
- // An escaped thing.
- else if (c == '\\')
- {
- c = text[++cursor];
-
- switch (c)
- {
- case '"': word += (char) 0x22; ++cursor; break;
- case '\'': word += (char) 0x27; ++cursor; break;
- case '\\': word += (char) 0x5C; ++cursor; break;
- case 'b': word += (char) 0x08; ++cursor; break;
- case 'f': word += (char) 0x0C; ++cursor; break;
- case 'n': word += (char) 0x0A; ++cursor; break;
- case 'r': word += (char) 0x0D; ++cursor; break;
- case 't': word += (char) 0x09; ++cursor; break;
- case 'v': word += (char) 0x0B; ++cursor; break;
-
- // This pass-through default case means that anything can be escaped
- // harmlessly. In particular 'quote' is included, if it not one of the
- // above characters.
- default: word += (char) c; ++cursor; break;
- }
- }
-
- // Ordinary character.
- else
- word += utf8_character (utf8_next_char (text, cursor));
- }
-
- // Verify termination.
- return word[0] == quote &&
- word[word.length () - 1] == quote &&
- word.length () >= 2;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Full implementation of an unquoted word. Includes:
-// one\ two
-// abcU+0020def
-// abc\u0020def
-// a\tb
-//
-// Ends at:
-// Lexer::isEOS
-// unicodeWhitespace
-// Lexer::isHardBoundary
-bool Lexer::readWord (
- const std::string& text,
- std::string::size_type& cursor,
- std::string& word)
-{
- std::string::size_type eos = text.length ();
-
- word = "";
- int c;
- int prev = 0;
- while ((c = text[cursor])) // Handles EOS.
- {
- // Unquoted word ends on white space.
- if (unicodeWhitespace (c))
- break;
-
- // Parentheses mostly.
- if (prev && Lexer::isHardBoundary (prev, c))
- break;
-
- // Unicode U+XXXX or \uXXXX codepoint.
- else if (eos - cursor >= 6 &&
- ((text[cursor + 0] == 'U' && text[cursor + 1] == '+') ||
- (text[cursor + 0] == '\\' && text[cursor + 1] == 'u')) &&
- unicodeHexDigit (text[cursor + 2]) &&
- unicodeHexDigit (text[cursor + 3]) &&
- unicodeHexDigit (text[cursor + 4]) &&
- unicodeHexDigit (text[cursor + 5]))
- {
- word += utf8_character (
- hexToInt (
- text[cursor + 2],
- text[cursor + 3],
- text[cursor + 4],
- text[cursor + 5]));
- cursor += 6;
- }
-
- // An escaped thing.
- else if (c == '\\')
- {
- c = text[++cursor];
-
- switch (c)
- {
- case '"': word += (char) 0x22; ++cursor; break;
- case '\'': word += (char) 0x27; ++cursor; break;
- case '\\': word += (char) 0x5C; ++cursor; break;
- case 'b': word += (char) 0x08; ++cursor; break;
- case 'f': word += (char) 0x0C; ++cursor; break;
- case 'n': word += (char) 0x0A; ++cursor; break;
- case 'r': word += (char) 0x0D; ++cursor; break;
- case 't': word += (char) 0x09; ++cursor; break;
- case 'v': word += (char) 0x0B; ++cursor; break;
-
- // This pass-through default case means that anything can be escaped
- // harmlessly. In particular 'quote' is included, if it not one of the
- // above characters.
- default: word += (char) c; ++cursor; break;
- }
- }
-
- // Ordinary character.
- else
- word += utf8_character (utf8_next_char (text, cursor));
-
- prev = c;
- }
-
- return word.length () > 0 ? true : false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2013 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_LEXER
-#define INCLUDED_LEXER
-
-#include <string>
-#include <map>
-#include <vector>
-#include <tuple>
-#include <cstddef>
-
-class Lexer
-{
-public:
- // These are overridable.
- static std::string dateFormat;
-
- enum class Type { uuid, number, hex,
- string,
- url,
- path,
- pattern,
- op,
- word,
- date, duration };
-
- explicit Lexer (const std::string&);
- bool token (std::string&, Lexer::Type&);
- static std::string typeToString (Lexer::Type);
-
- // Static helpers.
- static std::vector <std::tuple <std::string, Lexer::Type>> tokenize (const std::string&);
- static const std::string typeName (const Lexer::Type&);
- static bool isSingleCharOperator (int);
- static bool isDoubleCharOperator (int, int, int);
- static bool isTripleCharOperator (int, int, int, int);
- static bool isBoundary (int, int);
- static bool isHardBoundary (int, int);
- static bool isPunctuation (int);
- static bool wasQuoted (const std::string&);
- static bool readWord (const std::string&, const std::string&, std::string::size_type&, std::string&);
- static bool readWord (const std::string&, std::string::size_type&, std::string&);
- static int hexToInt (int);
- static int hexToInt (int, int);
- static int hexToInt (int, int, int, int);
- static std::string trimLeft (const std::string& in, const std::string& t = " ");
- static std::string trimRight (const std::string& in, const std::string& t = " ");
- static std::string trim (const std::string& in, const std::string& t = " ");
- static std::string dequote (const std::string&, const std::string& quotes = "'\"");
-
- // Stream Classifiers.
- bool isEOS () const;
- bool isString (std::string&, Lexer::Type&, const std::string&);
- bool isDate (std::string&, Lexer::Type&);
- bool isDuration (std::string&, Lexer::Type&);
- bool isUUID (std::string&, Lexer::Type&, bool);
- bool isNumber (std::string&, Lexer::Type&);
- bool isInteger (std::string&, Lexer::Type&);
- bool isHexNumber (std::string&, Lexer::Type&);
- bool isURL (std::string&, Lexer::Type&);
- bool isPath (std::string&, Lexer::Type&);
- bool isPattern (std::string&, Lexer::Type&);
- bool isOperator (std::string&, Lexer::Type&);
- bool isWord (std::string&, Lexer::Type&);
-
- // Disabling features.
- void noString () { _enableString = false; }
- void noDate () { _enableDate = false; }
- void noDuration () { _enableDuration = false; }
- void noUUID () { _enableUUID = false; }
- void noHexNumber () { _enableHexNumber = false; }
- void noWord () { _enableWord = false; }
- void noURL () { _enableURL = false; }
- void noPath () { _enablePath = false; }
- void noPattern () { _enablePattern = false; }
- void noOperator () { _enableOperator = false; }
-
-private:
- std::string _text {};
- std::size_t _cursor {0};
- std::size_t _eos {0};
-
- bool _enableString {true};
- bool _enableDate {true};
- bool _enableDuration {true};
- bool _enableUUID {true};
- bool _enableHexNumber {true};
- bool _enableWord {true};
- bool _enableURL {true};
- bool _enablePath {true};
- bool _enablePattern {true};
- bool _enableOperator {true};
-};
-
-#endif
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2015 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <Log.h>
-#include <shared.h>
-#include <format.h>
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Log::file () const
-{
- return _name;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Log::file (const std::string& name)
-{
- _name = name;
- _file = File (_name);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Log::ignore (const std::string& category)
-{
- _ignore.insert (category);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Log::write (const std::string& line)
-{
- write ("info", line);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Log::write (const std::string& category, const std::string& line)
-{
- // Determine if this category is in the ignore list.
- if (_ignore.find (category) != _ignore.end ())
- return;
-
- if (line != "")
- {
- // If line contains newlines, split it into separate lines and log each one.
- if (line.find ('\n') != std::string::npos)
- {
- for (const auto& single : split (line, '\n'))
- write (category, single);
-
- return;
- }
-
- // Single line.
- if (line == _prior)
- {
- ++_repetition;
- }
- else
- {
- _prior = line;
-
- // Catch up by writing out the backlog.
- if (_name != "" && _backlog.size ())
- {
- for (const auto& line : _backlog)
- _file.append (line);
-
- _backlog.clear ();
- }
-
- if (_repetition)
- {
- std::string message = Datetime ().toISO ()
- + ' ' + PACKAGE_VERSION
- + ' ' + category
- + ' ' + format ("(Repeated {1} times)", _repetition)
- + '\n';
-
- if (_name != "")
- _file.append (message);
- else
- _backlog.push_back (message);
-
- _repetition = 0;
- }
-
- std::string message = Datetime ().toISO ()
- + ' ' + PACKAGE_VERSION
- + ' ' + category
- + ' ' + line
- + '\n';
-
- if (_name != "")
- _file.append (message);
- else
- _backlog.push_back (message);
- }
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2015 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_LOG
-#define INCLUDED_LOG
-
-#include <FS.h>
-#include <Datetime.h>
-#include <vector>
-#include <set>
-#include <string>
-#include <stdio.h>
-
-class Log
-{
-public:
- std::string file () const;
- void file (const std::string&);
- void ignore (const std::string&);
- void write (const std::string&);
- void write (const std::string&, const std::string&);
-
-private:
- std::string _name {};
- std::vector <std::string> _backlog {};
- File _file {};
- std::string _prior {"none"};
- int _repetition {0};
- std::set <std::string> _ignore {};
-};
-
-#endif
-
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <Msg.h>
-#include <shared.h>
-#include <format.h>
-
-////////////////////////////////////////////////////////////////////////////////
-void Msg::set (const std::string& name, int value)
-{
- _header[name] = format (value);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Msg::set (const std::string& name, const std::string& value)
-{
- _header[name] = value;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Msg::get (const std::string& name) const
-{
- auto i = _header.find (name);
- if (i != _header.end ())
- return i->second;
-
- return "";
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Msg::setPayload (const std::string& payload)
-{
- _payload = payload;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Msg::getPayload () const
-{
- return _payload;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::vector <std::string> Msg::all () const
-{
- std::vector <std::string> names;
- for (auto& i : _header)
- names.push_back (i.first);
-
- return names;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Msg::serialize () const
-{
- std::string output;
- for (auto& i : _header)
- output += i.first + ": " + i.second + '\n';
-
- output += '\n' + _payload + '\n';
-
- return output;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Msg::parse (const std::string& input)
-{
- _header.clear ();
- _payload = "";
-
- auto separator = input.find ("\n\n");
- if (separator == std::string::npos)
- throw std::string ("Malformed message");
-
- // Parse header.
- auto lines = split (input.substr (0, separator), '\n');
- for (auto& i : lines)
- {
- auto delimiter = i.find (':');
- if (delimiter == std::string::npos)
- throw std::string ("Malformed message header '") + i + '\'';
-
- _header[trim (i.substr (0, delimiter))] = trim (i.substr (delimiter + 1));
- }
-
- // Parse payload.
- _payload = input.substr (separator + 2);
-
- return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_MSG
-#define INCLUDED_MSG
-
-#include <map>
-#include <vector>
-#include <string>
-
-class Msg
-{
-public:
- void set (const std::string&, int);
- void set (const std::string&, const std::string&);
- std::string get (const std::string&) const;
-
- void setPayload (const std::string&);
- std::string getPayload () const;
-
- std::vector <std::string> all () const;
- std::string serialize () const;
- bool parse (const std::string&);
-
-private:
- std::map <std::string, std::string> _header {};
- std::string _payload {""};
-};
-
-#endif
-
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2015 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <PEG.h>
-#include <Lexer.h>
-#include <shared.h>
-#include <format.h>
-#include <iostream>
-
-////////////////////////////////////////////////////////////////////////////////
-std::string PEG::Token::dump () const
-{
- std::stringstream out;
- out << (_lookahead == Token::Lookahead::positive ? "\e[34m&\e[0m" :
- _lookahead == Token::Lookahead::negative ? "\e[34m!\e[0m" : "")
- << _token
- << (_quantifier == Token::Quantifier::zero_or_one ? "\e[34m?\e[0m" :
- _quantifier == Token::Quantifier::one_or_more ? "\e[34m+\e[0m" :
- _quantifier == Token::Quantifier::zero_or_more ? "\e[34m*\e[0m" : "");
-
- for (const auto& tag : _tags)
- out << " \e[34m" << tag << "\e[0m";
-
- return out.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void PEG::loadFromFile (File& file)
-{
- if (! file.exists ())
- throw format ("PEG file '{1}' not found.", file._data);
-
- std::string contents;
- file.read (contents);
-
- // TODO Instead of simply reading a file, read a file and allow lines that
- // match /^include <path>$/ to represent nested files.
-
- loadFromString (contents);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Load and parse PEG.
-//
-// Syntax:
-// rule-name: alternate1-token1 alternate1-token2
-// alternate2-token1
-//
-// - Rules are aligned at left margin only, followed by colon.
-// - Productions are indented and never at left margin.
-// - Blank lines delineate rules.
-//
-// Details:
-// - String literals are always double-quoted.
-// - Character literals are alをays single-quoted.
-// - "*", "+" and "?" suffixes have POSIX wildcard semantics.
-//
-void PEG::loadFromString (const std::string& input)
-{
- // This is a state machine. Read each line.
- std::string rule_name = "";
- for (auto& line : loadImports (split (input, '\n')))
- {
- line = trim (line);
-
- // Eliminate inline comments.
- auto hash = line.find ('#');
- if (hash != std::string::npos)
- {
- line.resize (hash);
- line = trim (line);
-
- if (line == "")
- continue;
- }
- else
- {
- line = trim (line);
- }
-
- // Skip blank lines with no semantics.
- if (line == "" and rule_name == "")
- continue;
-
- if (line != "")
- {
- int token_count = 0;
-
- // Instantiate and configure the Lexer.
- Lexer l (line);
- l.noDate ();
- l.noDuration ();
- l.noUUID ();
- l.noHexNumber ();
- l.noURL ();
- l.noPath ();
- l.noPattern ();
- l.noOperator ();
-
- Lexer::Type type;
- std::string token;
- while (l.token (token, type))
- {
- ++token_count;
-
- // Rule definitions end in a colon.
- if (token.back () == ':')
- {
- // Capture the Rule_name.
- rule_name = token.substr (0, token.size () - 1);
-
- // If this is the first Rule, capture it as a starting point.
- if (_start == "")
- _start = rule_name;
-
- _rules[rule_name] = PEG::Rule ();
- token_count = 0;
- }
- // Production definition.
- else
- {
- // If no Production was added yet, add one.
- if (token_count <= 1)
- _rules[rule_name].push_back (PEG::Production ());
-
- // Decorate the token, if necessary.
- std::string::size_type start = 0;
- std::string::size_type end = token.length ();
-
- auto q = Token::Quantifier::one;
- auto l = Token::Lookahead::none;
-
- if (token.back () == '?')
- {
- q = Token::Quantifier::zero_or_one;
- --end;
- }
- else if (token.back () == '+')
- {
- q = Token::Quantifier::one_or_more;
- --end;
- }
- else if (token.back () == '*')
- {
- q = Token::Quantifier::zero_or_more;
- --end;
- }
-
- if (token.front () == '&')
- {
- l = Token::Lookahead::positive;
- ++start;
- }
- else if (token.front () == '!')
- {
- l = Token::Lookahead::negative;
- ++start;
- }
-
- PEG::Token t (token.substr (start, end - start));
- t._quantifier = q;
- t._lookahead = l;
-
- if (type == Lexer::Type::string)
- {
- t.tag ("literal");
- t.tag (token[0] == '\'' ? "character" : "string");
- }
- else if (t._token.front () == '<' and
- t._token.back () == '>')
- {
- t.tag ("intrinsic");
-
- if (t._token.substr (0, 8) == "<entity:")
- t.tag ("entity");
-
- if (t._token.substr (0, 10) == "<external:")
- t.tag ("external");
- }
-
- // Add the new Token to the most recent Production, of the current
- // Rule.
- _rules[rule_name].back ().push_back (t);
- }
- }
- }
-
- // A blank line in the input ends the current rule definition.
- else
- rule_name = "";
- }
-
- if (_debug)
- std::cout << dump ();
-
- // Validate the parsed grammar.
- validate ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::map <std::string, PEG::Rule> PEG::syntax () const
-{
- return _rules;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string PEG::firstRule () const
-{
- return _start;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void PEG::debug ()
-{
- ++_debug;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void PEG::strict (bool value)
-{
- _strict = value;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string PEG::dump () const
-{
- std::stringstream out;
- out << "PEG\n";
-
- // Show the import files, if any.
- if (_imports.size ())
- {
- for (const auto& import : _imports)
- out << " import " << import << '\n';
- out << '\n';
- }
-
- // Determine longest rule name, for display alignment.
- size_t longest = 0;
- for (const auto& rule : _rules)
- if (rule.first.length () > longest)
- longest = rule.first.length ();
-
- for (const auto& rule : _rules)
- {
- // Indicate the start Rule.
- out << " "
- << (rule.first == _start ? "▶" : " ")
- << ' '
- << rule.first
- << ':'
- << std::string (1 + longest - rule.first.length (), ' ');
-
- int count = 0;
- for (const auto& production : rule.second)
- {
- if (count)
- out << std::string (6 + longest, ' ');
-
- for (const auto& token : production)
- out << token.dump () << ' ';
-
- out << "\n";
- ++count;
- }
-
- out << "\n";
- }
-
- return out.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::vector <std::string> PEG::loadImports (const std::vector <std::string>& lines)
-{
- std::vector <std::string> resolved;
-
- for (auto& line : lines)
- {
- auto copy = trim (line);
-
- auto hash = copy.find ('#');
- if (hash != std::string::npos)
- {
- copy.resize (hash);
- copy = trim (copy);
- }
-
- if (copy.find ("import ") == 0)
- {
- File file (trim (copy.substr (7)));
- if (file.exists () &&
- file.readable ())
- {
- // Only import files that are not already imported.
- if (std::find (_imports.begin (), _imports.end (), file._data) == _imports.end ())
- {
- _imports.push_back (file._data);
-
- std::vector <std::string> imported;
- file.read (imported);
- imported = loadImports (imported);
-
- resolved.insert(std::end(resolved), std::begin(imported), std::end(imported));
- }
- }
- else
- throw format ("Cannot import '{1}'", file._data);
- }
- else
- {
- // Store original line.
- resolved.push_back (line);
- }
- }
-
- return resolved;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void PEG::validate () const
-{
- if (_start == "")
- throw std::string ("There are no rules defined.");
-
- std::vector <std::string> allRules;
- std::vector <std::string> allTokens;
- std::vector <std::string> allLeftRecursive;
- std::vector <std::string> intrinsics;
- std::vector <std::string> externals;
-
- for (const auto& rule : _rules)
- {
- allRules.push_back (rule.first);
-
- for (const auto& production : rule.second)
- {
- for (const auto& token : production)
- {
- if (token.hasTag ("intrinsic"))
- intrinsics.push_back (token._token);
-
- else if (token.hasTag ("external"))
- externals.push_back (token._token);
-
- else if (! token.hasTag ("literal"))
- allTokens.push_back (token._token);
-
- if (token._token == production[0]._token and
- rule.first == production[0]._token and
- production.size () == 1)
- allLeftRecursive.push_back (token._token);
- }
- }
- }
-
- std::vector <std::string> notUsed;
- std::vector <std::string> notDefined;
- listDiff (allRules, allTokens, notUsed, notDefined);
-
- // Undefined value - these are definitions that appear in token, but are
- // not in _rules.
- for (const auto& nd : notDefined)
- if (std::find (externals.begin (), externals.end (), nd) == externals.end ())
- throw format ("Definition '{1}' referenced, but not defined.", nd);
-
- // Circular definitions - these are names in _rules that also appear as
- // the only token in any of the alternates for that definition.
- for (const auto& lr : allLeftRecursive)
- throw format ("Definition '{1}' is left recursive.", lr);
-
- for (const auto& r : allRules)
- if (r[0] == '<')
- throw format ("Definition '{1}' may not redefine an intrinsic.");
-
- for (const auto& r : allRules)
- if (r[0] == '"' or
- r[0] == '\'')
- throw format ("Definition '{1}' may not be a literal.");
-
- // Unused definitions - these are names in _rules that are never
- // referenced as token.
- for (const auto& nu : notUsed)
- {
- if (std::find (externals.begin (), externals.end (), nu) == externals.end () &&
- nu != _start)
- {
- if (_strict)
- throw format ("Definition '{1}' is defined, but not referenced.", nu);
- else
- std::cout << "Warning: Definition '" << nu << "' is defined, but not referenced.\n";
- }
- }
-
- // Intrinsics must be recognized.
- for (auto& intrinsic : intrinsics)
- if (intrinsic != "<digit>" &&
- intrinsic != "<hex>" &&
- intrinsic != "<punct>" &&
- intrinsic != "<eol>" &&
- intrinsic != "<sep>" &&
- intrinsic != "<ws>" &&
- intrinsic != "<alpha>" &&
- intrinsic != "<character>" &&
- intrinsic != "<word>" &&
- intrinsic != "<token>" &&
- intrinsic.substr (0, 8) != "<entity:" &&
- intrinsic.substr (0, 10) != "<external:")
- throw format ("Specified intrinsic '{1}' is not supported.", intrinsic);
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2015 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_PEG
-#define INCLUDED_PEG
-
-#include <FS.h>
-#include <string>
-#include <vector>
-#include <map>
-#include <set>
-
-class PEG
-{
-public:
- class Token
- {
- public:
- Token (const std::string& value) { _token = value; }
- void tag (const std::string& tag) { _tags.insert (tag); }
- bool hasTag (const std::string& tag) const { return _tags.find (tag) != _tags.end (); };
- std::string dump () const;
-
- enum class Quantifier { one, zero_or_one, one_or_more, zero_or_more };
- enum class Lookahead { none, positive, negative };
-
- std::string _token {};
- std::set <std::string> _tags {};
- Quantifier _quantifier {Quantifier::one};
- Lookahead _lookahead {Lookahead::none};
- // TODO Added Lexer::Type support, which allows the PEG to specify
- // "<Lexer::Type>" as a built-in type.
- };
-
- class Production : public std::vector <Token>
- {
- };
-
- class Rule : public std::vector <Production>
- {
- };
-
-public:
- void loadFromFile (File&);
- void loadFromString (const std::string&);
- std::map <std::string, PEG::Rule> syntax () const;
- std::string firstRule () const;
- void debug ();
- void strict (bool);
- std::string dump () const;
-
-private:
- std::vector <std::string> loadImports (const std::vector <std::string>&);
- void validate () const;
-
-private:
- // rule name rule
- // | |
- std::map <std::string, PEG::Rule> _rules {};
- std::string _start {};
- int _debug {0};
- bool _strict {false};
- std::vector <std::string> _imports {};
-};
-
-#endif
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2015 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <Packrat.h>
-#include <shared.h>
-#include <format.h>
-#include <unicode.h>
-#include <utf8.h>
-#include <iostream>
-
-int Packrat::minimumMatchLength = 3;
-
-////////////////////////////////////////////////////////////////////////////////
-void Packrat::debug ()
-{
- ++_debug;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Walk the grammar tree to parse the input text, resulting in a parse tree.
-void Packrat::parse (const PEG& peg, const std::string& input)
-{
- // Used to walk the grammar tree.
- // Note there is only one rule at the top of the syntax tree, which was the
- // first one defined.
- _syntax = peg.syntax ();
- _tree->_name = peg.firstRule ();
-
- // The pig that will be sent down the pipe.
- Pig pig (input);
- if (_debug)
- std::cout << "trace " << pig.dump () << "\n";
-
- // Match the first rule. Recursion does the rest.
- if (! matchRule (_tree->_name, pig, _tree, 0))
- throw std::string ("Parse failed.");
-
- if (! pig.eos ())
- throw format ("Parse failed - extra character at position {1}.", pig.cursor ());
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Packrat::entity (const std::string& category, const std::string& name)
-{
- // Walk the list of entities for category.
- auto c = _entities.equal_range (category);
- for (auto e = c.first; e != c.second; ++e)
- if (e->second == name)
- return;
-
- // The category/name pair was not found, therefore add it.
- _entities.insert (std::pair <std::string, std::string> (category, name));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Packrat::external (
- const std::string& rule,
- bool (*fn)(Pig&, std::shared_ptr <Tree>))
-{
- if (_externals.find (rule) != _externals.end ())
- throw format ("There is already an external parser defined for rule '{1}'.", rule);
-
- _externals[rule] = fn;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// If there is a match, pig advances further down the pipe.
-bool Packrat::matchRule (
- const std::string& rule,
- Pig& pig,
- std::shared_ptr <Tree> parseTree,
- int indent)
-{
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "matchRule " << rule << "\n";
- auto checkpoint = pig.cursor ();
-
- for (const auto& production : _syntax.find (rule)->second)
- if (matchProduction (production, pig, parseTree, indent + 1))
- return true;
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Packrat::matchProduction (
- const PEG::Production& production,
- Pig& pig,
- std::shared_ptr <Tree> parseTree,
- int indent)
-{
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "matchProduction\n";
- auto checkpoint = pig.cursor ();
-
- auto collector = std::make_shared <Tree> ();
- for (const auto& token : production)
- {
- auto b = std::make_shared <Tree> ();
- if (! matchTokenQuant (token, pig, b, indent + 1))
- {
- pig.restoreTo (checkpoint);
- return false;
- }
-
- // Accumulate branches.
- collector->addBranch (b);
- }
-
- // On success transfer all sub-branches.
- for (auto& b : collector->_branches)
- for (auto sub : b->_branches)
- parseTree->addBranch (sub);
-
- return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Wraps calls to matchTokenLookahead, while properly handling the quantifier.
-bool Packrat::matchTokenQuant (
- const PEG::Token& token,
- Pig& pig,
- std::shared_ptr <Tree> parseTree,
- int indent)
-{
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "matchTokenQuant " << token.dump () << "\n";
-
- // Must match exactly once, so run once and return the result.
- if (token._quantifier == PEG::Token::Quantifier::one)
- {
- return matchTokenLookahead (token, pig, parseTree, indent + 1);
- }
-
- // May match zero or one time. If it matches, the cursor will be advanced.
- // If it fails, the cursor will not be advanced, but this is still considered
- // successful. Return true either way, but backtrack the cursor on failure.
- else if (token._quantifier == PEG::Token::Quantifier::zero_or_one)
- {
- // Check for a single match, succeed anyway.
- matchTokenLookahead (token, pig, parseTree, indent + 1);
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "\e[32mmatch ?\e[0m " << token.dump () << "\n";
- if (_debug)
- std::cout << "trace " << pig.dump () << ' ' << token.dump () << "\n";
- return true;
- }
-
- // May match 1 or more times. If it matches on the first attempt, continue
- // to greedily match until it fails. If it fails on the first attempt, then
- // the rule fails.
- else if (token._quantifier == PEG::Token::Quantifier::one_or_more)
- {
- if (! matchTokenLookahead (token, pig, parseTree, indent + 1))
- return false;
-
- while (matchTokenLookahead (token, pig, parseTree, indent + 1))
- {
- // "Forget it, he's rolling."
- }
-
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "\e[32mmatch +\e[0m " << token.dump () << "\n";
- if (_debug)
- std::cout << "trace " << pig.dump () << ' ' << token.dump () << "\n";
- return true;
- }
-
- // May match zero or more times. Keep calling while there are matches, and
- // return true always. Backtrack the cursor on failure.
- else if (token._quantifier == PEG::Token::Quantifier::zero_or_more)
- {
- while (matchTokenLookahead (token, pig, parseTree, indent + 1))
- {
- // Let it go.
- }
-
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "\e[32mmatch *\e[0m " << token.dump () << "\n";
- if (_debug)
- std::cout << "trace " << pig.dump () << ' ' << token.dump () << "\n";
- return true;
- }
-
- throw std::string ("This should never happen.");
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Wraps calls to matchToken, while properly handling lookahead.
-bool Packrat::matchTokenLookahead (
- const PEG::Token& token,
- Pig& pig,
- std::shared_ptr <Tree> parseTree,
- int indent)
-{
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "matchTokenLookahead " << token.dump () << "\n";
-
- if (token._lookahead == PEG::Token::Lookahead::none)
- {
- return matchToken (token, pig, parseTree, indent + 1);
- }
- else if (token._lookahead == PEG::Token::Lookahead::positive)
- {
- auto checkpoint = pig.cursor ();
- auto b = std::make_shared <Tree> ();
- if (matchToken (token, pig, b, indent + 1))
- {
- pig.restoreTo (checkpoint);
- return true;
- }
- }
- else if (token._lookahead == PEG::Token::Lookahead::negative)
- {
- auto checkpoint = pig.cursor ();
- auto b = std::make_shared <Tree> ();
- if (! matchToken (token, pig, b, indent + 1))
- {
- return true;
- }
-
- pig.restoreTo (checkpoint);
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Packrat::matchToken (
- const PEG::Token& token,
- Pig& pig,
- std::shared_ptr <Tree> parseTree,
- int indent)
-{
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "matchToken " << token.dump () << "\n";
-
- auto checkpoint = pig.cursor ();
- auto b = std::make_shared <Tree> ();
-
- if (token.hasTag ("intrinsic") &&
- matchIntrinsic (token, pig, parseTree, indent + 1))
- {
- return true;
- }
-
- else if (_syntax.find (token._token) != _syntax.end () &&
- matchRule (token._token, pig, b, indent + 1))
- {
- // This is the only case that adds a sub-branch.
- b->_name = token._token;
- parseTree->addBranch (b);
- return true;
- }
-
- else if (token.hasTag ("literal") &&
- token.hasTag ("character") &&
- matchCharLiteral (token, pig, parseTree, indent + 1))
- {
- return true;
- }
-
- else if (token.hasTag ("literal") &&
- token.hasTag ("string") &&
- matchStringLiteral (token, pig, parseTree, indent + 1))
- {
- return true;
- }
-
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Supports the following:
-// <digit> --> unicodeLatinDigit
-// <hex> --> unicodeHexDigit
-// <character> --> anything
-// <alpha> --> unicodeAlpha
-// <punct> --> unicodePunctuation
-// <ws> --> unicodeWhitespace
-// <sep> --> unicodeHorizontalWhitespace
-// <eol> --> unicodeVerticalWhitespace
-// <word> --> <alpha>
-// <token> --> consecutive non <ws>
-// <entity:e> --> Any category 'e' token
-// <external:x> --> Delegate to external function
-bool Packrat::matchIntrinsic (
- const PEG::Token& token,
- Pig& pig,
- std::shared_ptr <Tree> parseTree,
- int indent)
-{
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "matchIntrinsic " << token.dump () << "\n";
- auto checkpoint = pig.cursor ();
-
- // There are only 10 digits.
- if (token._token == "<digit>")
- {
- int digit;
- if (pig.getDigit (digit))
- {
- // Create a populated branch.
- auto b = std::make_shared <Tree> ();
- b->_name = "intrinsic";
- b->attribute ("expected", token._token);
- b->attribute ("value", format ("{1}", digit));
- parseTree->addBranch (b);
-
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "\e[32mmatch\e[0m " << digit << "\n";
- if (_debug)
- std::cout << "trace " << pig.dump () << ' ' << token.dump () << "\n";
- return true;
- }
- }
-
- // Upper or lower case hex digit.
- if (token._token == "<hex>")
- {
- int digit;
- if (pig.getHexDigit (digit))
- {
- // Create a populated branch.
- auto b = std::make_shared <Tree> ();
- b->_name = "intrinsic";
- b->attribute ("expected", token._token);
- b->attribute ("value", format ("{1}", digit));
- parseTree->addBranch (b);
-
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "\e[32mmatch\e[0m " << digit << "\n";
- if (_debug)
- std::cout << "trace " << pig.dump () << ' ' << token.dump () << "\n";
- return true;
- }
- }
-
- // Character means anything.
- else if (token._token == "<character>")
- {
- int character;
- if (pig.getCharacter (character))
- {
- // Create a populated branch.
- auto b = std::make_shared <Tree> ();
- b->_name = "intrinsic";
- b->attribute ("expected", token._token);
- b->attribute ("value", format ("{1}", character));
- parseTree->addBranch (b);
-
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "\e[32mmatch\e[0m " << character << "\n";
- if (_debug)
- std::cout << "trace " << pig.dump () << ' ' << token.dump () << "\n";
- return true;
- }
- }
-
- // <punct> ::ispunct
- else if (token._token == "<punct>")
- {
- int character = pig.peek ();
- if (unicodePunctuation (character))
- {
- pig.skip (character);
-
- // Create a populated branch.
- auto b = std::make_shared <Tree> ();
- b->_name = "intrinsic";
- b->attribute ("expected", token._token);
- b->attribute ("value", format ("{1}", character));
- parseTree->addBranch (b);
-
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "\e[32mmatch\e[0m " << character << "\n";
- if (_debug)
- std::cout << "trace " << pig.dump () << ' ' << token.dump () << "\n";
- return true;
- }
- }
-
- // <alpha>
- else if (token._token == "<alpha>")
- {
- int character = pig.peek ();
- if (unicodeAlpha (character))
- {
- pig.skip (character);
-
- // Create a populated branch.
- auto b = std::make_shared <Tree> ();
- b->_name = "intrinsic";
- b->attribute ("expected", token._token);
- b->attribute ("value", format ("{1}", character));
- parseTree->addBranch (b);
-
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "\e[32mmatch\e[0m " << character << "\n";
- if (_debug)
- std::cout << "trace " << pig.dump () << ' ' << token.dump () << "\n";
- return true;
- }
- }
-
- // <ws>
- else if (token._token == "<ws>")
- {
- int character = pig.peek ();
- if (unicodeWhitespace (character))
- {
- pig.skip (character);
-
- // Create a populated branch.
- auto b = std::make_shared <Tree> ();
- b->_name = "intrinsic";
- b->attribute ("expected", token._token);
- b->attribute ("value", format ("{1}", character));
- parseTree->addBranch (b);
-
- if (_debug > 10)
- std::cout << "trace " << std::string (indent, ' ') << "\e[32mmatch\e[0m " << character << "\n";
- if (_debug)
- std::cout << "trace " << pig.dump () << ' ' << token.dump () << "\n";
- return true;
- }
- }
-
- // <sep>
- else if (token._token == "<sep>")
- {
- int character = pig.peek ();
- if (unicodeHorizontalWhitespace (character))
- {
- pig.skip (character);
-
- // Create a populated branch.
- auto b = std::make_shared <Tree> ();
- b->_name = "intrinsic";
- b->attribute ("expected", token._token);
- b->attribute ("value", format ("{1}", character));
- parseTree->addBranch (b);
-
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "\e[32mmatch\e[0m " << character << "\n";
- if (_debug)
- std::cout << "trace " << pig.dump () << ' ' << token.dump () << "\n";
- return true;
- }
- }
-
- // <eol>
- else if (token._token == "<eol>")
- {
- int character = pig.peek ();
- if (unicodeVerticalWhitespace (character))
- {
- pig.skip (character);
-
- // Create a populated branch.
- auto b = std::make_shared <Tree> ();
- b->_name = "intrinsic";
- b->attribute ("expected", token._token);
- b->attribute ("value", format ("{1}", character));
- parseTree->addBranch (b);
-
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "\e[32mmatch\e[0m " << character << "\n";
- if (_debug)
- std::cout << "trace " << pig.dump () << ' ' << token.dump () << "\n";
- return true;
- }
- }
-
- // <word> consecutive non-<ws>, non-<punct>.
- else if (token._token == "<word>")
- {
- while (auto character = pig.peek ())
- {
- if (! character ||
- unicodeWhitespace (character) ||
- unicodePunctuation (character))
- break;
-
- pig.skip (character);
- }
-
- if (pig.cursor () > checkpoint)
- {
- auto word = pig.substr (checkpoint, pig.cursor () - checkpoint + 1);
-
- // Create a populated branch.
- auto b = std::make_shared <Tree> ();
- b->_name = "intrinsic";
- b->attribute ("expected", token._token);
- b->attribute ("value", word);
- parseTree->addBranch (b);
-
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "\e[32mmatch\e[0m " << word << "\n";
- if (_debug)
- std::cout << "trace " << pig.dump () << ' ' << token.dump () << "\n";
- return true;
- }
- }
-
- // <token> consecutive non-<ws>.
- else if (token._token == "<token>")
- {
- while (auto character = pig.peek ())
- {
- if (! character ||
- unicodeWhitespace (character))
- break;
-
- pig.skip (character);
- }
-
- if (pig.cursor () > checkpoint)
- {
- auto word = pig.substr (checkpoint, pig.cursor () - checkpoint);
-
- // Create a populated branch.
- auto b = std::make_shared <Tree> ();
- b->_name = "intrinsic";
- b->attribute ("expected", token._token);
- b->attribute ("value", word);
- parseTree->addBranch (b);
-
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "\e[32mmatch\e[0m " << word << "\n";
- if (_debug)
- std::cout << "trace " << pig.dump () << ' ' << token.dump () << "\n";
- return true;
- }
- }
-
- // <entity:category>.
- else if (token._token.find ("<entity:") == 0)
- {
- // Extract entity category
- auto category = token._token.substr (8, token._token.length () - 9);
-
- // Match against any one of the entity values in this category.
- auto values = _entities.equal_range (category);
- for (auto value = values.first; value != values.second; ++value)
- {
- if (pig.skipLiteral (value->second))
- {
- // Create a populated branch.
- auto b = std::make_shared <Tree> ();
- b->_name = "intrinsic";
- b->tag ("entity");
- b->attribute ("expected", token._token);
- b->attribute ("value", value->second);
- parseTree->addBranch (b);
-
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "\e[32mmatch\e[0m " << value->second << "\n";
- if (_debug)
- std::cout << "trace " << pig.dump () << ' ' << token.dump () << "\n";
-
- return true;
- }
- }
- }
-
- // <external:rule>
- else if (token._token.find ("<external:") == 0)
- {
- // Extract entity category
- auto rule = token._token.substr (10, token._token.length () - 11);
-
- // Any rule can be overridden by an external parser.
- if (_externals.find (rule) != _externals.end ())
- {
- // Create a pre-populated branch, which is attached on success only.
- auto newBranch = std::make_shared <Tree> ();
- newBranch->_name = "intrinsic";
- newBranch->tag ("external");
- newBranch->attribute ("expected", token._token);
-
- if (_externals[rule] (pig, newBranch))
- {
- // Determine what was parsed.
- auto word = pig.substr (checkpoint, pig.cursor () - checkpoint);
-
- // Attach the new branch.
- newBranch->attribute ("value", word);
- parseTree->addBranch (newBranch);
-
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "\e[32mmatch\e[0m " << word << "\n";
- if (_debug)
- std::cout << "trace " << pig.dump () << ' ' << token.dump () << "\n";
-
- return true;
- }
-
- // Note: Branch 'newBranch' goes out of scope here if parsing fails.
- }
- }
-
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "\e[31mfail\e[0m " << token._token << "\n";
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Packrat::matchCharLiteral (
- const PEG::Token& token,
- Pig& pig,
- std::shared_ptr <Tree> parseTree,
- int indent)
-{
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "matchCharLiteral " << token.dump () << "\n";
- auto checkpoint = pig.cursor ();
-
- if (token._token.length () >= 3 &&
- token._token[0] == '\'' &&
- token._token[2] == '\'')
- {
- int literal = token._token[1];
- if (pig.skip (literal))
- {
- // Create a populated branch.
- auto b = std::make_shared <Tree> ();
- b->_name = "charLiteral";
- b->attribute ("expected", token._token);
- b->attribute ("value", utf8_character (literal));
- parseTree->addBranch (b);
-
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "\e[32mmatch\e[0m " << token._token << "\n";
- if (_debug)
- std::cout << "trace " << pig.dump () << ' ' << token.dump () << "\n";
- return true;
- }
- }
-
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "\e[31mfail\e[0m " << token._token << "\n";
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Packrat::matchStringLiteral (
- const PEG::Token& token,
- Pig& pig,
- std::shared_ptr <Tree> parseTree,
- int indent)
-{
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "matchStringLiteral " << token.dump () << "\n";
- auto checkpoint = pig.cursor ();
-
- std::string literal = token._token.substr (1, token._token.length () - 2);
- if (pig.skipLiteral (literal))
- {
- // Create a populated branch.
- auto b = std::make_shared <Tree> ();
- b->_name = "stringLiteral";
- b->attribute ("expected", token._token);
- b->attribute ("value", literal);
- parseTree->addBranch (b);
-
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "\e[32mmatch\e[0m " << literal << "\n";
- if (_debug)
- std::cout << "trace " << pig.dump () << ' ' << token.dump () << "\n";
- return true;
- }
-
- if (_debug > 1)
- std::cout << "trace " << std::string (indent, ' ') << "\e[31mfail\e[0m " << token._token << "\n";
- pig.restoreTo (checkpoint);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Search for 'value' in _entities category, return canonicalized value.
-bool Packrat::canonicalize (
- std::string& canonicalized,
- const std::string& category,
- const std::string& value) const
-{
- // Extract a list of entities for category.
- std::vector <std::string> options;
- auto c = _entities.equal_range (category);
- for (auto e = c.first; e != c.second; ++e)
- {
- // Shortcut: if an exact match is found, success.
- if (value == e->second)
- {
- canonicalized = value;
- return true;
- }
-
- options.push_back (e->second);
- }
-
- // Match against the options, throw away results.
- std::vector <std::string> matches;
- if (autoComplete (value, options, matches, minimumMatchLength) == 1)
- {
- canonicalized = matches[0];
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Packrat::dump () const
-{
- std::stringstream out;
- if (_debug)
- out << '\n';
-
- out << "Packrat Parse "
- << _tree->dump ();
-
- if (_entities.size ())
- {
- out << " Entities\n";
- for (const auto& entity : _entities)
- out << " " << entity.first << ':' << entity.second << '\n';
- }
-
- if (_externals.size ())
- {
- out << " Externals\n";
- for (const auto& external : _externals)
- out << " " << external.first << "\n";
- }
-
- return out.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2015 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_PACKRAT
-#define INCLUDED_PACKRAT
-
-#include <PEG.h>
-#include <Tree.h>
-#include <Pig.h>
-#include <string>
-
-class Packrat
-{
-public:
- void parse (const PEG&, const std::string&);
- void entity (const std::string&, const std::string&);
- void external (const std::string&, bool (*)(Pig&, std::shared_ptr <Tree>));
-
- void debug ();
- std::string dump () const;
-
-private:
- bool matchRule (const std::string&, Pig&, std::shared_ptr <Tree>, int);
- bool matchProduction (const PEG::Production&, Pig&, std::shared_ptr <Tree>, int);
- bool matchTokenQuant (const PEG::Token&, Pig&, std::shared_ptr <Tree>, int);
- bool matchTokenLookahead (const PEG::Token&, Pig&, std::shared_ptr <Tree>, int);
- bool matchToken (const PEG::Token&, Pig&, std::shared_ptr <Tree>, int);
- bool matchIntrinsic (const PEG::Token&, Pig&, std::shared_ptr <Tree>, int);
- bool matchCharLiteral (const PEG::Token&, Pig&, std::shared_ptr <Tree>, int);
- bool matchStringLiteral (const PEG::Token&, Pig&, std::shared_ptr <Tree>, int);
-
- bool canonicalize (std::string&, const std::string&, const std::string&) const;
-
-public:
- static int minimumMatchLength;
-
-private:
- int _debug {0};
- std::map <std::string, PEG::Rule> _syntax {};
- std::shared_ptr <Tree> _tree {std::make_shared <Tree> ()};
- std::multimap <std::string, std::string> _entities {};
- std::map <std::string, bool (*)(Pig&, std::shared_ptr <Tree>)> _externals {};
-};
-
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2015 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <Palette.h>
-
-////////////////////////////////////////////////////////////////////////////////
-// Use a default palette, which is overwritten in ::initialize.
-Palette::Palette ()
-{
- _colors = {
- Color ("white on red"),
- Color ("white on blue"),
- Color ("black on green"),
- Color ("black on magenta"),
- Color ("black on cyan"),
- Color ("black on yellow"),
- Color ("black on white"),
- Color ("white on bright red"),
- Color ("white on bright blue"),
- Color ("black on bright green"),
- Color ("black on bright magenta"),
- Color ("black on bright cyan"),
- Color ("black on bright yellow"),
- };
-
- _current = 0;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Palette::add (Color c)
-{
- _colors.push_back (c);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Return the next color in the list. Cycle to the beginning if necessary.
-Color Palette::next ()
-{
- if (enabled)
- return _colors[_current++ % _colors.size ()];
-
- return Color ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Palette::size () const
-{
- return static_cast <int> (_colors.size ());
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Palette::clear ()
-{
- _colors.clear ();
- _current = 0;
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2015 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_PALETTE
-#define INCLUDED_PALETTE
-
-#include <Color.h>
-#include <vector>
-#include <string>
-
-class Palette
-{
-public:
- Palette ();
- void add (Color);
- Color next ();
- int size () const;
- void clear ();
-
-public:
- bool enabled {true};
-
-private:
- std::vector <Color> _colors {};
- int _current {0};
-};
-
-#endif
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2015 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <Pig.h>
-#include <shared.h>
-#include <unicode.h>
-#include <utf8.h>
-#include <algorithm>
-#include <sstream>
-#include <cinttypes>
-#include <cstdlib>
-
-////////////////////////////////////////////////////////////////////////////////
-Pig::Pig (const std::string& text)
-: _text {std::make_shared <std::string> (text)}
-{
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Pig::skip (int c)
-{
- if ((*_text)[_cursor] == c)
- {
- ++_cursor;
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Pig::skipN (const int quantity)
-{
- auto save = _cursor;
-
- auto count = 0;
- while (count++ < quantity)
- {
- if (! utf8_next_char (*_text, _cursor))
- {
- _cursor = save;
- return false;
- }
- }
-
- return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Pig::skipWS ()
-{
- auto save = _cursor;
-
- int c;
- auto prev = _cursor;
- while ((c = utf8_next_char (*_text, _cursor)))
- {
- if (! unicodeWhitespace (c))
- {
- _cursor = prev;
- break;
- }
- prev = _cursor;
- }
-
- return _cursor > save;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Pig::skipLiteral (const std::string& literal)
-{
- if (_text->find (literal, _cursor) == _cursor)
- {
- _cursor += literal.length ();
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Pig::skipPartial (const std::string& reference, std::string& result)
-{
- // Walk the common substring.
- auto pos = 0;
- while (reference[pos] &&
- (*_text)[_cursor + pos] &&
- reference[pos] == (*_text)[_cursor + pos])
- ++pos;
-
- if (pos > 0)
- {
- result = _text->substr (_cursor, pos);
- _cursor += pos;
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Pig::getUntil (int end, std::string& result)
-{
- auto save = _cursor;
-
- int c;
- auto prev = _cursor;
- while ((c = utf8_next_char (*_text, _cursor)))
- {
- if (c == end)
- {
- _cursor = prev;
- result = _text->substr (save, _cursor - save);
- return true;
- }
-
- else if (eos ())
- {
- result = _text->substr (save, _cursor - save);
- return true;
- }
-
- prev = _cursor;
- }
-
- return _cursor > save;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Pig::getUntilWS (std::string& result)
-{
- auto save = _cursor;
-
- int c;
- auto prev = _cursor;
- while ((c = utf8_next_char (*_text, _cursor)))
- {
- if (unicodeWhitespace (c))
- {
- _cursor = prev;
- result = _text->substr (save, _cursor - save);
- return true;
- }
-
- // Note: This test must follow the above unicodeWhitespace(c) test because
- // it is testing the value of 'c', and eos() is testing _cursor,
- // which has already been advanced.
- else if (eos ())
- {
- result = _text->substr (save, _cursor - save);
- return true;
- }
-
- prev = _cursor;
- }
-
- return _cursor > save;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Pig::getCharacter (int& result)
-{
- int c = (*_text)[_cursor];
- if (c)
- {
- result = c;
- ++_cursor;
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Pig::getDigit (int& result)
-{
- int c = (*_text)[_cursor];
- if (c &&
- unicodeLatinDigit (c))
- {
- result = c - '0';
- ++_cursor;
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Pig::getDigit2 (int& result)
-{
- if (unicodeLatinDigit ((*_text)[_cursor + 0]))
- {
- if (unicodeLatinDigit ((*_text)[_cursor + 1]))
- {
- result = strtoimax (_text->substr (_cursor, 2).c_str (), NULL, 10);
- _cursor += 2;
- return true;
- }
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Pig::getDigit3 (int& result)
-{
- if (unicodeLatinDigit ((*_text)[_cursor + 0]))
- {
- if (unicodeLatinDigit ((*_text)[_cursor + 1]))
- {
- if (unicodeLatinDigit ((*_text)[_cursor + 2]))
- {
- result = strtoimax (_text->substr (_cursor, 3).c_str (), NULL, 10);
- _cursor += 3;
- return true;
- }
- }
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Pig::getDigit4 (int& result)
-{
- if (unicodeLatinDigit ((*_text)[_cursor + 0]))
- {
- if (unicodeLatinDigit ((*_text)[_cursor + 1]))
- {
- if (unicodeLatinDigit ((*_text)[_cursor + 2]))
- {
- if (unicodeLatinDigit ((*_text)[_cursor + 3]))
- {
- result = strtoimax (_text->substr (_cursor, 4).c_str (), NULL, 10);
- _cursor += 4;
- return true;
- }
- }
- }
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Pig::getDigits (int& result)
-{
- auto save = _cursor;
-
- int c;
- auto prev = _cursor;
- while ((c = utf8_next_char (*_text, _cursor)))
- {
- if (! unicodeLatinDigit (c))
- {
- _cursor = prev;
- break;
- }
-
- prev = _cursor;
- }
-
- if (_cursor > save)
- {
- result = strtoimax (_text->substr (save, _cursor - save).c_str (), NULL, 10);
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Pig::getHexDigit (int& result)
-{
- int c = (*_text)[_cursor];
- if (c &&
- unicodeHexDigit (c))
- {
- if (c >= '0' && c <= '9')
- {
- result = c - '0';
- ++_cursor;
- return true;
- }
- else if (c >= 'A' && c <= 'F')
- {
- result = c - 'A' + 10;
- ++_cursor;
- return true;
- }
- else if (c >= 'a' && c <= 'f')
- {
- result = c - 'a' + 10;
- ++_cursor;
- return true;
- }
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// number:
-// int frac? exp?
-//
-// int:
-// (-|+)? digit+
-//
-// frac:
-// . digit+
-//
-// exp:
-// e digit+
-//
-// e:
-// e|E (+|-)?
-//
-bool Pig::getNumber (std::string& result)
-{
- auto i = _cursor;
-
- // [+-]?
- if ((*_text)[i] &&
- ((*_text)[i] == '-' ||
- (*_text)[i] == '+'))
- ++i;
-
- // digit+
- if ((*_text)[i] &&
- unicodeLatinDigit ((*_text)[i]))
- {
- ++i;
-
- while ((*_text)[i] && unicodeLatinDigit ((*_text)[i]))
- ++i;
-
- // ( . digit+ )?
- if ((*_text)[i] && (*_text)[i] == '.')
- {
- ++i;
-
- while ((*_text)[i] && unicodeLatinDigit ((*_text)[i]))
- ++i;
- }
-
- // ( [eE] [+-]? digit+ )?
- if ((*_text)[i] &&
- ((*_text)[i] == 'e' ||
- (*_text)[i] == 'E'))
- {
- ++i;
-
- if ((*_text)[i] &&
- ((*_text)[i] == '+' ||
- (*_text)[i] == '-'))
- ++i;
-
- if ((*_text)[i] && unicodeLatinDigit ((*_text)[i]))
- {
- ++i;
-
- while ((*_text)[i] && unicodeLatinDigit ((*_text)[i]))
- ++i;
-
- result = _text->substr (_cursor, i - _cursor);
- _cursor = i;
- return true;
- }
-
- return false;
- }
-
- result = _text->substr (_cursor, i - _cursor);
- _cursor = i;
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Pig::getNumber (double& result)
-{
- std::string s;
- if (getNumber (s))
- {
- result = std::strtod (s.c_str (), NULL);
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// [ + | - ] \d+ [ . [ \d+ ]]
-bool Pig::getDecimal (std::string& result)
-{
- auto i = _cursor;
-
- // [+-]?
- if ((*_text)[i] &&
- ((*_text)[i] == '-' ||
- (*_text)[i] == '+'))
- ++i;
-
- // digit+
- if ((*_text)[i] && unicodeLatinDigit ((*_text)[i]))
- {
- ++i;
-
- while ((*_text)[i] && unicodeLatinDigit ((*_text)[i]))
- ++i;
-
- // ( . digit+ )?
- if ((*_text)[i] && (*_text)[i] == '.')
- {
- ++i;
-
- while ((*_text)[i] && unicodeLatinDigit ((*_text)[i]))
- ++i;
- }
-
- result = _text->substr (_cursor, i - _cursor);
- _cursor = i;
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Pig::getDecimal (double& result)
-{
- std::string s;
- if (getDecimal (s))
- {
- result = std::strtod (s.c_str (), NULL);
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Gets quote content: "foobar" -> foobar (for c = '"')
-// Handles escaped quotes: "foo\"bar" -> foo\"bar (for c = '"')
-// Returns false if first character is not c, or if there is no closing c.
-// Does not modify content between quotes.
-bool Pig::getQuoted (int quote, std::string& result)
-{
- if (! (*_text)[_cursor] ||
- (*_text)[_cursor] != quote)
- return false;
-
- auto start = _cursor + utf8_sequence (quote);
- auto i = start;
-
- while ((*_text)[i])
- {
- i = _text->find (quote, i);
- if (i == std::string::npos)
- return false; // Unclosed quote. Short cut, not definitive.
-
- if (i == start)
- {
- // Empty quote
- _cursor += 2 * utf8_sequence (quote); // Skip both quote chars
- result = "";
- return true;
- }
-
- if ((*_text)[i - 1] == '\\')
- {
- // Check for escaped backslashes. Backtracking like this is not very
- // efficient, but is only done in extreme corner cases.
-
- auto j = i - 2; // Start one character further left
- bool is_escaped_quote = true;
- while (j >= start && (*_text)[j] == '\\')
- {
- // Toggle flag for each further backslash encountered.
- is_escaped_quote = is_escaped_quote ? false : true;
- --j;
- }
-
- if (is_escaped_quote)
- {
- // Keep searching
- ++i;
- continue;
- }
- }
-
- // None of the above applied, we must have found the closing quote char.
- result.assign (*_text, start, i - start);
- _cursor = i + utf8_sequence (quote); // Skip closing quote char
- return true;
- }
-
- // This should never be reached. We could throw here instead.
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Assumes that the options are sorted by decreasing length, so that if the
-// options contain 'fourteen' and 'four', the stream is first matched against
-// the longer entry.
-bool Pig::getOneOf (
- const std::vector <std::string>& options,
- std::string& found)
-{
- for (const auto& option : options)
- {
- if (skipLiteral (option))
- {
- found = option;
- return true;
- }
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Pig::getHMS (int& hours, int& minutes, int& seconds)
-{
- auto save = _cursor;
-
- if ((getDigit2 (hours) || getDigit (hours)) &&
- skip (':') &&
- getDigit2 (minutes))
- {
- seconds = 0;
- if (skip (':') &&
- ! getDigit2 (seconds))
- return false;
-
- return true;
- }
-
- _cursor = save;
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Pig::getRemainder (std::string& result)
-{
- if ((*_text)[_cursor])
- {
- result = _text->substr (_cursor);
- _cursor += result.length ();
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Pig::eos () const
-{
- return (*_text)[_cursor] == '\0';
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Peeks ahead - does not move cursor.
-int Pig::peek () const
-{
- return (*_text)[_cursor];
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Peeks ahead - does not move cursor.
-std::string Pig::peek (const int quantity) const
-{
- std::string::size_type adjusted = std::min (static_cast <std::string::size_type> (quantity), _text->length () - _cursor);
- if ((*_text)[_cursor])
- return _text->substr (_cursor, adjusted);
-
- return "";
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string::size_type Pig::cursor () const
-{
- return _cursor;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Note: never called internally, otherwise the client cannot rely on iṫ.
-std::string::size_type Pig::save ()
-{
- return _saved = _cursor;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Note: never called internally, otherwise the client cannot rely on iṫ.
-std::string::size_type Pig::restore ()
-{
- return _cursor = _saved;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string::size_type Pig::restoreTo (std::string::size_type previous)
-{
- return _cursor = previous;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Pig::substr (
- std::string::size_type start,
- std::string::size_type end) const
-{
- return _text->substr (start, end - start);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Pig::str () const
-{
- return _text->substr (_cursor);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Show the text, with the matched part in white on green, and the unmatched
-// part white on red, followed by the index equivalent.
-std::string Pig::dump () const
-{
- std::stringstream out;
- if (_cursor)
- out << "\e[37;42m"
- << _text->substr (0, _cursor)
- << "\e[0m";
-
- out << "\e[37;41m"
- << _text->substr (_cursor)
- << "\e[0m "
- << _cursor
- << '/'
- << _text->length ();
-
- return str_replace (out.str (), "\n", "\\n");
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2015 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_PIG
-#define INCLUDED_PIG
-
-#include <string>
-#include <vector>
-#include <memory>
-
-class Pig
-{
-public:
- explicit Pig (const std::string&);
-
- bool skip (int);
- bool skipN (const int quantity = 1);
- bool skipWS ();
- bool skipLiteral (const std::string&);
- bool skipPartial (const std::string&, std::string&);
-
- bool getUntil (int, std::string&);
- bool getUntilWS (std::string&);
- bool getCharacter (int&);
- bool getDigit (int&);
- bool getDigit2 (int&);
- bool getDigit3 (int&);
- bool getDigit4 (int&);
- bool getDigits (int&);
- bool getHexDigit (int&);
- bool getNumber (std::string&);
- bool getNumber (double&);
- bool getDecimal (std::string&);
- bool getDecimal (double&);
- bool getQuoted (int, std::string&);
- bool getOneOf (const std::vector <std::string>&, std::string&);
- bool getHMS (int&, int&, int&);
- bool getRemainder (std::string&);
-
- bool eos () const;
- int peek () const;
- std::string peek (const int) const;
- std::string::size_type cursor () const;
- std::string::size_type save ();
- std::string::size_type restore ();
- std::string::size_type restoreTo (std::string::size_type);
-
- std::string substr (std::string::size_type, std::string::size_type) const;
- std::string str () const;
- std::string dump () const;
-
-private:
- std::shared_ptr<std::string> _text;
- std::string::size_type _cursor {0};
- std::string::size_type _saved {0};
-};
-
-#endif
+++ /dev/null
-Shared
-======
-
-The 'shared' library is a set of reusable objects to be shared as-is between
-various programs, including Taskwarrior, Taskserver, Tasksh and Timewarrior.
-
-In order to be reusable, and therefore a member of libshared, conditions must
-be met:
-
- - Shared objects may only make use of other shared objects, or external
- dependencies (libuuid, gettext, readline etc), but no objects from the
- project directories.
-
- - Shared objects may assume only one external 'cmake.h' that defines
- platform/portability constants.
-
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <RX.h>
-#include <cstdlib>
-#include <cstring>
-
-////////////////////////////////////////////////////////////////////////////////
-RX::RX ()
-{
-}
-
-////////////////////////////////////////////////////////////////////////////////
-RX::RX (
- const std::string& pattern,
- bool case_sensitive /* = true */)
-: _compiled (false)
-, _pattern (pattern)
-, _case_sensitive (case_sensitive)
-{
- compile ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-RX::RX (const RX& other)
-{
- _compiled = false;
- _pattern = other._pattern;
- _case_sensitive = other._case_sensitive;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-RX::~RX ()
-{
- if (_compiled)
- regfree (&_regex);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-RX& RX::operator= (const RX& other)
-{
- _compiled = false;
- _pattern = other._pattern;
- _case_sensitive = other._case_sensitive;
-
- return *this;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void RX::compile ()
-{
- if (! _compiled)
- {
- memset (&_regex, 0, sizeof (regex_t));
-
- int result;
- if ((result = regcomp (&_regex, _pattern.c_str (),
-#if defined REG_ENHANCED
- REG_ENHANCED | REG_EXTENDED | REG_NEWLINE |
-#else
- REG_EXTENDED | REG_NEWLINE |
-#endif
- (_case_sensitive ? 0 : REG_ICASE))) != 0)
- {
- char message[256];
- regerror (result, &_regex, message, 256);
- throw std::string (message);
- }
-
- _compiled = true;
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool RX::match (const std::string& in)
-{
- if (! _compiled)
- compile ();
-
- return regexec (&_regex, in.c_str (), 0, nullptr, 0) == 0 ? true : false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool RX::match (
- std::vector<std::string>& matches,
- const std::string& in)
-{
- if (! _compiled)
- compile ();
-
- regmatch_t rm[2];
- int offset = 0;
- int length = in.length ();
- while (regexec (&_regex, in.c_str () + offset, 2, &rm[0], 0) == 0 &&
- offset < length)
- {
- matches.push_back (in.substr (rm[0].rm_so + offset, rm[0].rm_eo - rm[0].rm_so));
- offset += rm[0].rm_eo;
-
- // Protection against zero-width patterns causing infinite loops.
- if (rm[0].rm_so == rm[0].rm_eo)
- ++offset;
- }
-
- return matches.size () ? true : false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool RX::match (
- std::vector <int>& start,
- std::vector <int>& end,
- const std::string& in)
-{
- if (! _compiled)
- compile ();
-
- regmatch_t rm[2];
- int offset = 0;
- int length = in.length ();
- while (regexec (&_regex, in.c_str () + offset, 2, &rm[0], 0) == 0 &&
- offset < length)
- {
- start.push_back (rm[0].rm_so + offset);
- end.push_back (rm[0].rm_eo + offset);
- offset += rm[0].rm_eo;
-
- // Protection against zero-width patterns causing infinite loops.
- if (rm[0].rm_so == rm[0].rm_eo)
- ++offset;
- }
-
- return start.size () ? true : false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_RX
-#define INCLUDED_RX
-
-#include <string>
-#include <vector>
-#include <regex.h>
-
-class RX
-{
-public:
- RX ();
- RX (const std::string&, bool caseSensitive = true);
- RX (const RX&);
- ~RX ();
- RX& operator= (const RX&);
-
- bool match (const std::string&);
- bool match (std::vector<std::string>&, const std::string&);
- bool match (std::vector <int>&, std::vector <int>&, const std::string&);
-
-private:
- void compile ();
-
-private:
- bool _compiled {false};
- std::string _pattern {};
- bool _case_sensitive {false};
- regex_t _regex;
-};
-
-#endif
-
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <JSON.h>
-#include <utf8.h>
-#include <sstream>
-#include <stdlib.h>
-#include <inttypes.h>
-#include <errno.h>
-
-////////////////////////////////////////////////////////////////////////////////
-bool json::SAX::parse (const std::string& input, SAX::Sink& sink)
-{
- sink.eventDocStart ();
- std::string::size_type cursor = 0;
- ignoreWhitespace (input, cursor);
- if (isObject (input, cursor, sink) ||
- isArray (input, cursor, sink))
- {
- ignoreWhitespace (input, cursor);
- if (cursor < input.length ())
- error ("Error: extra characters found at position ", cursor);
-
- sink.eventDocEnd ();
- return true;
- }
-
- error ("Error: Missing '{' or '[' at position ", cursor);
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Complete Unicode whitespace list.
-//
-// http://en.wikipedia.org/wiki/Whitespace_character
-// Updated 2015-09-13
-void json::SAX::ignoreWhitespace (const std::string& input, std::string::size_type& cursor)
-{
- int c = input[cursor];
- while (c == 0x0020 || // space Common Separator, space
- c == 0x0009 || // Common Other, control HT, Horizontal Tab
- c == 0x000A || // Common Other, control LF, Line feed
- c == 0x000B || // Common Other, control VT, Vertical Tab
- c == 0x000C || // Common Other, control FF, Form feed
- c == 0x000D || // Common Other, control CR, Carriage return
- c == 0x0085 || // Common Other, control NEL, Next line
- c == 0x00A0 || // no-break space Common Separator, space
- c == 0x1680 || // ogham space mark Ogham Separator, space
- c == 0x180E || // mongolian vowel separator Mongolian Separator, space
- c == 0x2000 || // en quad Common Separator, space
- c == 0x2001 || // em quad Common Separator, space
- c == 0x2002 || // en space Common Separator, space
- c == 0x2003 || // em space Common Separator, space
- c == 0x2004 || // three-per-em space Common Separator, space
- c == 0x2005 || // four-per-em space Common Separator, space
- c == 0x2006 || // six-per-em space Common Separator, space
- c == 0x2007 || // figure space Common Separator, space
- c == 0x2008 || // punctuation space Common Separator, space
- c == 0x2009 || // thin space Common Separator, space
- c == 0x200A || // hair space Common Separator, space
- c == 0x200B || // zero width space
- c == 0x200C || // zero width non-joiner
- c == 0x200D || // zero width joiner
- c == 0x2028 || // line separator Common Separator, line
- c == 0x2029 || // paragraph separator Common Separator, paragraph
- c == 0x202F || // narrow no-break space Common Separator, space
- c == 0x205F || // medium mathematical space Common Separator, space
- c == 0x2060 || // word joiner
- c == 0x3000) // ideographic space Common Separator, space
- {
- c = input[++cursor];
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// object := '{' [<pair> [, <pair> ...]] '}'
-bool json::SAX::isObject (const std::string& input, std::string::size_type& cursor, SAX::Sink& sink)
-{
- ignoreWhitespace (input, cursor);
- auto backup = cursor;
-
- if (isLiteral (input, '{', cursor))
- {
- sink.eventObjectStart ();
- int counter = 0;
-
- if (isPair (input, cursor, sink))
- {
- ++counter;
- while (isLiteral (input, ',', cursor) &&
- isPair (input, cursor, sink))
- {
- ++counter;
- }
- }
-
- ignoreWhitespace (input, cursor);
- if (isLiteral (input, '}', cursor))
- {
- sink.eventObjectEnd (counter);
- return true;
- }
- else
- error ("Error: Missing '}' at position ", cursor);
- }
-
- cursor = backup;
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// array := '[' [<value> [, <value> ...]] ']'
-bool json::SAX::isArray (const std::string& input, std::string::size_type& cursor, SAX::Sink& sink)
-{
- ignoreWhitespace (input, cursor);
- auto backup = cursor;
-
- if (isLiteral (input, '[', cursor))
- {
- sink.eventArrayStart ();
- int counter = 0;
-
- if (isValue (input, cursor, sink))
- {
- ++counter;
- while (isLiteral (input, ',', cursor) &&
- isValue (input, cursor, sink))
- {
- ++counter;
- }
- }
-
- ignoreWhitespace (input, cursor);
- if (isLiteral (input, ']', cursor))
- {
- sink.eventArrayEnd (counter);
- return true;
- }
- else
- error ("Error: Missing ']' at position ", cursor);
- }
-
- cursor = backup;
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// pair := <string> ':' <value>
-bool json::SAX::isPair (const std::string& input, std::string::size_type& cursor, SAX::Sink& sink)
-{
- ignoreWhitespace (input, cursor);
- auto backup = cursor;
-
- if (isKey (input, cursor, sink))
- {
- if (isLiteral (input, ':', cursor))
- {
- if (isValue (input, cursor, sink))
- return true;
-
- error ("Error: Missing value at position ", cursor);
- }
- else
- error ("Error: Missing ':' at position ", cursor);
- }
-
- cursor = backup;
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// value := <string>
-// | <number>
-// | <object>
-// | <array>
-// | 'true'
-// | 'false'
-// | 'null'
-bool json::SAX::isValue (const std::string& input, std::string::size_type& cursor, SAX::Sink& sink)
-{
- ignoreWhitespace (input, cursor);
-
- return isString (input, cursor, sink) ||
- isNumber (input, cursor, sink) ||
- isObject (input, cursor, sink) ||
- isArray (input, cursor, sink) ||
- isBool (input, cursor, sink) ||
- isNull (input, cursor, sink);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool json::SAX::isKey (const std::string& input, std::string::size_type& cursor, SAX::Sink& sink)
-{
- ignoreWhitespace (input, cursor);
-
- std::string value;
- if (isStringValue (input, cursor, value))
- {
- sink.eventName (value);
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool json::SAX::isString (const std::string& input, std::string::size_type& cursor, SAX::Sink& sink)
-{
- ignoreWhitespace (input, cursor);
-
- std::string value;
- if (isStringValue (input, cursor, value))
- {
- sink.eventValueString (value);
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// string := '"' [<chars> ...] '"'
-// chars := <unicode>
-// | '\"'
-// | '\\'
-// | '\/'
-// | '\b'
-// | '\f'
-// | '\n'
-// | '\r'
-// | '\t'
-// | \uXXXX
-bool json::SAX::isStringValue (const std::string& input, std::string::size_type& cursor, std::string& value)
-{
- auto backup = cursor;
-
- if (isLiteral (input, '"', cursor))
- {
- std::string word;
- int c;
- while ((c = input[cursor]))
- {
- // EOS.
- if (c == '"')
- {
- ++cursor;
- value = word;
- return true;
- }
-
- // Unicode \uXXXX codepoint.
- else if (input[cursor + 0] == '\\' &&
- input[cursor + 1] == 'u' &&
- isHexDigit (input[cursor + 2]) &&
- isHexDigit (input[cursor + 3]) &&
- isHexDigit (input[cursor + 4]) &&
- isHexDigit (input[cursor + 5]))
- {
- word += utf8_character (
- hexToInt (
- input[cursor + 2],
- input[cursor + 3],
- input[cursor + 4],
- input[cursor + 5]));
- cursor += 6;
- }
-
- // An escaped thing.
- else if (c == '\\')
- {
- c = input[++cursor];
- switch (c)
- {
- case '"': word += (char) 0x22; ++cursor; break;
- case '\'': word += (char) 0x27; ++cursor; break;
- case '\\': word += (char) 0x5C; ++cursor; break;
- case 'b': word += (char) 0x08; ++cursor; break;
- case 'f': word += (char) 0x0C; ++cursor; break;
- case 'n': word += (char) 0x0A; ++cursor; break;
- case 'r': word += (char) 0x0D; ++cursor; break;
- case 't': word += (char) 0x09; ++cursor; break;
- case 'v': word += (char) 0x0B; ++cursor; break;
-
- // This pass-through default case means that anything can be escaped
- // harmlessly. In particular 'quote' is included, if it not one of the
- // above characters.
- default: word += (char) c; ++cursor; break;
- }
- }
-
- // Ordinary character.
- else
- {
- word += (char) c;
- ++cursor;
- }
- }
-
- error ("Error: Missing '\"' at position ", cursor);
- }
-
- cursor = backup;
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// number := <int> [<frac>] [<exp>]
-bool json::SAX::isNumber (const std::string& input, std::string::size_type& cursor, SAX::Sink& sink)
-{
- ignoreWhitespace (input, cursor);
- auto backup = cursor;
-
- std::string integerPart;
- if (isInt (input, cursor, integerPart))
- {
- std::string fractionalPart;
- isFrac (input, cursor, fractionalPart);
-
- std::string exponentPart;
- isExp (input, cursor, exponentPart);
-
- // Does it fit in a long?
- std::string combined = integerPart + fractionalPart + exponentPart;
- char* end;
- long longValue = strtol (combined.c_str (), &end, 10);
- if (! *end && errno != ERANGE)
- {
- sink.eventValueInt (longValue);
- return true;
- }
-
- // Does it fit in an unsigned long?
- unsigned long ulongValue = strtoul (combined.c_str (), &end, 10);
- if (! *end && errno != ERANGE)
- {
- sink.eventValueUint (ulongValue);
- return true;
- }
-
- // If the above fail, allow this one to be capped at imax.
- double doubleValue = strtod (combined.c_str (), &end);
- if (! *end)
- {
- sink.eventValueDouble (doubleValue);
- return true;
- }
- }
-
- cursor = backup;
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// int := ['-'] <digits>
-bool json::SAX::isInt (const std::string& input, std::string::size_type& cursor, std::string& value)
-{
- auto backup = cursor;
-
- isLiteral (input, '-', cursor);
- if (isDigits (input, cursor))
- {
- value = input.substr (backup, cursor - backup);
- return true;
- }
-
- // No restore necessary.
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// frac := '.' <digits>
-bool json::SAX::isFrac (const std::string& input, std::string::size_type& cursor, std::string& value)
-{
- auto backup = cursor;
-
- if (isLiteral (input, '.', cursor) &&
- isDigits (input, cursor))
- {
- value = input.substr (backup, cursor - backup);
- return true;
- }
-
- cursor = backup;
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// digits := <digit> [<digit> ...]
-bool json::SAX::isDigits (const std::string& input, std::string::size_type& cursor)
-{
- int c = input[cursor];
- if (isDecDigit (c))
- {
- c = input[++cursor];
-
- while (isDecDigit (c))
- c = input[++cursor];
-
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// digit := 0x30 ('0') .. 0x39 ('9')
-bool json::SAX::isDecDigit (int c)
-{
- return c >= 0x30 && c <= 0x39;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// hex := 0x30 ('0') .. 0x39 ('9')
-bool json::SAX::isHexDigit (int c)
-{
- return (c >= 0x30 && c <= 0x39) ||
- (c >= 0x61 && c <= 0x66) ||
- (c >= 0x41 && c <= 0x46);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// exp := <e> <digits>
-bool json::SAX::isExp (const std::string& input, std::string::size_type& cursor, std::string& value)
-{
- auto backup = cursor;
-
- if (isE (input, cursor) &&
- isDigits (input, cursor))
- {
- value = input.substr (backup, cursor - backup);
- return true;
- }
-
- cursor = backup;
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// e := e
-// | e+
-// | e-
-// | E
-// | E+
-// | E-
-bool json::SAX::isE (const std::string& input, std::string::size_type& cursor)
-{
- int c = input[cursor];
- if (c == 'e' ||
- c == 'E')
- {
- c = input[++cursor];
-
- if (c == '+' ||
- c == '-')
- {
- ++cursor;
- }
-
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool json::SAX::isBool (const std::string& input, std::string::size_type& cursor, SAX::Sink& sink)
-{
- ignoreWhitespace (input, cursor);
-
- if (input[cursor + 0] == 't' &&
- input[cursor + 1] == 'r' &&
- input[cursor + 2] == 'u' &&
- input[cursor + 3] == 'e')
- {
- cursor += 4;
- sink.eventValueBool (true);
- return true;
- }
- else if (input[cursor + 0] == 'f' &&
- input[cursor + 1] == 'a' &&
- input[cursor + 2] == 'l' &&
- input[cursor + 3] == 's' &&
- input[cursor + 4] == 'e')
- {
- cursor += 5;
- sink.eventValueBool (false);
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool json::SAX::isNull (const std::string& input, std::string::size_type& cursor, SAX::Sink& sink)
-{
- ignoreWhitespace (input, cursor);
-
- if (input[cursor + 0] == 'n' &&
- input[cursor + 1] == 'u' &&
- input[cursor + 2] == 'l' &&
- input[cursor + 3] == 'l')
- {
- cursor += 4;
- sink.eventValueNull ();
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool json::SAX::isLiteral (const std::string& input, char literal, std::string::size_type& cursor)
-{
- ignoreWhitespace (input, cursor);
-
- if (input[cursor] == literal)
- {
- ++cursor;
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Converts '0' -> 0
-// '9' -> 9
-// 'a'/'A' -> 10
-// 'f'/'F' -> 15
-int json::SAX::hexToInt (int c)
-{
- if (c >= 0x30 && c <= 0x39) return (c - 0x30);
- else if (c >= 0x41 && c <= 0x46) return (c - 0x41 + 10);
- else return (c - 0x61 + 10);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int json::SAX::hexToInt (int c0, int c1, int c2, int c3)
-{
- return (hexToInt (c0) << 12) +
- (hexToInt (c1) << 8) +
- (hexToInt (c2) << 4) +
- hexToInt (c3);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void json::SAX::error (const std::string& message, std::string::size_type cursor)
-{
- std::stringstream error;
- error << message << cursor;
- throw error.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <Table.h>
-#include <shared.h>
-#include <format.h>
-#include <utf8.h>
-#include <unistd.h>
-
-////////////////////////////////////////////////////////////////////////////////
-void Table::add (const std::string& col, bool alignLeft, bool wrap)
-{
- _columns.push_back (col);
- _align.push_back (alignLeft);
- _wrap.push_back (wrap);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Table::addRow ()
-{
- _data.push_back (std::vector <std::string> (_columns.size (), ""));
- _color.push_back (std::vector <Color> (_columns.size (), Color::nocolor));
- _oddness.push_back (_data.size () % 2 ? true : false);
- return _data.size () - 1;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Table::addRowOdd ()
-{
- _data.push_back (std::vector <std::string> (_columns.size (), ""));
- _color.push_back (std::vector <Color> (_columns.size (), Color::nocolor));
- _oddness.push_back (true);
- return _data.size () - 1;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Table::addRowEven ()
-{
- _data.push_back (std::vector <std::string> (_columns.size (), ""));
- _color.push_back (std::vector <Color> (_columns.size (), Color::nocolor));
- _oddness.push_back (false);
- return _data.size () - 1;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Table::set (int row, int col, const std::string& value, const Color color)
-{
- _data[row][col] = value;
-
- if (color.nontrivial ())
- _color[row][col] = color;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Table::set (int row, int col, int value, const Color color)
-{
- std::string string_value = format (value);
- _data[row][col] = string_value;
-
- if (color.nontrivial ())
- _color[row][col] = color;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Table::set (int row, int col, const Color color)
-{
- if (color.nontrivial ())
- _color[row][col] = color;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Table::render ()
-{
- // Piped output disables color, unless overridden.
- if (! _forceColor &&
- ! isatty (STDOUT_FILENO))
- {
- _header = Color ("");
- _odd = Color ("");
- _even = Color ("");
- _intra_odd = Color ("");
- _intra_even = Color ("");
- _extra_odd = Color ("");
- _extra_even = Color ("");
-
- for (auto& row : _color)
- for (auto& col : row)
- col = Color ("");
-
- _underline_headers = true;
- }
-
- // Determine minimal, ideal column widths.
- std::vector <int> minimal;
- std::vector <int> ideal;
- for (unsigned int col = 0; col < _columns.size (); ++col)
- {
- // Headers factor in to width calculations.
- unsigned int global_min = utf8_text_width (_columns[col]);
- unsigned int global_ideal = global_min;
-
- for (unsigned int row = 0; row < _data.size (); ++row)
- {
- // Determine minimum and ideal width for this column.
- unsigned int min = 0;
- unsigned int ideal = 0;
- measureCell (_data[row][col], min, ideal);
-
- if (min > global_min) global_min = min;
- if (ideal > global_ideal) global_ideal = ideal;
- }
-
- minimal.push_back (global_min);
- ideal.push_back (global_ideal);
- }
-
- // Sum the minimal widths.
- int sum_minimal = 0;
- for (const auto& c : minimal)
- sum_minimal += c;
-
- // Sum the ideal widths.
- int sum_ideal = 0;
- for (const auto& c : ideal)
- sum_ideal += c;
-
- // Calculate final column widths.
- int overage = _width
- - _left_margin
- - (2 * _extra_padding)
- - ((_columns.size () - 1) * _intra_padding);
-
- std::vector <int> widths;
- if (sum_ideal <= overage)
- widths = ideal;
- else if (sum_minimal > overage || overage < 0)
- widths = minimal;
- else if (overage > 0)
- {
- widths = minimal;
- overage -= sum_minimal;
-
- // Spread 'overage' among columns where width[i] < ideal[i]
- while (overage)
- {
- for (unsigned int i = 0; i < _columns.size () && overage; ++i)
- {
- if (widths[i] < ideal[i])
- {
- ++widths[i];
- --overage;
- }
- }
- }
- }
-
- // Compose column headers.
- unsigned int max_lines = 0;
- std::vector <std::vector <std::string>> headers;
- for (unsigned int c = 0; c < _columns.size (); ++c)
- {
- headers.push_back ({});
- renderCell (headers[c], _columns[c], widths[c], _align[c], _wrap[c], _header);
-
- if (headers[c].size () > max_lines)
- max_lines = headers[c].size ();
- }
-
- // Output string.
- std::string out;
- _lines = 0;
-
- // Render column headers.
- std::string left_margin = std::string (_left_margin, ' ');
- std::string extra = std::string (_extra_padding, ' ');
- std::string intra = std::string (_intra_padding, ' ');
-
- std::string extra_odd = _extra_odd.colorize (extra);
- std::string extra_even = _extra_even.colorize (extra);
- std::string intra_odd = _intra_odd.colorize (intra);
- std::string intra_even = _intra_even.colorize (intra);
-
- for (unsigned int i = 0; i < max_lines; ++i)
- {
- out += left_margin + extra;
-
- for (unsigned int c = 0; c < _columns.size (); ++c)
- {
- if (c)
- out += intra;
-
- if (headers[c].size () < max_lines - i)
- out += _header.colorize (std::string (widths[c], ' '));
- else
- out += headers[c][i];
- }
-
- out += extra;
-
- // Trim right.
- out.erase (out.find_last_not_of (" ") + 1);
- out += '\n';
-
- // Stop if the line limit is exceeded.
- if (++_lines >= _truncate_lines && _truncate_lines != 0)
- return out;
- }
-
- // Underline headers with ------ if necessary.
- if (_underline_headers)
- {
- out += left_margin + extra;
- for (unsigned int c = 0; c < _columns.size (); ++c)
- {
- if (c)
- out += intra;
-
- out += _header.colorize (std::string (widths[c], '-'));
- }
-
- out += '\n';
- }
-
- // Compose, render columns, in sequence.
- _rows = 0;
- std::vector <std::vector <std::string>> cells;
- for (unsigned int row = 0; row < _data.size (); ++row)
- {
- max_lines = 0;
-
- // Alternate rows based on |s % 2|
- auto oddness = _oddness[row];
- Color row_color = oddness ? _odd : _even;
-
- // TODO row_color.blend (provided color);
- // TODO Problem: colors for columns are specified, not rows,
- // therefore there are only cell colors, not intra colors.
-
- Color cell_color;
- for (unsigned int col = 0; col < _columns.size (); ++col)
- {
- cell_color = row_color;
- cell_color.blend (_color[row][col]);
-
- cells.push_back (std::vector <std::string> ());
- renderCell (cells[col], _data[row][col], widths[col], _align[col], _wrap[col], cell_color);
-
- if (cells[col].size () > max_lines)
- max_lines = cells[col].size ();
-
- if (_obfuscate)
- for (auto& value : cells[col])
- value = obfuscateText (value);
- }
-
- for (unsigned int i = 0; i < max_lines; ++i)
- {
- out += left_margin + (oddness ? extra_odd : extra_even);
-
- for (unsigned int col = 0; col < _columns.size (); ++col)
- {
- if (col)
- {
- if (row_color.nontrivial ())
- out += row_color.colorize (intra);
- else
- out += (oddness ? intra_odd : intra_even);
- }
-
- if (i < cells[col].size ())
- out += cells[col][i];
- else
- {
- cell_color = row_color;
- cell_color.blend (_color[row][col]);
- out += cell_color.colorize (std::string (widths[col], ' '));
- }
- }
-
- out += (oddness ? extra_odd : extra_even);
-
- // Trim right.
- out.erase (out.find_last_not_of (" ") + 1);
- out += '\n';
-
- // Stop if the line limit is exceeded.
- if (++_lines >= _truncate_lines && _truncate_lines != 0)
- return out;
- }
-
- cells.clear ();
-
- // Stop if the row limit is exceeded.
- if (++_rows >= _truncate_rows && _truncate_rows != 0)
- return out;
- }
-
- return out;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Table::measureCell (
- const std::string& data,
- unsigned int& minimum,
- unsigned int& maximum) const
-{
- std::string stripped = Color::strip (data);
- maximum = longestLine (stripped);
- minimum = longestWord (stripped);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Table::renderCell (
- std::vector <std::string>& lines,
- const std::string& value,
- int width,
- bool alignLeft,
- bool wrap,
- const Color& color) const
-{
- if (wrap)
- {
- std::vector <std::string> raw;
- wrapText (raw, value, width, false);
-
- for (const auto& line : raw)
- if (alignLeft)
- lines.push_back (
- color.colorize (
- leftJustify (line, width)));
- else
- lines.push_back (
- color.colorize (
- rightJustify (line, width)));
- }
- else
- {
- if (alignLeft)
- lines.push_back (
- color.colorize (
- leftJustify (value, width)));
- else
- lines.push_back (
- color.colorize (
- rightJustify (value, width)));
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_TABLE
-#define INCLUDED_TABLE
-
-#include <string>
-#include <vector>
-#include <Color.h>
-
-class Table
-{
-public:
- // View specifications.
- void add (const std::string& col, bool alignLeft = true, bool wrap = true);
- void width (int width) { _width = width; }
- void leftMargin (int margin) { _left_margin = margin; }
- void colorHeader (const Color& c) { _header = c; }
- void colorOdd (const Color& c) { _odd = c; }
- void colorEven (const Color& c) { _even = c; }
- void intraPadding (int padding) { _intra_padding = padding; }
- void intraColorOdd (const Color& c) { _intra_odd = c; }
- void intraColorEven (const Color& c) { _intra_even = c; }
- void extraPadding (int padding) { _extra_padding = padding; }
- void extraColorOdd (const Color& c) { _extra_odd = c; }
- void extraColorEven (const Color& c) { _extra_even = c; }
- void truncateLines (int n) { _truncate_lines = n; }
- void truncateRows (int n) { _truncate_rows = n; }
- void forceColor () { _forceColor = true; }
- void obfuscate () { _obfuscate = true; }
- void underlineHeaders () { _underline_headers = true; }
- int lines () { return _lines; }
- int rows () { return (int) _data.size (); }
-
- // Data provision.
- int addRow ();
- int addRowOdd ();
- int addRowEven ();
- void set (int, int, const std::string&, const Color color = Color::nocolor);
- void set (int, int, int, const Color color = Color::nocolor);
- void set (int, int, const Color);
-
- // View rendering.
- std::string render ();
-
-private:
- void measureCell (const std::string&, unsigned int&, unsigned int&) const;
- void renderCell (std::vector <std::string>&, const std::string&, int, bool, bool, const Color&) const;
-
-private:
- std::vector <std::vector <std::string>> _data;
- std::vector <std::vector <Color>> _color;
- std::vector <std::string> _columns;
- std::vector <bool> _align;
- std::vector <bool> _wrap;
- std::vector <bool> _oddness;
- int _width {0};
- int _left_margin {0};
- Color _header {0};
- Color _odd {0};
- Color _even {0};
- int _intra_padding {1};
- Color _intra_odd {0};
- Color _intra_even {0};
- int _extra_padding {0};
- Color _extra_odd {0};
- Color _extra_even {0};
- int _truncate_lines {0};
- int _truncate_rows {0};
- bool _forceColor {false};
- bool _obfuscate {false};
- bool _underline_headers {false};
- int _lines {0};
- int _rows {0};
-};
-
-#endif
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <Timer.h>
-
-////////////////////////////////////////////////////////////////////////////////
-Timer::Timer ()
-{
- start ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Timer::start ()
-{
- _start = std::chrono::high_resolution_clock::now ();
- _end = {};
- _running = true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Timer::stop ()
-{
- if (_running)
- {
- _end = std::chrono::high_resolution_clock::now ();
- _running = false;
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-double Timer::total_s () const
-{
- auto endpoint = _end;
- if (_running)
- endpoint = std::chrono::high_resolution_clock::now ();
-
- return std::chrono::duration_cast<std::chrono::seconds>(endpoint - _start).count();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-double Timer::total_ms () const
-{
- auto endpoint = _end;
- if (_running)
- endpoint = std::chrono::high_resolution_clock::now ();
-
- return std::chrono::duration_cast<std::chrono::milliseconds>(endpoint - _start).count();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-double Timer::total_us () const
-{
- auto endpoint = _end;
- if (_running)
- endpoint = std::chrono::high_resolution_clock::now ();
-
- return std::chrono::duration_cast<std::chrono::microseconds>(endpoint - _start).count();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-double Timer::total_ns () const
-{
- auto endpoint = _end;
- if (_running)
- endpoint = std::chrono::high_resolution_clock::now ();
-
- return std::chrono::duration_cast<std::chrono::nanoseconds>(endpoint - _start).count();
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_TIMER
-#define INCLUDED_TIMER
-
-#include <string>
-#include <chrono>
-
-class Timer
-{
-public:
- Timer ();
- ~Timer () = default;
-
- void start ();
- void stop ();
-
- double total_s () const;
- double total_ms () const;
- double total_us () const;
- double total_ns () const;
-
-private:
- std::chrono::time_point<std::chrono::high_resolution_clock> _start {};
- std::chrono::time_point<std::chrono::high_resolution_clock> _end {};
- bool _running {false};
-};
-
-#endif
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2010 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <Tree.h>
-#include <algorithm>
-#include <iostream>
-#include <sstream>
-#include <shared.h>
-#include <format.h>
-
-////////////////////////////////////////////////////////////////////////////////
-// - Tree, Branch and Node are synonymous.
-// - A Tree may contain any number of branches.
-// - A Branch may contain any number of name/value pairs, unique by name.
-// - The destructor will delete all branches recursively.
-// - Tree::enumerate is a snapshot, and is invalidated by modification.
-// - Branch sequence is preserved.
-void Tree::addBranch (std::shared_ptr <Tree> branch)
-{
- if (! branch)
- throw "Failed to allocate memory for parse tree.";
-
- _branches.push_back (branch);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Tree::removeBranch (std::shared_ptr <Tree> branch)
-{
- for (auto i = _branches.begin (); i != _branches.end (); ++i)
- {
- if (branch == *i)
- {
- _branches.erase (i);
- return;
- }
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Tree::removeAllBranches ()
-{
- _branches.erase (_branches.begin (), _branches.end ());
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Tree::replaceBranch (std::shared_ptr <Tree> from, std::shared_ptr <Tree> to)
-{
- for (unsigned int i = 0; i < _branches.size (); ++i)
- {
- if (_branches[i] == from)
- {
- _branches[i] = to;
- return;
- }
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Accessor for attributes.
-void Tree::attribute (const std::string& name, const std::string& value)
-{
- _attributes[name] = value;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Accessor for attributes.
-void Tree::attribute (const std::string& name, const int value)
-{
- _attributes[name] = format (value);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Accessor for attributes.
-void Tree::attribute (const std::string& name, const double value)
-{
- _attributes[name] = format (value, 1, 8);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Accessor for attributes.
-std::string Tree::attribute (const std::string& name)
-{
- // Prevent autovivification.
- auto i = _attributes.find (name);
- if (i != _attributes.end ())
- return i->second;
-
- return "";
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Tree::removeAttribute (const std::string& name)
-{
- _attributes.erase (name);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Recursively builds a list of std::shared_ptr <Tree> objects, left to right,
-// depth first. The reason for the depth-first enumeration is that a client may
-// wish to traverse the tree and delete nodes. With a depth-first iteration,
-// this is a safe mechanism, and a node pointer will never be dereferenced after
-// it has been deleted.
-void Tree::enumerate (std::vector <std::shared_ptr <Tree>>& all) const
-{
- for (auto& i : _branches)
- {
- i->enumerate (all);
- all.push_back (i);
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool Tree::hasTag (const std::string& tag) const
-{
- return std::find (_tags.begin (), _tags.end (), tag) != _tags.end ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Tree::tag (const std::string& tag)
-{
- if (! hasTag (tag))
- _tags.push_back (tag);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void Tree::unTag (const std::string& tag)
-{
- auto i = std::find (_tags.begin (), _tags.end (), tag);
- if (i != _tags.end ())
- _tags.erase (i);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Tree::countTags () const
-{
- return _tags.size ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int Tree::count () const
-{
- // This branch.
- int total = 1;
-
- // Recurse and count the branches.
- for (auto& i : _branches)
- total += i->count ();
-
- return total;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::shared_ptr <Tree> Tree::find (const std::string& path)
-{
- std::vector <std::string> elements = split (path, '/');
-
- // Must start at the trunk.
- auto cursor = std::make_shared <Tree> (*this);
- auto it = elements.begin ();
- if (cursor->_name != *it)
- return nullptr;
-
- // Perhaps the trunk is what is needed?
- if (elements.size () == 1)
- return cursor;
-
- // Now look for the next branch.
- for (++it; it != elements.end (); ++it)
- {
- bool found = false;
-
- // If the cursor has a branch that matches *it, proceed.
- for (auto i = cursor->_branches.begin (); i != cursor->_branches.end (); ++i)
- {
- if ((*i)->_name == *it)
- {
- cursor = *i;
- found = true;
- break;
- }
- }
-
- if (! found)
- return nullptr;
- }
-
- return cursor;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Tree::dumpNode (
- const std::shared_ptr <Tree> t,
- int depth) const
-{
- std::stringstream out;
-
- // Dump node
- for (int i = 0; i < depth; ++i)
- out << " ";
-
- out
- // Useful for debugging tree node new/delete errors.
- // << std::hex << t << " "
- << "\033[1m" << t->_name << "\033[0m";
-
- // Dump attributes.
- std::string atts;
- for (auto& a : t->_attributes)
- {
- if (atts != "")
- atts += ' ';
-
- atts += a.first + "='\033[33m" + a.second + "\033[0m'";
- }
-
- if (atts.length ())
- out << ' ' << atts;
-
- // Dump tags.
- std::string tags;
- for (auto& tag : t->_tags)
- {
- if (tags.length ())
- tags += ' ';
-
- tags += "\033[32m" + tag + "\033[0m";
- }
-
- if (tags.length ())
- out << ' ' << tags;
- out << '\n';
-
- // Recurse for branches.
- for (auto& b : t->_branches)
- out << dumpNode (b, depth + 1);
-
- return out.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string Tree::dump () const
-{
- std::stringstream out;
- out << "Tree (" << count () << " nodes)\n"
- << dumpNode (std::make_shared <Tree> (*this), 1);
-
- return out.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2010 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_TREE
-#define INCLUDED_TREE
-
-#include <map>
-#include <vector>
-#include <string>
-#include <memory>
-
-class Tree;
-
-class Tree
-{
-public:
- void addBranch (std::shared_ptr <Tree>);
- void removeBranch (std::shared_ptr <Tree>);
- void removeAllBranches ();
- void replaceBranch (std::shared_ptr <Tree>, std::shared_ptr <Tree>);
-
- void attribute (const std::string&, const std::string&);
- void attribute (const std::string&, const int);
- void attribute (const std::string&, const double);
- std::string attribute (const std::string&);
- void removeAttribute (const std::string&);
-
- void enumerate (std::vector <std::shared_ptr <Tree>>& all) const;
-
- bool hasTag (const std::string&) const;
- void tag (const std::string&);
- void unTag (const std::string&);
- int countTags () const;
-
- int count () const;
-
- std::shared_ptr <Tree> find (const std::string&);
-
- std::string dump () const;
-
-private:
- std::string dumpNode (const std::shared_ptr <Tree>, int) const;
-
-public:
- std::string _name {"Unknown"}; // Name.
- std::vector <std::shared_ptr <Tree>> _branches {}; // Children.
- std::map <std::string, std::string> _attributes {}; // Attributes (name->value).
- std::vector <std::string> _tags {}; // Tags (tag, tag ...).
-};
-
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <format.h>
-#include <utf8.h>
-#include <algorithm>
-#include <sstream>
-#include <iostream>
-#include <iomanip>
-#include <cctype>
-#include <strings.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/select.h>
-#include <time.h>
-#include <csignal>
-#include <cmath>
-
-////////////////////////////////////////////////////////////////////////////////
-const std::string format (std::string& value)
-{
- return value;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-const std::string format (const char* value)
-{
- std::string s (value);
- return s;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-const std::string formatHex (int value)
-{
- std::stringstream s;
- s.setf (std::ios::hex, std::ios::basefield);
- s << value;
- return s.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-const std::string format (float value, int width, int precision)
-{
- std::stringstream s;
- s.width (width);
- s.precision (precision);
- if (0 < value && value < 1)
- {
- // For value close to zero, width - 2 (2 accounts for the first zero and
- // the dot) is the number of digits after zero that are significant
- double factor = 1;
- for (int i = 2; i < width; i++)
- factor *= 10;
- value = roundf (value * factor) / factor;
- }
- s << value;
- return s.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-const std::string format (double value, int width, int precision)
-{
- std::stringstream s;
- s.width (width);
- s.precision (precision);
- if (0 < value && value < 1)
- {
- // For value close to zero, width - 2 (2 accounts for the first zero and
- // the dot) is the number of digits after zero that are significant
- double factor = 1;
- for (int i = 2; i < width; i++)
- factor *= 10;
- value = round (value * factor) / factor;
- }
- s << value;
- return s.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-const std::string format (double value)
-{
- std::stringstream s;
- s << std::fixed << value;
- return s.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-void replace_positional (
- std::string& fmt,
- const std::string& from,
- const std::string& to)
-{
- std::string::size_type pos = 0;
- while ((pos = fmt.find (from, pos)) != std::string::npos)
- {
- fmt.replace (pos, from.length (), to);
- pos += to.length ();
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string leftJustify (const int input, const int width)
-{
- std::stringstream s;
- s << input;
- std::string output = s.str ();
- return output + std::string (width - output.length (), ' ');
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string leftJustify (const std::string& input, const int width)
-{
- return input + std::string (width - utf8_text_width (input), ' ');
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string rightJustifyZero (const int input, const int width)
-{
- std::stringstream s;
- s << std::setw (width) << std::setfill ('0') << input;
- return s.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string rightJustify (const int input, const int width)
-{
- std::stringstream s;
- s << std::setw (width) << std::setfill (' ') << input;
- return s.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string rightJustify (const std::string& input, const int width)
-{
- unsigned int len = utf8_text_width (input);
- return (((unsigned int) width > len)
- ? std::string (width - len, ' ')
- : "")
- + input;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string commify (const std::string& data)
-{
- // First scan for decimal point and end of digits.
- int decimalPoint = -1;
- int end = -1;
-
- int i;
- for (int i = 0; i < (int) data.length (); ++i)
- {
- if (isdigit (data[i]))
- end = i;
-
- if (data[i] == '.')
- decimalPoint = i;
- }
-
- std::string result;
- if (decimalPoint != -1)
- {
- // In reverse order, transfer all digits up to, and including the decimal
- // point.
- for (i = (int) data.length () - 1; i >= decimalPoint; --i)
- result += data[i];
-
- int consecutiveDigits = 0;
- for (; i >= 0; --i)
- {
- if (isdigit (data[i]))
- {
- result += data[i];
-
- if (++consecutiveDigits == 3 && i && isdigit (data[i - 1]))
- {
- result += ',';
- consecutiveDigits = 0;
- }
- }
- else
- result += data[i];
- }
- }
- else
- {
- // In reverse order, transfer all digits up to, but not including the last
- // digit.
- for (i = (int) data.length () - 1; i > end; --i)
- result += data[i];
-
- int consecutiveDigits = 0;
- for (; i >= 0; --i)
- {
- if (isdigit (data[i]))
- {
- result += data[i];
-
- if (++consecutiveDigits == 3 && i && isdigit (data[i - 1]))
- {
- result += ',';
- consecutiveDigits = 0;
- }
- }
- else
- result += data[i];
- }
- }
-
- // reverse result into data.
- std::string done;
- for (int i = (int) result.length () - 1; i >= 0; --i)
- done += result[i];
-
- return done;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Convert a quantity in bytes to a more readable format.
-std::string formatBytes (size_t bytes)
-{
- char formatted[24];
-
- if (bytes >= 995000000) sprintf (formatted, "%.1f GiB", bytes / 1000000000.0);
- else if (bytes >= 995000) sprintf (formatted, "%.1f MiB", bytes / 1000000.0);
- else if (bytes >= 995) sprintf (formatted, "%.1f KiB", bytes / 1000.0);
- else sprintf (formatted, "%d B", (int)bytes);
-
- return commify (formatted);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Convert a quantity in seconds to a more readable format.
-std::string formatTime (time_t seconds)
-{
- char formatted[24];
- float days = (float) seconds / 86400.0;
-
- if (seconds >= 86400 * 365) sprintf (formatted, "%.1f y", (days / 365.0));
- else if (seconds >= 86400 * 84) sprintf (formatted, "%1d mo", (int) (days / 30));
- else if (seconds >= 86400 * 13) sprintf (formatted, "%d wk", (int) (float) (days / 7.0));
- else if (seconds >= 86400) sprintf (formatted, "%d d", (int) days);
- else if (seconds >= 3600) sprintf (formatted, "%d h", (int) (seconds / 3600));
- else if (seconds >= 60) sprintf (formatted, "%d m", (int) (seconds / 60));
- else if (seconds >= 1) sprintf (formatted, "%d s", (int) seconds);
- else strcpy (formatted, "-");
-
- return std::string (formatted);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string printable (const std::string& input)
-{
- // Sanitize 'message'.
- std::string sanitized = input;
- std::string::size_type bad;
- while ((bad = sanitized.find ("\r")) != std::string::npos)
- sanitized.replace (bad, 1, "\\r");
-
- while ((bad = sanitized.find ("\n")) != std::string::npos)
- sanitized.replace (bad, 1, "\\n");
-
- while ((bad = sanitized.find ("\f")) != std::string::npos)
- sanitized.replace (bad, 1, "\\f");
-
- while ((bad = sanitized.find ("\t")) != std::string::npos)
- sanitized.replace (bad, 1, "\\t");
-
- while ((bad = sanitized.find ("\v")) != std::string::npos)
- sanitized.replace (bad, 1, "\\v");
-
- return sanitized;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string printable (char input)
-{
- // Sanitize 'message'.
- char stringized[2] = {0};
- stringized[0] = input;
-
- std::string sanitized = stringized;
- switch (input)
- {
- case '\r': sanitized = "\\r"; break;
- case '\n': sanitized = "\\n"; break;
- case '\f': sanitized = "\\f"; break;
- case '\t': sanitized = "\\t"; break;
- case '\v': sanitized = "\\v"; break;
- default: sanitized = input; break;
- }
-
- return sanitized;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Iterate over the input, converting text to 'x'.
-// Does not modify color codes.
-std::string obfuscateText (const std::string& input)
-{
- std::stringstream output;
- std::string::size_type i = 0;
- int character;
- bool inside = false;
-
- while ((character = utf8_next_char (input, i)))
- {
- if (inside)
- {
- output << (char) character;
-
- if (character == 'm')
- inside = false;
- }
- else
- {
- if (character == 033)
- inside = true;
-
- if (inside || character == ' ')
- output << (char) character;
- else
- output << 'x';
- }
- }
-
- return output.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_FORMAT
-#define INCLUDED_FORMAT
-
-#include <sstream>
-#include <algorithm>
-#include <string>
-#include <vector>
-
-const std::string format (std::string&);
-const std::string format (const char*);
-const std::string formatHex (int);
-const std::string format (float, int, int);
-const std::string format (double, int, int);
-const std::string format (double);
-
-void replace_positional (std::string&, const std::string&, const std::string&);
-
-template<typename T>
-const std::string format (T value)
-{
- std::stringstream s;
- s << value;
- return s.str ();
-}
-
-template<typename T>
-const std::string format (int fmt_num, const std::string& fmt, T arg)
-{
- std::string output = fmt;
- replace_positional (output, '{' + format (fmt_num) + '}', format (arg));
- return output;
-}
-
-template<typename T, typename... Args>
-const std::string format (int fmt_num, const std::string& fmt, T arg, Args... args)
-{
- const std::string fmt_replaced (format (fmt_num, fmt, arg));
- return format (fmt_num+1, fmt_replaced, args...);
-}
-
-template<typename... Args>
-const std::string format (const std::string& fmt, Args... args)
-{
- return format (1, fmt, args...);
-}
-
-std::string leftJustify (const int, const int);
-std::string leftJustify (const std::string&, const int);
-std::string rightJustifyZero (const int, const int);
-std::string rightJustify (const int, const int);
-std::string rightJustify (const std::string&, const int);
-
-std::string commify (const std::string&);
-std::string formatBytes (size_t);
-std::string formatTime (time_t);
-std::string printable (const std::string&);
-std::string printable (char);
-
-std::string obfuscateText (const std::string&);
-
-#endif
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <shared.h>
-#include <cctype>
-
-static bool isPort (const std::string&, unsigned int&);
-static bool isChar (const std::string&, char, unsigned int&);
-static bool isEOS (const std::string&, unsigned int&);
-static bool isIPv4Block (const std::string&, unsigned int&);
-static bool isIPv4BlockSet (const std::string&, unsigned int&);
-static bool isIPv6Block (const std::string&, unsigned int&);
-static bool isIPv6BlockSet (const std::string&, unsigned int&);
-
-////////////////////////////////////////////////////////////////////////////////
-static bool isPort (const std::string& input, unsigned int& c)
-{
- auto start = c;
- while (std::isdigit (input[c]))
- ++c;
-
- return c - start > 0 &&
- c - start < 6;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-static bool isChar (const std::string& input, char character, unsigned int& c)
-{
- if (input[c] == character)
- {
- ++c;
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-static bool isEOS (const std::string& input, unsigned int& c)
-{
- return c >= input.length ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-static bool isIPv4Block (const std::string& input, unsigned int& c)
-{
- auto start = c;
- while (std::isdigit (input[c]))
- ++c;
-
- if (c - start > 0 &&
- c - start < 4)
- {
- auto byte = std::stoi (input.substr (start, c - start));
- if (byte < 256)
- return true;
- }
-
- c = start;
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-static bool isIPv4BlockSet (const std::string& input, unsigned int& c)
-{
- auto start = c;
-
- if (isIPv4Block (input, c) &&
- isChar (input, '.', c) &&
- isIPv4Block (input, c) &&
- isChar (input, '.', c) &&
- isIPv4Block (input, c) &&
- isChar (input, '.', c) &&
- isIPv4Block (input, c))
- {
-
- return true;
- }
-
- c = start;
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-static bool isIPv6Block (const std::string& input, unsigned int& c)
-{
- auto start = c;
- while (std::isxdigit (input[c]))
- ++c;
-
- if (c - start > 0 &&
- c - start < 5)
- {
- return true;
- }
-
- c = start;
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// at least one non-empty block, or '::'
-// at least two colons
-// 8 or less blocks
-static bool isIPv6BlockSet (const std::string& input, unsigned int& c)
-{
- auto start = c;
- int count_colons {0};
- int count_blocks {0};
-
- while (1)
- {
- if (isEOS (input, c))
- {
- if (c == start)
- return false;
-
- break;
- }
-
- else if (isChar (input, ':', c))
- ++count_colons;
-
- else if (isIPv4BlockSet (input, c))
- ++count_blocks;
-
- else if (isIPv6Block (input, c))
- ++count_blocks;
-
- else if (isChar (input, '.', c) ||
- isChar (input, ']', c))
- {
- --c;
- break;
- }
-
- else
- break;
- }
-
- if (count_colons >= 2 &&
- count_colons <= 7 &&
- ((count_blocks == 0 && input.substr (start, c) == "::") || count_blocks >= 1) &&
- count_blocks <= 8)
- {
- return true;
- }
-
- c = start;
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// <address>:<port>
-// <address>
-bool isIPv4Address (const std::string& input, std::string& address, int& port)
-{
- unsigned int c = 0;
- if (isIPv4BlockSet (input, c))
- {
- auto colon = c;
- if (isChar (input, ':', c))
- if (! isPort (input, c))
- return false;
-
- if (isEOS (input, c))
- {
- address = input.substr (0, std::min (c, colon));
- if (! isEOS (input, colon))
- port = std::stoi (input.substr (colon + 1));
-
- return true;
- }
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// [<address>]:<port>
-// <address>
-bool isIPv6Address (const std::string& input, std::string& address, int& port)
-{
- unsigned int c = 0;
-
- if (isChar (input, '[', c) &&
- isIPv6BlockSet (input, c) &&
- isChar (input, ']', c))
- {
- auto colon = c;
- if (isChar (input, ':', c) &&
- isPort (input, c) &&
- isEOS (input, c))
- {
- address = input.substr (1, colon - 2);
- port = std::stoi (input.substr (colon + 1));
- return true;
- }
- }
-
- c = 0;
- if (isIPv6BlockSet (input, c) &&
- isEOS (input, c))
- {
- address = input;
- port = 0;
- return true;
- }
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-
-#include <iostream>
-#include <Lexer.h>
-
-int main (int argc, char** argv)
-{
- for (auto i = 1; i < argc; i++)
- {
- std::cout << "argument '" << argv[i] << "'\n";
-
- Lexer l (argv[i]);
- std::string token;
- Lexer::Type type;
- while (l.token (token, type))
- std::cout << " token '" << token << "' " << Lexer::typeToString (type) << "\n";
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <shared.h>
-#include <utf8.h>
-#include <algorithm>
-#include <sstream>
-#include <iostream>
-#include <iomanip>
-#include <cctype>
-#include <strings.h>
-#include <unistd.h>
-#include <sys/select.h>
-#include <cerrno>
-#include <csignal>
-#include <cmath>
-#include <cstring>
-#include <sys/wait.h>
-#include <format.h>
-
-///////////////////////////////////////////////////////////////////////////////
-void wrapText (
- std::vector <std::string>& lines,
- const std::string& text,
- const int width,
- bool hyphenate)
-{
- std::string line;
- unsigned int offset = 0;
- while (extractLine (line, text, width, hyphenate, offset))
- lines.push_back (line);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Split in a separator. Two adjacent separators means empty token.
-std::vector <std::string> split (const std::string& input, const char delimiter)
-{
- std::vector <std::string> results;
- std::string::size_type start = 0;
- std::string::size_type i;
- while ((i = input.find (delimiter, start)) != std::string::npos)
- {
- results.push_back (input.substr (start, i - start));
- start = i + 1;
- }
-
- if (input.length ())
- results.push_back (input.substr (start));
-
- return results;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Split on words. Adjacent separators collapsed.
-std::vector <std::string> split (const std::string& input)
-{
- static std::string delims = " \t\n\f\r";
- std::vector <std::string> results;
-
- std::string::size_type start = 0;
- std::string::size_type end;
- while ((start = input.find_first_not_of (delims, start)) != std::string::npos)
- {
- if ((end = input.find_first_of (delims, start)) != std::string::npos)
- {
- results.push_back (input.substr (start, end - start));
- start = end;
- }
- else
- {
- results.push_back (input.substr (start));
- start = std::string::npos;
- }
- }
-
- return results;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string join (
- const std::string& separator,
- const std::vector<int>& items)
-{
- std::stringstream s;
- auto size = items.size ();
- for (unsigned int i = 0; i < size; ++i)
- {
- if (i)
- s << separator;
-
- s << items[i];
- }
-
- return s.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string join (
- const std::string& separator,
- const std::vector<std::string>& items)
-{
- std::stringstream s;
- auto size = items.size ();
- for (unsigned int i = 0; i < size; ++i)
- {
- if (i)
- s << separator;
-
- s << items[i];
- }
-
- return s.str ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string str_replace (
- const std::string &str,
- const std::string& search,
- const std::string& replacement)
-{
- std::string modified {str};
- std::string::size_type pos = 0;
- while ((pos = modified.find (search, pos)) != std::string::npos)
- {
- modified.replace (pos, search.length (), replacement);
- pos += replacement.length ();
- }
-
- return modified;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string trim (const std::string& input, const std::string& edible)
-{
- auto start = input.find_first_not_of (edible);
- auto end = input.find_last_not_of (edible);
-
- if (start == std::string::npos)
- return "";
-
- if (end == std::string::npos)
- return input.substr (start);
-
- return input.substr (start, end - start + 1);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string ltrim (const std::string& input, const std::string& edible)
-{
- auto start = input.find_first_not_of (edible);
- if (start == std::string::npos)
- return "";
-
- return input.substr (start);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string rtrim (const std::string& input, const std::string& edible)
-{
- if (input.find_first_not_of (edible) == std::string::npos)
- return "";
-
- auto end = input.find_last_not_of (edible);
- if (end == std::string::npos)
- return input;
-
- return input.substr (0, end + 1);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int longestWord (const std::string& input)
-{
- int longest = 0;
- int length = 0;
- std::string::size_type i = 0;
- int character;
-
- while ((character = utf8_next_char (input, i)))
- {
- if (character == ' ')
- {
- if (length > longest)
- longest = length;
-
- length = 0;
- }
- else
- length += mk_wcwidth (character);
- }
-
- if (length > longest)
- longest = length;
-
- return longest;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int longestLine (const std::string& input)
-{
- int longest = 0;
- int length = 0;
- std::string::size_type i = 0;
- int character;
-
- while ((character = utf8_next_char (input, i)))
- {
- if (character == '\n')
- {
- if (length > longest)
- longest = length;
-
- length = 0;
- }
- else
- length += mk_wcwidth (character);
- }
-
- if (length > longest)
- longest = length;
-
- return longest;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Walk the input text looking for a break point. A break point is one of:
-// - EOS
-// - \n
-// - last space before 'length' characters
-// - last punctuation (, ; . :) before 'length' characters, even if not
-// followed by a space
-// - first 'length' characters
-//
-// text "one two three\n four"
-// bytes 0123456789012 3456789
-// characters 1234567890a23 4567890
-//
-// leading_ws
-// ws ^ ^ ^^
-// punct
-// break ^
-bool extractLine (
- std::string& line,
- const std::string& text,
- int width,
- bool hyphenate,
- unsigned int& offset)
-{
- // Terminate processing.
- // Note: bytes vs bytes.
- if (offset >= text.length ())
- return false;
-
- std::string::size_type last_last_bytes = offset;
- std::string::size_type last_bytes = offset;
- std::string::size_type bytes = offset;
- unsigned int last_ws = 0;
- int character;
- int char_width = 0;
- int line_width = 0;
- while (1)
- {
- last_last_bytes = last_bytes;
- last_bytes = bytes;
- character = utf8_next_char (text, bytes);
-
- if (character == 0 ||
- character == '\n')
- {
- line = text.substr (offset, last_bytes - offset);
- offset = bytes;
- break;
- }
- else if (character == ' ')
- last_ws = last_bytes;
-
- char_width = mk_wcwidth (character);
- if (line_width + char_width > width)
- {
- int last_last_character = text[last_last_bytes];
- int last_character = text[last_bytes];
-
- // [case 1] one| two --> last_last != 32, last == 32, ws == 0
- if (last_last_character != ' ' &&
- last_character == ' ')
- {
- line = text.substr (offset, last_bytes - offset);
- offset = last_bytes + 1;
- break;
- }
-
- // [case 2] one |two --> last_last == 32, last != 32, ws != 0
- else if (last_last_character == ' ' &&
- last_character != ' ' &&
- last_ws != 0)
- {
- line = text.substr (offset, last_bytes - offset - 1);
- offset = last_bytes;
- break;
- }
-
- else if (last_last_character != ' ' &&
- last_character != ' ')
- {
- // [case 3] one t|wo --> last_last != 32, last != 32, ws != 0
- if (last_ws != 0)
- {
- line = text.substr (offset, last_ws - offset);
- offset = last_ws + 1;
- break;
- }
- // [case 4] on|e two --> last_last != 32, last != 32, ws == 0
- else
- {
- if (hyphenate)
- {
- line = text.substr (offset, last_bytes - offset - 1) + '-';
- offset = last_last_bytes;
- }
- else
- {
- line = text.substr (offset, last_bytes - offset);
- offset = last_bytes;
- }
- }
-
- break;
- }
- }
-
- line_width += char_width;
- }
-
- return true;
-}
-/*
-
-TODO Resolve above against below, which is from Taskwarrior 2.6.0, and known to
- be wrong.
-////////////////////////////////////////////////////////////////////////////////
-// Break UTF8 text into chunks no more than width characters.
-bool extractLine (
- std::string& line,
- const std::string& text,
- int width,
- bool hyphenate,
- unsigned int& offset)
-{
- // Terminate processing.
- if (offset >= text.length ())
- return false;
-
- int line_length {0};
- int character {0};
- std::string::size_type lastWordEnd {std::string::npos};
- bool something {false};
- std::string::size_type cursor {offset};
- std::string::size_type prior_cursor {offset};
- while ((character = utf8_next_char (text, cursor)))
- {
- // Premature EOL.
- if (character == '\n')
- {
- line = text.substr (offset, line_length);
- offset = cursor;
- return true;
- }
-
- if (! Lexer::isWhitespace (character))
- {
- something = true;
- if (! text[cursor] || Lexer::isWhitespace (text[cursor]))
- lastWordEnd = prior_cursor;
- }
-
- line_length += mk_wcwidth (character);
-
- if (line_length >= width)
- {
- // Backtrack to previous word end.
- if (lastWordEnd != std::string::npos)
- {
- // Eat one WS after lastWordEnd.
- std::string::size_type lastBreak = lastWordEnd;
- utf8_next_char (text, lastBreak);
-
- // Position offset at following char.
- std::string::size_type nextStart = lastBreak;
- utf8_next_char (text, nextStart);
-
- line = text.substr (offset, lastBreak - offset);
- offset = nextStart;
- return true;
- }
-
- // No backtrack, possible hyphenation.
- else if (hyphenate)
- {
- line = text.substr (offset, prior_cursor - offset) + '-';
- offset = prior_cursor;
- return true;
- }
-
- // No hyphenation, just truncation.
- else
- {
- line = text.substr (offset, cursor - offset);
- offset = cursor;
- return true;
- }
- }
-
- // Hindsight.
- prior_cursor = cursor;
- }
-
- // Residual text.
- if (something)
- {
- line = text.substr (offset, cursor - offset);
- offset = cursor;
- return true;
- }
-
- return false;
-}
-*/
-
-////////////////////////////////////////////////////////////////////////////////
-bool compare (
- const std::string& left,
- const std::string& right,
- bool sensitive /*= true*/)
-{
- // Use strcasecmp if required.
- if (! sensitive)
- return strcasecmp (left.c_str (), right.c_str ()) == 0 ? true : false;
-
- // Otherwise, just use std::string::operator==.
- return left == right;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool closeEnough (
- const std::string& reference,
- const std::string& attempt,
- unsigned int minLength /* = 0 */)
-{
- // An exact match is accepted first.
- if (compare (reference, attempt, false))
- return true;
-
- // A partial match will suffice.
- if (attempt.length () < reference.length () &&
- attempt.length () >= minLength)
- return compare (reference.substr (0, attempt.length ()), attempt, false);
-
- return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int matchLength (
- const std::string& left,
- const std::string& right)
-{
- int pos = 0;
- while (left[pos] &&
- right[pos] &&
- left[pos] == right[pos])
- ++pos;
-
- return pos;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string::size_type find (
- const std::string& text,
- const std::string& pattern,
- bool sensitive)
-{
- return find (text, pattern, 0, sensitive);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string::size_type find (
- const std::string& text,
- const std::string& pattern,
- std::string::size_type begin,
- bool sensitive)
-{
- // Implement a sensitive find, which is really just a loop withing a loop,
- // comparing lower-case versions of each character in turn.
- if (!sensitive)
- {
- // Handle empty pattern.
- const char* p = pattern.c_str ();
- size_t len = pattern.length ();
- if (len == 0)
- return 0;
-
- // Handle bad begin.
- if (begin >= text.length ())
- return std::string::npos;
-
- // Evaluate these once, for performance reasons.
- const char* start = text.c_str ();
- const char* t = start + begin;
- const char* end = start + text.size ();
-
- for (; t <= end - len; ++t)
- {
- int diff = 0;
- for (size_t i = 0; i < len; ++i)
- if ((diff = tolower (t[i]) - tolower (p[i])))
- break;
-
- // diff == 0 means there was no break from the loop, which only occurs
- // when a difference is detected. Therefore, the loop terminated, and
- // diff is zero.
- if (diff == 0)
- return t - start;
- }
-
- return std::string::npos;
- }
-
- // Otherwise, just use std::string::find.
- return text.find (pattern, begin);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string lowerCase (const std::string& input)
-{
- std::string output {input};
- std::transform (output.begin (), output.end (), output.begin (), tolower);
- return output;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string upperCase (const std::string& input)
-{
- std::string output {input};
- std::transform (output.begin (), output.end (), output.begin (), toupper);
- return output;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string upperCaseFirst (const std::string& input)
-{
- std::string output {input};
- output[0] = toupper (output[0]);
- return output;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int autoComplete (
- const std::string& partial,
- const std::vector<std::string>& list,
- std::vector<std::string>& matches,
- int minimum/* = 1*/)
-{
- matches.clear ();
-
- // Handle trivial case.
- unsigned int length = partial.length ();
- if (length)
- {
- for (auto& item : list)
- {
- // An exact match is a special case. Assume there is only one exact match
- // and return immediately.
- if (partial == item)
- {
- matches.clear ();
- matches.push_back (item);
- return 1;
- }
-
- // Maintain a list of partial matches.
- else if (length >= (unsigned) minimum &&
- length <= item.length () &&
- partial == item.substr (0, length))
- matches.push_back (item);
- }
- }
-
- return matches.size ();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Uses std::getline, because std::cin eats leading whitespace, and that means
-// that if a newline is entered, std::cin eats it and never returns from the
-// "std::cin >> answer;" line, but it does display the newline. This way, with
-// std::getline, the newline can be detected, and the prompt re-written.
-static void signal_handler (int s)
-{
- if (s == SIGINT)
- {
- std::cout << "\n\nInterrupted: No changes made.\n";
- exit (1);
- }
-}
-
-bool confirm (const std::string& question)
-{
- std::vector <std::string> options {"yes", "no"};
- std::vector <std::string> matches;
-
- signal (SIGINT, signal_handler);
-
- do
- {
- std::cout << question
- << " (yes/no) ";
-
- std::string answer {""};
- std::getline (std::cin, answer);
- answer = std::cin.eof () ? "no" : lowerCase (trim (answer));
-
- autoComplete (answer, options, matches, 1); // Hard-coded 1.
- }
- while (! std::cin.eof () && matches.size () != 1);
-
- signal (SIGINT, SIG_DFL);
- return matches.size () == 1 && matches[0] == "yes" ? true : false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Run a binary with args, capturing output.
-int execute (
- const std::string& executable,
- const std::vector <std::string>& args,
- const std::string& input,
- std::string& output)
-{
- pid_t pid;
- int pin[2], pout[2];
- fd_set rfds, wfds;
- struct timeval tv;
- int select_retval, read_retval, write_retval;
- char buf[16384];
- unsigned int written;
- const char* input_cstr = input.c_str ();
-
- if (signal (SIGPIPE, SIG_IGN) == SIG_ERR) // Handled locally with EPIPE.
- throw std::string (std::strerror (errno));
-
- if (pipe (pin) == -1)
- throw std::string (std::strerror (errno));
-
- if (pipe (pout) == -1)
- throw std::string (std::strerror (errno));
-
- if ((pid = fork ()) == -1)
- throw std::string (std::strerror (errno));
-
- if (pid == 0)
- {
- // This is only reached in the child
- close (pin[1]); // Close the write end of the input pipe.
- close (pout[0]); // Close the read end of the output pipe.
-
- // Parent writes to pin[1]. Set read end pin[0] as STDIN for child.
- if (dup2 (pin[0], STDIN_FILENO) == -1)
- throw std::string (std::strerror (errno));
- close (pin[0]);
-
- // Parent reads from pout[0]. Set write end pout[1] as STDOUT for child.
- if (dup2 (pout[1], STDOUT_FILENO) == -1)
- throw std::string (std::strerror (errno));
- close (pout[1]);
-
- // Add executable as argv[0] and NULL-terminate the array for execvp().
- char** argv = new char* [args.size () + 2];
- argv[0] = (char*) executable.c_str ();
- for (unsigned int i = 0; i < args.size (); ++i)
- argv[i+1] = (char*) args[i].c_str ();
-
- argv[args.size () + 1] = NULL;
- _exit (execvp (executable.c_str (), argv));
- }
-
- // This is only reached in the parent
- close (pin[0]); // Close the read end of the input pipe.
- close (pout[1]); // Close the write end of the output pipe.
-
- if (input.size () == 0)
- {
- // Nothing to send to the child, close the pipe early.
- close (pin[1]);
- }
-
- output = "";
- read_retval = -1;
- written = 0;
- while (read_retval != 0 || input.size () != written)
- {
- FD_ZERO (&rfds);
- if (read_retval != 0)
- FD_SET (pout[0], &rfds);
-
- FD_ZERO (&wfds);
- if (input.size () != written)
- FD_SET (pin[1], &wfds);
-
- // On Linux, tv may be overwritten by select(). Reset it each time.
- // NOTE: Timeout chosen arbitrarily - we don't time out execute() calls.
- // select() is run over and over again unless the child exits or closes
- // its pipes.
- tv.tv_sec = 5;
- tv.tv_usec = 0;
-
- select_retval = select (std::max (pout[0], pin[1]) + 1, &rfds, &wfds, NULL, &tv);
-
- if (select_retval == -1)
- throw std::string (std::strerror (errno));
-
- // Write data to child's STDIN
- if (FD_ISSET (pin[1], &wfds))
- {
- write_retval = write (pin[1], input_cstr + written, input.size () - written);
- if (write_retval == -1)
- {
- if (errno == EPIPE)
- {
- // Child died (or closed the pipe) before reading all input.
- // We don't really care; pretend we wrote it all.
- write_retval = input.size () - written;
- }
- else
- {
- throw std::string (std::strerror (errno));
- }
- }
- written += write_retval;
-
- if (written == input.size ())
- {
- // Let the child know that no more input is coming by closing the pipe.
- close (pin[1]);
- }
- }
-
- // Read data from child's STDOUT
- if (FD_ISSET (pout[0], &rfds))
- {
- read_retval = read (pout[0], &buf, sizeof (buf) - 1);
- if (read_retval == -1)
- throw std::string (std::strerror (errno));
-
- buf[read_retval] = '\0';
- output += buf;
- }
- }
-
- close (pout[0]); // Close the read end of the output pipe.
-
- int status = -1;
- if (wait (&status) == -1)
- throw std::string (std::strerror (errno));
-
- if (WIFEXITED (status))
- {
- status = WEXITSTATUS (status);
- }
- else
- {
- throw std::string ("Error: Could not get Hook exit status!");
- }
-
- if (signal (SIGPIPE, SIG_DFL) == SIG_ERR) // We're done, return to default.
- throw std::string (std::strerror (errno));
-
- return status;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-std::string osName ()
-{
-#if defined (DARWIN)
- return "Darwin";
-#elif defined (SOLARIS)
- return "Solaris";
-#elif defined (CYGWIN)
- return "Cygwin";
-#elif defined (HAIKU)
- return "Haiku";
-#elif defined (OPENBSD)
- return "OpenBSD";
-#elif defined (FREEBSD)
- return "FreeBSD";
-#elif defined (NETBSD)
- return "NetBSD";
-#elif defined (LINUX)
- return "Linux";
-#elif defined (KFREEBSD)
- return "GNU/kFreeBSD";
-#elif defined (GNUHURD)
- return "GNU/Hurd";
-#else
- return "<unknown>";
-#endif
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// 16.8 Predefined macro names [cpp.predefined]
-//
-// The following macro names shall be defined by the implementation:
-//
-// __cplusplus
-// The name __cplusplus is defined to the value 201402L when compiling a C++
-// translation unit.156
-//
-// ---
-// 156) It is intended that future versions of this standard will replace the
-// value of this macro with a greater value. Non-conforming compilers should
-// use a value with at most five decimal digits.
-std::string cppCompliance ()
-{
-#ifdef __cplusplus
- auto level = __cplusplus;
-
- if (level == 199711) return "C++98/03";
- else if (level == 201103) return "C++11";
- else if (level == 201402) return "C++14";
-
- // This is a hack. Replace with correct value on standard publication.
- else if (level > 201700) return "C++17";
-
- // Unknown, just show the value.
- else if (level > 99999) return format (__cplusplus);
-#endif
-
- // No C++.
- return "non-compliant";
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_SHARED
-#define INCLUDED_SHARED
-
-#include <sstream>
-#include <algorithm>
-#include <string>
-#include <vector>
-
-// shared.cpp, Non-UTF-8 aware.
-void wrapText (std::vector <std::string>&, const std::string&, const int, bool);
-int longestWord (const std::string&);
-int longestLine (const std::string&);
-bool extractLine (std::string&, const std::string&, int, bool, unsigned int&);
-std::vector <std::string> split (const std::string&, const char);
-std::vector <std::string> split (const std::string&);
-std::string join (const std::string&, const std::vector<int>&);
-std::string join (const std::string&, const std::vector<std::string>&);
-std::string str_replace (const std::string&, const std::string&, const std::string&);
-std::string trim (const std::string&, const std::string& edible = " \t\n\f\r");
-std::string ltrim (const std::string&, const std::string& edible = " \t\n\f\r");
-std::string rtrim (const std::string&, const std::string& edible = " \t\n\f\r");
-bool compare (const std::string&, const std::string&, bool sensitive = true);
-bool closeEnough (const std::string&, const std::string&, unsigned int minLength = 0);
-int matchLength (const std::string&, const std::string&);
-std::string::size_type find (const std::string&, const std::string&, bool sensitive = true);
-std::string::size_type find (const std::string&, const std::string&, std::string::size_type, bool sensitive = true);
-
-// List operations.
-template <class T> void listDiff (
- const T& left, const T& right, T& leftOnly, T& rightOnly)
-{
- leftOnly.clear ();
- for (auto& l : left)
- if (std::find (right.begin (), right.end (), l) == right.end ())
- leftOnly.push_back (l);
-
- rightOnly.clear ();
- for (auto& r : right)
- if (std::find (left.begin (), left.end (), r) == left.end ())
- rightOnly.push_back (r);
-}
-
-template <class T> T listIntersect (
- const T& left, const T& right)
-{
- T intersection;
- for (auto& l : left)
- if (std::find (right.begin (), right.end (), l) != right.end ())
- intersection.push_back (l);
-
- return intersection;
-}
-
-std::string lowerCase (const std::string&);
-std::string upperCase (const std::string&);
-std::string upperCaseFirst (const std::string&);
-
-int autoComplete (const std::string&, const std::vector<std::string>&, std::vector<std::string>&, int minimum = 1);
-bool confirm (const std::string&);
-
-int execute (const std::string&, const std::vector <std::string>&, const std::string&, std::string&);
-std::string osName ();
-std::string cppCompliance ();
-
-// ip.cpp
-bool isIPv4Address (const std::string&, std::string&, int&);
-bool isIPv6Address (const std::string&, std::string&, int&);
-
-#endif
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2015 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <unicode.h>
-#include <cwctype>
-
-////////////////////////////////////////////////////////////////////////////////
-// Complete Unicode whitespace list.
-bool unicodeWhitespace (unsigned int c)
-{
- return unicodeHorizontalWhitespace (c) ||
- unicodeVerticalWhitespace (c);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Complete Unicode whitespace list.
-//
-// http://en.wikipedia.org/wiki/Whitespace_character
-// Updated 2015-09-13
-// Static
-//
-// TODO This list should be derived from the Unicode database.
-bool unicodeHorizontalWhitespace (unsigned int c)
-{
- return (c == 0x0020 || // space Common Separator, space
- c == 0x0009 || // Common Other, control HT, Horizontal Tab
- c == 0x00A0 || // no-break space Common Separator, space
- c == 0x1680 || // ogham space mark Ogham Separator, space
- c == 0x180E || // mongolian vowel separator Mongolian Separator, space
- c == 0x2000 || // en quad Common Separator, space
- c == 0x2001 || // em quad Common Separator, space
- c == 0x2002 || // en space Common Separator, space
- c == 0x2003 || // em space Common Separator, space
- c == 0x2004 || // three-per-em space Common Separator, space
- c == 0x2005 || // four-per-em space Common Separator, space
- c == 0x2006 || // six-per-em space Common Separator, space
- c == 0x2007 || // figure space Common Separator, space
- c == 0x2008 || // punctuation space Common Separator, space
- c == 0x2009 || // thin space Common Separator, space
- c == 0x200A || // hair space Common Separator, space
- c == 0x200B || // zero width space
- c == 0x200C || // zero width non-joiner
- c == 0x200D || // zero width joiner
- c == 0x202F || // narrow no-break space Common Separator, space
- c == 0x205F || // medium mathematical space Common Separator, space
- c == 0x2060 || // word joiner
- c == 0x3000); // ideographic space Common Separator, space
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Complete Unicode whitespace list.
-//
-// http://en.wikipedia.org/wiki/Whitespace_character
-// Updated 2015-09-13
-// Static
-//
-// TODO This list should be derived from the Unicode database.
-bool unicodeVerticalWhitespace (unsigned int c)
-{
- return (c == 0x000A || // Common Other, control LF, Line feed
- c == 0x000B || // Common Other, control VT, Vertical Tab
- c == 0x000C || // Common Other, control FF, Form feed
- c == 0x000D || // Common Other, control CR, Carriage return
- c == 0x0085 || // Common Other, control NEL, Next line
- c == 0x2028 || // line separator Common Separator, line
- c == 0x2029); // paragraph separator Common Separator, paragraph
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool unicodePunctuation (unsigned int c)
-{
- return iswpunct (c) ? true : false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-bool unicodeAlpha (unsigned int c)
-{
- return iswprint (c) &&
- ! iswpunct (c) &&
- ! unicodeWhitespace (c);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// TODO Needs better definition.
-bool unicodeLatinAlpha (unsigned int c)
-{
- return (c >= 'A' && c <= 'Z') ||
- (c >= 'a' && c <= 'z');
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Digits 0-9.
-//
-// TODO This list should be derived from the Unicode database.
-bool unicodeLatinDigit (unsigned int c)
-{
- return c >= 0x30 && c <= 0x39;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Digits 0-9 a-f A-F.
-bool unicodeHexDigit (unsigned int c)
-{
- return (c >= '0' && c <= '9') ||
- (c >= 'a' && c <= 'f') ||
- (c >= 'A' && c <= 'F');
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2015 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_UNICODE
-#define INCLUDED_UNICODE
-
-bool unicodeWhitespace (unsigned int);
-bool unicodeHorizontalWhitespace (unsigned int);
-bool unicodeVerticalWhitespace (unsigned int);
-bool unicodePunctuation (unsigned int);
-bool unicodeAlpha (unsigned int);
-bool unicodeLatinAlpha (unsigned int);
-bool unicodeLatinDigit (unsigned int);
-bool unicodeHexDigit (unsigned int);
-
-#endif
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2013 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <utf8.h>
-
-////////////////////////////////////////////////////////////////////////////////
-// Converts '0' -> 0
-// '9' -> 9
-// 'a'/'A' -> 10
-// 'f'/'F' -> 15
-#define XDIGIT(x) ((x) >= '0' && (x) <= '9' ? ((x) - '0') : \
- (x) >= 'a' && (x) <= 'f' ? ((x) + 10 - 'a') : \
- (x) >= 'A' && (x) <= 'F' ? ((x) + 10 - 'A') : 0)
-
-////////////////////////////////////////////////////////////////////////////////
-// Note: Assumes 4-digit hex codepoints:
-// xxxx
-// \uxxxx
-// U+xxxx
-unsigned int utf8_codepoint (const std::string& input)
-{
- unsigned int codepoint = 0;
- int length = input.length ();
-
- // U+xxxx, \uxxxx
- if (length >= 6 &&
- ((input[0] == 'U' && input[1] == '+') ||
- (input[0] == '\\' && input[1] == 'u')))
- {
- codepoint = XDIGIT (input[2]) << 12 |
- XDIGIT (input[3]) << 8 |
- XDIGIT (input[4]) << 4 |
- XDIGIT (input[5]);
- }
- else if (length >= 4)
- {
- codepoint = XDIGIT (input[0]) << 12 |
- XDIGIT (input[1]) << 8 |
- XDIGIT (input[2]) << 4 |
- XDIGIT (input[3]);
- }
-
- return codepoint;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Iterates along a UTF8 string.
-// - argument i counts bytes advanced through the string
-// - returns the next character
-unsigned int utf8_next_char (const std::string& input, std::string::size_type& i)
-{
- if (input[i] == '\0')
- return 0;
-
- // How many bytes in the sequence?
- int length = utf8_sequence (input[i]);
- i += length;
-
- // 0xxxxxxx -> 0xxxxxxx
- if (length == 1)
- return input[i - 1];
-
- // 110yyyyy 10xxxxxx -> 00000yyy yyxxxxxx
- if (length == 2)
- return ((input[i - 2] & 0x1F) << 6) +
- (input[i - 1] & 0x3F);
-
- // 1110zzzz 10yyyyyy 10xxxxxx -> zzzzyyyy yyxxxxxx
- if (length == 3)
- return ((input[i - 3] & 0xF) << 12) +
- ((input[i - 2] & 0x3F) << 6) +
- (input[i - 1] & 0x3F);
-
- // 11110www 10zzzzzz 10yyyyyy 10xxxxxx -> 000wwwzz zzzzyyyy yyxxxxxx
- if (length == 4)
- return ((input[i - 4] & 0x7) << 18) +
- ((input[i - 3] & 0x3F) << 12) +
- ((input[i - 2] & 0x3F) << 6) +
- (input[i - 1] & 0x3F);
-
- // Default: pretend as though it's a single character.
- // TODO Or should this throw?
- return input[i - 1];
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// http://en.wikipedia.org/wiki/UTF-8
-std::string utf8_character (unsigned int codepoint)
-{
- char sequence[5] {};
-
- // 0xxxxxxx -> 0xxxxxxx
- if (codepoint < 0x80)
- {
- sequence[0] = codepoint;
- }
-
- // 00000yyy yyxxxxxx -> 110yyyyy 10xxxxxx
- else if (codepoint < 0x800)
- {
- sequence[0] = 0xC0 | (codepoint & 0x7C0) >> 6;
- sequence[1] = 0x80 | (codepoint & 0x3F);
- }
-
- // zzzzyyyy yyxxxxxx -> 1110zzzz 10yyyyyy 10xxxxxx
- else if (codepoint < 0x10000)
- {
- sequence[0] = 0xE0 | (codepoint & 0xF000) >> 12;
- sequence[1] = 0x80 | (codepoint & 0xFC0) >> 6;
- sequence[2] = 0x80 | (codepoint & 0x3F);
- }
-
- // 000wwwzz zzzzyyyy yyxxxxxx -> 11110www 10zzzzzz 10yyyyyy 10xxxxxx
- else if (codepoint < 0x110000)
- {
- sequence[0] = 0xF0 | (codepoint & 0x1C0000) >> 18;
- sequence[1] = 0x80 | (codepoint & 0x03F000) >> 12;
- sequence[2] = 0x80 | (codepoint & 0x0FC0) >> 6;
- sequence[3] = 0x80 | (codepoint & 0x3F);
- }
-
- return std::string (sequence);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-int utf8_sequence (unsigned int character)
-{
- if ((character & 0xE0) == 0xC0)
- return 2;
-
- if ((character & 0xF0) == 0xE0)
- return 3;
-
- if ((character & 0xF8) == 0xF0)
- return 4;
-
- return 1;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Length of a string in characters.
-unsigned int utf8_length (const std::string& str)
-{
- int byteLength = str.length ();
- int charLength = byteLength;
- const char* data = str.data ();
-
- // Decrement the number of bytes for each byte that matches 0b10??????
- // this way only the first byte of any utf8 sequence is counted.
- for (int i = 0; i < byteLength; i++)
- {
- // Extract the first two bits and check whether they are 10
- if ((data[i] & 0xC0) == 0x80)
- charLength--;
- }
-
- return charLength;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Width of a string in character cells.
-unsigned int utf8_width (const std::string& str)
-{
- unsigned int length = 0;
- std::string::size_type i = 0;
- unsigned int c;
- while ((c = utf8_next_char (str, i)))
- {
- // Control characters, and more especially newline characters, make
- // mk_wcwidth() return -1. Ignore that, thereby "adding zero" to length.
- // Since control characters are not displayed in reports, this is a valid
- // choice.
- int l = mk_wcwidth (c);
- if (l != -1)
- length += l;
- }
-
- return length;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-unsigned int utf8_text_length (const std::string& str)
-{
- int byteLength = str.length ();
- int charLength = byteLength;
- const char* data = str.data ();
- bool in_color = false;
-
- // Decrement the number of bytes for each byte that matches 0b10??????
- // this way only the first byte of any utf8 sequence is counted.
- for (int i = 0; i < byteLength; i++)
- {
- if (in_color)
- {
- if (data[i] == 'm')
- in_color = false;
-
- --charLength;
- }
- else
- {
- if (data[i] == 033)
- {
- in_color = true;
- --charLength;
- }
- else
- {
- // Extract the first two bits and check whether they are 10
- if ((data[i] & 0xC0) == 0x80)
- --charLength;
- }
- }
- }
-
- return charLength;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-unsigned int utf8_text_width (const std::string& str)
-{
- bool in_color = false;
-
- unsigned int length = 0;
- std::string::size_type i = 0;
- unsigned int c;
- while ((c = utf8_next_char (str, i)))
- {
- if (in_color)
- {
- if (c == 'm')
- in_color = false;
- }
- else if (c == 033)
- {
- in_color = true;
- }
- else
- length += mk_wcwidth (c);
- }
-
- return length;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-const std::string utf8_substr (
- const std::string& input,
- unsigned int start,
- unsigned int length /* = 0 */)
-{
- // Find the starting index.
- std::string::size_type index_start = 0;
- for (unsigned int i = 0; i < start; i++)
- utf8_next_char (input, index_start);
-
- std::string result;
- if (length)
- {
- std::string::size_type index_end = index_start;
- for (unsigned int i = 0; i < length; i++)
- utf8_next_char (input, index_end);
-
- result = input.substr (index_start, index_end - index_start);
- }
- else
- result = input.substr (index_start);
-
- return result;
-}
-
-////////////////////////////////////////////////////////////////////////////////
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2013 - 2017, Paul Beckingham, Federico Hernandez.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDED_UTF8
-#define INCLUDED_UTF8
-
-#include <string>
-
-unsigned int utf8_codepoint (const std::string&);
-unsigned int utf8_next_char (const std::string&, std::string::size_type&);
-std::string utf8_character (unsigned int);
-int utf8_sequence (unsigned int);
-unsigned int utf8_length (const std::string&);
-unsigned int utf8_text_length (const std::string&);
-unsigned int utf8_width (const std::string& str);
-unsigned int utf8_text_width (const std::string&);
-const std::string utf8_substr (const std::string&, unsigned int, unsigned int length = 0);
-
-int mk_wcwidth (wchar_t);
-
-#endif
+++ /dev/null
-/*
- * This is an implementation of wcwidth() and wcswidth() (defined in
- * IEEE Std 1002.1-2001) for Unicode.
- *
- * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
- * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
- *
- * In fixed-width output devices, Latin characters all occupy a single
- * "cell" position of equal width, whereas ideographic CJK characters
- * occupy two such cells. Interoperability between terminal-line
- * applications and (teletype-style) character terminals using the
- * UTF-8 encoding requires agreement on which character should advance
- * the cursor by how many cell positions. No established formal
- * standards exist at present on which Unicode character shall occupy
- * how many cell positions on character terminals. These routines are
- * a first attempt of defining such behavior based on simple rules
- * applied to data provided by the Unicode Consortium.
- *
- * For some graphical characters, the Unicode standard explicitly
- * defines a character-cell width via the definition of the East Asian
- * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
- * In all these cases, there is no ambiguity about which width a
- * terminal shall use. For characters in the East Asian Ambiguous (A)
- * class, the width choice depends purely on a preference of backward
- * compatibility with either historic CJK or Western practice.
- * Choosing single-width for these characters is easy to justify as
- * the appropriate long-term solution, as the CJK practice of
- * displaying these characters as double-width comes from historic
- * implementation simplicity (8-bit encoded characters were displayed
- * single-width and 16-bit ones double-width, even for Greek,
- * Cyrillic, etc.) and not any typographic considerations.
- *
- * Much less clear is the choice of width for the Not East Asian
- * (Neutral) class. Existing practice does not dictate a width for any
- * of these characters. It would nevertheless make sense
- * typographically to allocate two character cells to characters such
- * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
- * represented adequately with a single-width glyph. The following
- * routines at present merely assign a single-cell width to all
- * neutral characters, in the interest of simplicity. This is not
- * entirely satisfactory and should be reconsidered before
- * establishing a formal standard in this area. At the moment, the
- * decision which Not East Asian (Neutral) characters should be
- * represented by double-width glyphs cannot yet be answered by
- * applying a simple rule from the Unicode database content. Setting
- * up a proper standard for the behavior of UTF-8 character terminals
- * will require a careful analysis not only of each Unicode character,
- * but also of each presentation form, something the author of these
- * routines has avoided to do so far.
- *
- * http://www.unicode.org/unicode/reports/tr11/
- *
- * Markus Kuhn -- 2007-05-26 (Unicode 5.0)
- *
- * Permission to use, copy, modify, and distribute this software
- * for any purpose and without fee is hereby granted. The author
- * disclaims all warranties with regard to this software.
- *
- * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
- */
-
-#include <cmake.h>
-#include <wchar.h>
-
-struct interval {
- int first;
- int last;
-};
-
-/* auxiliary function for binary search in interval table */
-static int bisearch(wchar_t ucs, const struct interval *table, int max) {
- int min = 0;
- int mid;
-
- if (ucs < table[0].first || ucs > table[max].last)
- return 0;
- while (max >= min) {
- mid = (min + max) / 2;
- if (ucs > table[mid].last)
- min = mid + 1;
- else if (ucs < table[mid].first)
- max = mid - 1;
- else
- return 1;
- }
-
- return 0;
-}
-
-
-/* The following two functions define the column width of an ISO 10646
- * character as follows:
- *
- * - The null character (U+0000) has a column width of 0.
- *
- * - Other C0/C1 control characters and DEL will lead to a return
- * value of -1.
- *
- * - Non-spacing and enclosing combining characters (general
- * category code Mn or Me in the Unicode database) have a
- * column width of 0.
- *
- * - SOFT HYPHEN (U+00AD) has a column width of 1.
- *
- * - Other format characters (general category code Cf in the Unicode
- * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
- *
- * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
- * have a column width of 0.
- *
- * - Spacing characters in the East Asian Wide (W) or East Asian
- * Full-width (F) category as defined in Unicode Technical
- * Report #11 have a column width of 2.
- *
- * - All remaining characters (including all printable
- * ISO 8859-1 and WGL4 characters, Unicode control characters,
- * etc.) have a column width of 1.
- *
- * This implementation assumes that wchar_t characters are encoded
- * in ISO 10646.
- */
-
-int mk_wcwidth(wchar_t ucs)
-{
- /* sorted list of non-overlapping intervals of non-spacing characters */
- /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
- static const struct interval combining[] = {
- { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
- { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
- { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
- { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
- { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
- { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
- { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
- { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
- { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
- { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
- { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
- { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
- { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
- { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
- { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
- { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
- { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
- { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
- { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
- { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
- { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
- { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
- { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
- { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
- { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
- { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
- { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
- { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
- { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
- { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
- { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
- { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
- { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
- { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
- { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
- { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
- { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
- { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
- { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
- { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
- { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
- { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
- { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
- { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
- { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
- { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
- { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
- { 0xE0100, 0xE01EF }
- };
-
- /* test for 8-bit control characters */
- if (ucs == 0)
- return 0;
- if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
- return -1;
-
- /* binary search in table of non-spacing characters */
- if (bisearch(ucs, combining,
- sizeof(combining) / sizeof(struct interval) - 1))
- return 0;
-
- /* if we arrive here, ucs is not a combining or C0/C1 control character */
-
- return 1 +
- (ucs >= 0x1100 &&
- (ucs <= 0x115f || /* Hangul Jamo init. consonants */
- ucs == 0x2329 || ucs == 0x232a ||
- (ucs >= 0x2e80 && ucs <= 0xa4cf &&
- ucs != 0x303f) || /* CJK ... Yi */
- (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
- (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
- (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
- (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
- (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
- (ucs >= 0xffe0 && ucs <= 0xffe6)
-#ifndef CYGWIN
- ||
- (ucs >= 0x20000 && ucs <= 0x2fffd) ||
- (ucs >= 0x30000 && ucs <= 0x3fffd)
-#endif
- )
- );
-}
-
#include <cstdio>
#include <stdlib.h>
#include <unistd.h>
-#include <shared.h>
#ifdef HAVE_READLINE
#include <readline/readline.h>
}
else if (command != "")
{
- auto args = split (command, ' ');
-
// Dispatch command.
- if (args[0] == "<EOF>") status = -1;
- else if (closeEnough ("exit", args[0], 3)) status = -1;
- else if (closeEnough ("quit", args[0], 3)) status = -1;
+ if (command == "<EOF>") status = -1;
else if (command != "")
{
command = command;
{
try
{
- // Get the Taskwarrior rc.tasksh.autoclear Boolean setting.
bool autoClear = false;
std::string input;
std::string output;
- execute ("task", {"_get", "rc.tasksh.autoclear"}, input, output);
- output = lowerCase (output);
autoClear = (output == "true\n" ||
output == "1\n" ||
output == "y\n" ||
#include <cmake.h>
#include <vector>
#include <string>
-#include <Color.h>
static std::vector <std::string> contextColors = {
"bold white on red",
if (pretty)
combined += (combined != "" ? " " : "")
+ std::string ("\001")
- + Color::colorize ("\002 " + contexts[i] + " \001", contextColors[i % contextColors.size ()])
+ "\002";
else
combined += (combined != "" ? " " : "") + contexts[i];
////////////////////////////////////////////////////////////////////////////////
std::string promptCompose ()
{
-/* auto decoration = composeContexts (true); */
- // if (decoration.length ())
- // return "task " + decoration + "> ";
-
return ">>>";
}
+++ /dev/null
-////////////////////////////////////////////////////////////////////////////////
-//
-// Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// http://www.opensource.org/licenses/mit-license.php
-//
-////////////////////////////////////////////////////////////////////////////////
-
-#include <cmake.h>
-#include <vector>
-#include <string>
-#include <stdlib.h>
-#include <shared.h>
-
-////////////////////////////////////////////////////////////////////////////////
-int cmdShell (const std::vector <std::string>& args)
-{
- auto combined = join (" ", args);
-
- // Support '!ls' as well as '! ls'.
- if (combined[0] == '!')
- combined = combined.substr (1);
-
- system (combined.c_str ());
- return 0; // Ignore system return code.
-}
-
-////////////////////////////////////////////////////////////////////////////////