mirror of
https://github.com/GothenburgBitFactory/taskshell.git
synced 2025-06-26 10:54:29 +02:00
libshared: Integrated
This commit is contained in:
parent
7d78b9440b
commit
22c42f9de3
27 changed files with 117 additions and 3430 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -12,4 +12,6 @@ _CPack_Packages
|
|||
CPackConfig.cmake
|
||||
CPackSourceConfig.cmake
|
||||
patches
|
||||
liblibshared.a
|
||||
libtasksh.a
|
||||
*.exe
|
||||
|
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[submodule "src/libshared"]
|
||||
path = src/libshared
|
||||
url = https://git.tasktools.org/scm/tm/libshared.git
|
|
@ -1,85 +1,17 @@
|
|||
cmake_minimum_required (VERSION 2.8)
|
||||
set(CMAKE_LEGACY_CYGWIN_WIN32 0) # Remove when CMake >= 2.8.4 is required
|
||||
|
||||
include (CheckFunctionExists)
|
||||
include (CheckStructHasMember)
|
||||
|
||||
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 (tasksh)
|
||||
include (CXXSniffer)
|
||||
|
||||
set (PROJECT_VERSION "1.1.0")
|
||||
|
||||
message ("CMAKE_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}")
|
||||
|
||||
include (CheckFunctionExists)
|
||||
include (CheckStructHasMember)
|
||||
include (CheckCXXCompilerFlag)
|
||||
|
||||
# NOTE: If we are to actually use C++11 features, we should either require
|
||||
# a compiler that supports the -std=c++11 flag or check for the
|
||||
# features used.
|
||||
# Relying on -std=c++0x or even -std=gnu++0x is highly volatile.
|
||||
|
||||
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. If you have a good reason for using an outdated compiler, please let us know at support@taskwarrior.org.")
|
||||
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}")
|
||||
|
||||
if (NETBSD)
|
||||
# Since readline, etc likely to be in /usr/pkg/lib, not standard library
|
||||
# Otherwise will remove links during install
|
||||
set (CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
endif (NETBSD)
|
||||
|
||||
if (FREEBSD)
|
||||
SET (TASKSH_MAN1DIR man/man1 CACHE STRING "Installation directory for man pages, section 1")
|
||||
else (FREEBSD)
|
||||
SET (TASKSH_MAN1DIR share/man/man1 CACHE STRING "Installation directory for man pages, section 1")
|
||||
endif (FREEBSD)
|
||||
SET (TASKSH_DOCDIR share/doc/tasksh CACHE STRING "Installation directory for doc files")
|
||||
SET (TASKSH_RCDIR "${TASKSH_DOCDIR}/rc" CACHE STRING "Installation directory for configuration files")
|
||||
SET (TASKSH_BINDIR bin CACHE STRING "Installation directory for the binary")
|
||||
|
||||
message ("-- Looking for SHA1 references")
|
||||
if (EXISTS ${CMAKE_SOURCE_DIR}/.git/index)
|
||||
set (HAVE_COMMIT true)
|
||||
|
@ -99,6 +31,15 @@ set (PACKAGE_TARNAME "${PACKAGE}")
|
|||
set (PACKAGE_VERSION "${VERSION}")
|
||||
set (PACKAGE_STRING "${PACKAGE} ${VERSION}")
|
||||
|
||||
if (FREEBSD)
|
||||
SET (TASKSH_MAN1DIR man/man1 CACHE STRING "Installation directory for man pages, section 1")
|
||||
else (FREEBSD)
|
||||
SET (TASKSH_MAN1DIR share/man/man1 CACHE STRING "Installation directory for man pages, section 1")
|
||||
endif (FREEBSD)
|
||||
SET (TASKSH_DOCDIR share/doc/tasksh CACHE STRING "Installation directory for doc files")
|
||||
SET (TASKSH_RCDIR "${TASKSH_DOCDIR}/rc" CACHE STRING "Installation directory for configuration files")
|
||||
SET (TASKSH_BINDIR bin CACHE STRING "Installation directory for the binary")
|
||||
|
||||
# include the readline library finder module
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules")
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
- Implemented 'diag' command.
|
||||
- Added 'review N' option, to specify the number of tasks you would like to
|
||||
review.
|
||||
- Integrated libshared.git.
|
||||
|
||||
------ current release ---------------------------
|
||||
|
||||
|
|
51
cmake/CXXSniffer.cmake
Normal file
51
cmake/CXXSniffer.cmake
Normal file
|
@ -0,0 +1,51 @@
|
|||
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. If you have a good reason for using an outdated compiler, please let us know at support@taskwarrior.org.")
|
||||
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}")
|
|
@ -1,22 +1,32 @@
|
|||
cmake_minimum_required (VERSION 2.8)
|
||||
include_directories (${CMAKE_SOURCE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/src/libshared/src
|
||||
${TASKSH_INCLUDE_DIRS})
|
||||
|
||||
set (tasksh_SRCS diag.cpp
|
||||
help.cpp
|
||||
prompt.cpp
|
||||
review.cpp
|
||||
shell.cpp
|
||||
Color.cpp Color.h
|
||||
FS.cpp FS.h
|
||||
text.cpp text.h
|
||||
utf8.cpp utf8.h
|
||||
util.cpp util.h
|
||||
wcwidth6.cpp)
|
||||
shell.cpp)
|
||||
|
||||
add_executable (tasksh_executable main.cpp ${tasksh_SRCS})
|
||||
target_link_libraries (tasksh_executable ${TASKSH_LIBRARIES})
|
||||
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_executable (tasksh_executable main.cpp)
|
||||
|
||||
target_link_libraries (tasksh_executable tasksh libshared ${TASKSH_LIBRARIES})
|
||||
|
||||
set_property (TARGET tasksh_executable PROPERTY OUTPUT_NAME "tasksh")
|
||||
|
||||
|
|
610
src/Color.cpp
610
src/Color.cpp
|
@ -1,610 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2006 - 2016, 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 <iostream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <text.h>
|
||||
#include <i18n.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static struct
|
||||
{
|
||||
Color::color_id id;
|
||||
int string_id;
|
||||
std::string english_name;
|
||||
int index; // offset red=3 (therefore fg=33, bg=43)
|
||||
} allColors[] =
|
||||
{
|
||||
// Color.h enum i18n.h English Index
|
||||
{ Color::nocolor, 0, "none", 0},
|
||||
{ Color::black, CCOLOR_BLACK, "black", 1}, // fg 29+0 bg 39+0
|
||||
{ Color::red, CCOLOR_RED, "red", 2},
|
||||
{ Color::green, CCOLOR_GREEN, "green", 3},
|
||||
{ Color::yellow, CCOLOR_YELLOW, "yellow", 4},
|
||||
{ Color::blue, CCOLOR_BLUE, "blue", 5},
|
||||
{ Color::magenta, CCOLOR_MAGENTA, "magenta", 6},
|
||||
{ Color::cyan, CCOLOR_CYAN, "cyan", 7},
|
||||
{ Color::white, CCOLOR_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)
|
||||
{
|
||||
// By converting underscores to spaces, we inherently support the old "on_red"
|
||||
// style of specifying background colors. We consider underscores to be
|
||||
// deprecated.
|
||||
std::string modifiable_spec = spec;
|
||||
std::replace (modifiable_spec.begin (), modifiable_spec.end (), '_', ' ');
|
||||
|
||||
// Split spec into words.
|
||||
std::vector <std::string> words;
|
||||
split (words, modifiable_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;
|
||||
std::string word;
|
||||
std::vector <std::string>::iterator it;
|
||||
for (it = words.begin (); it != words.end (); ++it)
|
||||
{
|
||||
word = lowerCase (trim (*it));
|
||||
|
||||
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.substr (0, 4) == "grey" ||
|
||||
word.substr (0, 4) == "gray")
|
||||
{
|
||||
index = atoi (word.substr (4).c_str ());
|
||||
if (index < 0 || index > 23)
|
||||
throw format (STRING_COLOR_UNRECOGNIZED, *it);
|
||||
|
||||
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.substr (0, 3) == "rgb")
|
||||
{
|
||||
index = atoi (word.substr (3).c_str ());
|
||||
if (word.length () != 6 ||
|
||||
index < 0 || index > 555)
|
||||
throw format (STRING_COLOR_UNRECOGNIZED, *it);
|
||||
|
||||
int r = atoi (word.substr (3, 1).c_str ());
|
||||
int g = atoi (word.substr (4, 1).c_str ());
|
||||
int b = atoi (word.substr (5, 1).c_str ());
|
||||
if (r < 0 || r > 5 ||
|
||||
g < 0 || g > 5 ||
|
||||
b < 0 || b > 5)
|
||||
throw format (STRING_COLOR_UNRECOGNIZED, *it);
|
||||
|
||||
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.substr (0, 5) == "color")
|
||||
{
|
||||
index = atoi (word.substr (5).c_str ());
|
||||
if (index < 0 || index > 255)
|
||||
throw format (STRING_COLOR_UNRECOGNIZED, *it);
|
||||
|
||||
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 (STRING_COLOR_UNRECOGNIZED, *it);
|
||||
}
|
||||
|
||||
// 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)
|
||||
: _value (0)
|
||||
{
|
||||
if (bg != Color::nocolor)
|
||||
{
|
||||
_value |= COLOR_HASBG;
|
||||
_value |= (bg << 8);
|
||||
}
|
||||
|
||||
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::~Color ()
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
Color& Color::operator= (const Color& other)
|
||||
{
|
||||
if (this != &other)
|
||||
_value = other._value;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
std::string Color::colorize (const std::string& input)
|
||||
{
|
||||
if (!nontrivial ())
|
||||
return input;
|
||||
|
||||
int count = 0;
|
||||
std::stringstream result;
|
||||
|
||||
// 256 color
|
||||
if (_value & COLOR_256)
|
||||
{
|
||||
bool needTerminator = false;
|
||||
|
||||
if (_value & COLOR_UNDERLINE)
|
||||
{
|
||||
result << "\033[4m";
|
||||
needTerminator = true;
|
||||
}
|
||||
|
||||
if (_value & COLOR_INVERSE)
|
||||
{
|
||||
result << "\033[7m";
|
||||
needTerminator = true;
|
||||
}
|
||||
|
||||
if (_value & COLOR_HASFG)
|
||||
{
|
||||
result << "\033[38;5;" << (_value & COLOR_FG) << "m";
|
||||
needTerminator = true;
|
||||
}
|
||||
|
||||
if (_value & COLOR_HASBG)
|
||||
{
|
||||
result << "\033[48;5;" << ((_value & COLOR_BG) >> 8) << "m";
|
||||
needTerminator = true;
|
||||
}
|
||||
|
||||
result << input;
|
||||
if (needTerminator)
|
||||
result << "\033[0m";
|
||||
|
||||
return result.str ();
|
||||
}
|
||||
|
||||
// 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 << (29 + (_value & COLOR_FG));
|
||||
}
|
||||
|
||||
if (_value & COLOR_HASBG)
|
||||
{
|
||||
if (count++) result << ";";
|
||||
result << ((_value & COLOR_BRIGHT ? 99 : 39) + ((_value & COLOR_BG) >> 8));
|
||||
}
|
||||
|
||||
result << "m" << input << "\033[0m";
|
||||
return result.str ();
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
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 "";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
80
src/Color.h
80
src/Color.h
|
@ -1,80 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2006 - 2016, 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); // fg, bg.
|
||||
Color (color_id, color_id, bool, bool, bool); // fg, bg, underline, bold, bright
|
||||
~Color ();
|
||||
Color& operator= (const Color&);
|
||||
operator std::string () const;
|
||||
operator int () const;
|
||||
|
||||
void upgrade ();
|
||||
void blend (const Color&);
|
||||
|
||||
std::string colorize (const std::string&);
|
||||
static std::string colorize (const std::string&, const std::string&);
|
||||
static std::string strip (const std::string&);
|
||||
|
||||
bool nontrivial () const;
|
||||
|
||||
private:
|
||||
int find (const std::string&);
|
||||
std::string fg () const;
|
||||
std::string bg () const;
|
||||
|
||||
private:
|
||||
unsigned int _value;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
893
src/FS.cpp
893
src/FS.cpp
|
@ -1,893 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2006 - 2016, 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 <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <text.h>
|
||||
#include <i18n.h>
|
||||
|
||||
#if defined SOLARIS || defined NETBSD || defined FREEBSD
|
||||
#include <limits.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
|
||||
{
|
||||
struct stat s {};
|
||||
if (! stat (_data.c_str (), &s) &&
|
||||
S_ISDIR (s.st_mode))
|
||||
return true;
|
||||
|
||||
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) &&
|
||||
S_ISLNK (s.st_mode))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool Path::readable () const
|
||||
{
|
||||
return access (_data.c_str (), R_OK) ? false : true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool Path::writable () const
|
||||
{
|
||||
return access (_data.c_str (), W_OK) ? false : true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool Path::executable () const
|
||||
{
|
||||
return access (_data.c_str (), X_OK) ? false : true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool Path::rename (const std::string& new_name)
|
||||
{
|
||||
std::string expanded = expand (new_name);
|
||||
if (_data != expanded)
|
||||
{
|
||||
if (::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 (2);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool File::open ()
|
||||
{
|
||||
if (_data != "")
|
||||
{
|
||||
if (! _fh)
|
||||
{
|
||||
bool already_exists = exists ();
|
||||
if (already_exists)
|
||||
if (!readable () || !writable ())
|
||||
throw std::string (format (STRING_FS_PERMS, _data));
|
||||
|
||||
_fh = fopen (_data.c_str (), (already_exists ? "r+" : "w+"));
|
||||
if (_fh)
|
||||
{
|
||||
_h = fileno (_fh);
|
||||
_locked = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void File::close ()
|
||||
{
|
||||
if (_fh)
|
||||
{
|
||||
if (_locked)
|
||||
unlock ();
|
||||
|
||||
fclose (_fh);
|
||||
_fh = nullptr;
|
||||
_h = -1;
|
||||
_locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool File::lock ()
|
||||
{
|
||||
_locked = false;
|
||||
if (_fh && _h != -1)
|
||||
{
|
||||
// l_type l_whence l_start l_len l_pid
|
||||
struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0 };
|
||||
fl.l_pid = getpid ();
|
||||
if (fcntl (_h, F_SETLKW, &fl) == 0)
|
||||
_locked = true;
|
||||
}
|
||||
|
||||
return _locked;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void File::unlock ()
|
||||
{
|
||||
if (_locked)
|
||||
{
|
||||
// l_type l_whence l_start l_len l_pid
|
||||
struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0 };
|
||||
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 ())
|
||||
{
|
||||
std::string line;
|
||||
line.reserve (512 * 1024);
|
||||
while (getline (in, line))
|
||||
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 ())
|
||||
{
|
||||
std::string line;
|
||||
line.reserve (512 * 1024);
|
||||
while (getline (in, line))
|
||||
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);
|
||||
fputs (line.c_str (), _fh);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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)
|
||||
fputs (line.c_str (), _fh);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void File::write_raw (const std::string& line)
|
||||
{
|
||||
if (!_fh)
|
||||
open ();
|
||||
|
||||
if (_fh)
|
||||
{
|
||||
fputs (line.c_str (), _fh);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void File::truncate ()
|
||||
{
|
||||
if (!_fh)
|
||||
open ();
|
||||
|
||||
if (_fh)
|
||||
(void) ftruncate (_h, 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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))
|
||||
return s.st_mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
size_t File::size () const
|
||||
{
|
||||
struct stat s;
|
||||
if (!stat (_data.c_str (), &s))
|
||||
return s.st_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
time_t File::mtime () const
|
||||
{
|
||||
struct stat s;
|
||||
if (!stat (_data.c_str (), &s))
|
||||
return s.st_mtime;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
time_t File::ctime () const
|
||||
{
|
||||
struct stat s;
|
||||
if (!stat (_data.c_str (), &s))
|
||||
return s.st_ctime;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
time_t File::btime () const
|
||||
{
|
||||
struct stat s;
|
||||
if (!stat (_data.c_str (), &s))
|
||||
#ifdef HAVE_ST_BIRTHTIME
|
||||
return s.st_birthtime;
|
||||
#else
|
||||
return s.st_ctime;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
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 ();
|
||||
chmod (full_name.c_str (), mode);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool File::read (const std::string& name, std::string& contents)
|
||||
{
|
||||
contents = "";
|
||||
|
||||
std::ifstream in (name.c_str ());
|
||||
if (in.good ())
|
||||
{
|
||||
std::string line;
|
||||
line.reserve (1024);
|
||||
while (getline (in, line))
|
||||
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 ())
|
||||
{
|
||||
std::string line;
|
||||
line.reserve (1024);
|
||||
while (getline (in, line))
|
||||
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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
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 */)
|
||||
{
|
||||
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;
|
||||
lstat ((dir + "/" + de->d_name).c_str (), &s);
|
||||
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;
|
||||
lstat ((dir + "/" + de->d_name).c_str (), &s);
|
||||
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;
|
||||
stat ((base + "/" + de->d_name).c_str (), &s);
|
||||
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;
|
||||
lstat ((base + "/" + de->d_name).c_str (), &s);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
144
src/FS.h
144
src/FS.h
|
@ -1,144 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2006 - 2016, 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>
|
||||
|
||||
class Path
|
||||
{
|
||||
public:
|
||||
Path ();
|
||||
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 ();
|
||||
File (const Path&);
|
||||
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&);
|
||||
|
||||
private:
|
||||
FILE* _fh;
|
||||
int _h;
|
||||
bool _locked;
|
||||
};
|
||||
|
||||
class Directory : public File
|
||||
{
|
||||
public:
|
||||
Directory ();
|
||||
Directory (const Directory&);
|
||||
Directory (const File&);
|
||||
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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -34,7 +34,8 @@
|
|||
#include <FS.h>
|
||||
#include <Color.h>
|
||||
#include <i18n.h>
|
||||
#include <text.h>
|
||||
#include <shared.h>
|
||||
#include <format.h>
|
||||
#include <util.h>
|
||||
|
||||
#ifdef HAVE_READLINE
|
||||
|
@ -177,8 +178,7 @@ TRING_CMD_DIAG_COMPILER
|
|||
std::string path (getenv ("PATH"));
|
||||
std::cout << " PATH: " << path << "\n";
|
||||
|
||||
std::vector <std::string> paths;
|
||||
split (paths, path, ':');
|
||||
std::vector <std::string> paths = split (path, ':');
|
||||
|
||||
std::vector <std::string>::iterator i;
|
||||
for (i = paths.begin (); i != paths.end (); ++i)
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <i18n.h>
|
||||
#include <text.h>
|
||||
#include <shared.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int cmdHelp ()
|
||||
|
|
1
src/libshared
Submodule
1
src/libshared
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 83f8ac2a0de4caba98472925ae710c5124ad61c3
|
|
@ -31,7 +31,7 @@
|
|||
#include <cstring>
|
||||
#include <stdlib.h>
|
||||
#include <i18n.h>
|
||||
#include <text.h>
|
||||
#include <shared.h>
|
||||
|
||||
#ifdef HAVE_READLINE
|
||||
#include <readline/readline.h>
|
||||
|
@ -105,8 +105,7 @@ static int commandLoop ()
|
|||
int status = 0;
|
||||
if (command != "")
|
||||
{
|
||||
std::vector <std::string> args;
|
||||
split (args, command, ' ');
|
||||
std::vector <std::string> args = split (command, ' ');
|
||||
|
||||
// Dispatch command.
|
||||
if (closeEnough ("exit", args[0], 3)) status = -1;
|
||||
|
|
|
@ -44,7 +44,9 @@
|
|||
#endif
|
||||
|
||||
#include <Color.h>
|
||||
#include <text.h>
|
||||
#include <Lexer.h>
|
||||
#include <shared.h>
|
||||
#include <format.h>
|
||||
#include <util.h>
|
||||
#include <i18n.h>
|
||||
|
||||
|
@ -124,7 +126,7 @@ static const std::string reviewStart (
|
|||
|
||||
std::vector <std::string> lines;
|
||||
wrapText (lines, welcome, width, false);
|
||||
join (welcome, "\n", lines);
|
||||
welcome = join ("\n", lines);
|
||||
|
||||
return "\n" + welcome + "\n\n";
|
||||
}
|
||||
|
@ -195,7 +197,7 @@ static void reviewLoop (const std::vector <std::string>& uuids, int limit)
|
|||
dummy,
|
||||
description);
|
||||
|
||||
std::cout << banner (current + 1, total, width, trimRight (description, "\n"));
|
||||
std::cout << banner (current + 1, total, width, Lexer::trimRight (description, "\n"));
|
||||
|
||||
// Use 'system' to run the command and show the output.
|
||||
std::string command = "task " + uuid + " information";
|
||||
|
@ -268,8 +270,7 @@ int cmdReview (const std::vector <std::string>& args)
|
|||
input, output);
|
||||
|
||||
// Review the set of UUIDs.
|
||||
std::vector <std::string> uuids;
|
||||
split (uuids, trimRight (output, "\n"), '\n');
|
||||
std::vector <std::string> uuids = split (Lexer::trimRight (output, "\n"), '\n');
|
||||
reviewLoop (uuids, limit);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -28,13 +28,12 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
#include <stdlib.h>
|
||||
#include <text.h>
|
||||
#include <shared.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int cmdShell (const std::vector <std::string>& args)
|
||||
{
|
||||
std::string combined;
|
||||
join (combined, " ", args);
|
||||
auto combined = join (" ", args);
|
||||
|
||||
// Support '!ls' as well as '! ls'.
|
||||
if (combined[0] == '!')
|
||||
|
|
303
src/text.cpp
303
src/text.cpp
|
@ -1,303 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2006 - 2016, 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 <text.h>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <strings.h>
|
||||
#include <utf8.h>
|
||||
|
||||
static void replace_positional (std::string&, const std::string&, const std::string&);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void split (
|
||||
std::vector<std::string>& results,
|
||||
const std::string& input,
|
||||
const char delimiter)
|
||||
{
|
||||
results.clear ();
|
||||
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));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void join (
|
||||
std::string& result,
|
||||
const std::string& separator,
|
||||
const std::vector<std::string>& items)
|
||||
{
|
||||
std::stringstream s;
|
||||
unsigned int size = items.size ();
|
||||
for (unsigned int i = 0; i < size; ++i)
|
||||
{
|
||||
s << items[i];
|
||||
if (i < size - 1)
|
||||
s << separator;
|
||||
}
|
||||
|
||||
result = s.str ();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
std::string trimLeft (const std::string& in, const std::string& t /*= " "*/)
|
||||
{
|
||||
std::string out = in;
|
||||
return out.erase (0, in.find_first_not_of (t));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
std::string trimRight (const std::string& in, const std::string& t /*= " "*/)
|
||||
{
|
||||
std::string out = in;
|
||||
return out.erase (out.find_last_not_of (t) + 1);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
std::string trim (const std::string& in, const std::string& t /*= " "*/)
|
||||
{
|
||||
std::string out = in;
|
||||
return trimLeft (trimRight (out, t), t);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
std::string lowerCase (const std::string& input)
|
||||
{
|
||||
std::string output = input;
|
||||
std::transform (output.begin (), output.end (), output.begin (), tolower);
|
||||
return output;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
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 */)
|
||||
{
|
||||
if (compare (reference, attempt, false))
|
||||
return true;
|
||||
|
||||
if (attempt.length () < reference.length () &&
|
||||
attempt.length () >= minLength)
|
||||
return compare (reference.substr (0, attempt.length ()), attempt, false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
const std::string format (int value)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << value;
|
||||
return s.str ();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static 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 ();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
const std::string format (
|
||||
const std::string& fmt,
|
||||
const std::string& arg1)
|
||||
{
|
||||
std::string output = fmt;
|
||||
replace_positional (output, "{1}", arg1);
|
||||
return output;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
const std::string format (
|
||||
const std::string& fmt,
|
||||
int arg1,
|
||||
int arg2)
|
||||
{
|
||||
std::string output = fmt;
|
||||
replace_positional (output, "{1}", format (arg1));
|
||||
replace_positional (output, "{2}", format (arg2));
|
||||
return output;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
297
src/utf8.cpp
297
src/utf8.cpp
|
@ -1,297 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2013 - 2016, 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>
|
||||
#include <string>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
45
src/utf8.h
45
src/utf8.h
|
@ -1,45 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2013 - 2016, 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
193
src/util.cpp
193
src/util.cpp
|
@ -1,193 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2006 - 2016, 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>
|
||||
// If <iostream> is included, put it after <stdio.h>, because it includes
|
||||
// <stdio.h>, and therefore would ignore the _WITH_GETLINE.
|
||||
#ifdef FREEBSD
|
||||
#define _WITH_GETLINE
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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] = nullptr;
|
||||
_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, nullptr, &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 exit status!");
|
||||
}
|
||||
|
||||
if (signal (SIGPIPE, SIG_DFL) == SIG_ERR) // We're done, return to default.
|
||||
throw std::string (std::strerror (errno));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
34
src/util.h
34
src/util.h
|
@ -1,34 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2006 - 2016, 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_UTIL
|
||||
#define INCLUDED_UTIL
|
||||
|
||||
// util.cpp
|
||||
int execute (const std::string&, const std::vector <std::string>&, const std::string&, std::string&);
|
||||
|
||||
#endif
|
||||
////////////////////////////////////////////////////////////////////////////////
|
211
src/wcwidth6.cpp
211
src/wcwidth6.cpp
|
@ -1,211 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
)
|
||||
);
|
||||
}
|
||||
|
2
test/.gitignore
vendored
2
test/.gitignore
vendored
|
@ -1,3 +1 @@
|
|||
all.log
|
||||
color.t
|
||||
fs.t
|
||||
|
|
|
@ -1,47 +1,29 @@
|
|||
cmake_minimum_required (VERSION 2.8)
|
||||
|
||||
# See this CMake issue before complaining about the following.
|
||||
# https://cmake.org/Bug/view.php?id=16062
|
||||
if(POLICY CMP0037)
|
||||
cmake_policy(SET CMP0037 OLD)
|
||||
endif()
|
||||
|
||||
include_directories (${CMAKE_SOURCE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/src/libshared/src
|
||||
${CMAKE_SOURCE_DIR}/test
|
||||
${TASKSH_INCLUDE_DIRS})
|
||||
|
||||
set (test_SRCS color.t fs.t)
|
||||
include_directories (${CMAKE_INSTALL_PREFIX}/include)
|
||||
link_directories(${CMAKE_INSTALL_PREFIX}/lib)
|
||||
|
||||
message ("-- Configuring run_all")
|
||||
if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
|
||||
|
||||
set (TESTBLOB "./*.t")
|
||||
if (CYGWIN)
|
||||
set (TESTBLOB "./*.t ./*.t.exe")
|
||||
endif (CYGWIN)
|
||||
|
||||
else (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
|
||||
|
||||
set (TESTBLOB "${CMAKE_SOURCE_DIR}/test/*.t ${CMAKE_BINARY_DIR}/test/*.t")
|
||||
if (CYGWIN)
|
||||
set (TESTBLOB "${CMAKE_SOURCE_DIR}/test/*.t ${CMAKE_BINARY_DIR}/test/*.t.exe")
|
||||
endif (CYGWIN)
|
||||
|
||||
endif (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
|
||||
set (test_SRCS)
|
||||
|
||||
add_custom_target (test ./run_all --verbose
|
||||
DEPENDS ${test_SRCS} tasksh_executable
|
||||
DEPENDS ${test_SRCS}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test)
|
||||
|
||||
add_custom_target (build_tests DEPENDS ${test_SRCS}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/test)
|
||||
|
||||
foreach (src_FILE ${test_SRCS})
|
||||
add_executable (${src_FILE} "${src_FILE}.cpp"
|
||||
../src/Color.cpp
|
||||
../src/FS.cpp
|
||||
../src/text.cpp
|
||||
../src/utf8.cpp
|
||||
../src/wcwidth6.cpp
|
||||
test.cpp)
|
||||
target_link_libraries (${src_FILE} ${TASKSH_LIBRARIES})
|
||||
add_executable (${src_FILE} "${src_FILE}.cpp" test.cpp)
|
||||
target_link_libraries (${src_FILE} tasksh libshared ${TASKSH_LIBRARIES})
|
||||
endforeach (src_FILE)
|
||||
|
||||
configure_file(run_all run_all COPYONLY)
|
||||
|
|
197
test/color.t.cpp
197
test/color.t.cpp
|
@ -1,197 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2006 - 2016, 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 <iostream>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <Color.h>
|
||||
#include <test.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int main (int, char**)
|
||||
{
|
||||
UnitTest t (1036);
|
||||
|
||||
// Ensure environment has no influence.
|
||||
unsetenv ("TASKDATA");
|
||||
unsetenv ("TASKRC");
|
||||
|
||||
// Names matched to values.
|
||||
t.is ((int) Color (""), (int) Color (Color::nocolor), "'' == Color::nocolor");
|
||||
t.is ((int) Color ("black"), (int) Color (Color::black), "'black' == Color::black");
|
||||
t.is ((int) Color ("red"), (int) Color (Color::red), "'red' == Color::red");
|
||||
t.is ((int) Color ("green"), (int) Color (Color::green), "'green' == Color::green");
|
||||
t.is ((int) Color ("yellow"), (int) Color (Color::yellow), "'yellow' == Color::yellow");
|
||||
t.is ((int) Color ("blue"), (int) Color (Color::blue), "'blue' == Color::blue");
|
||||
t.is ((int) Color ("magenta"), (int) Color (Color::magenta), "'magenta' == Color::magenta");
|
||||
t.is ((int) Color ("cyan"), (int) Color (Color::cyan), "'cyan' == Color::cyan");
|
||||
t.is ((int) Color ("white"), (int) Color (Color::white), "'white' == Color::white");
|
||||
|
||||
// Auto upgrades.
|
||||
Color c ("red on color0");
|
||||
t.is ((std::string) c, "color1 on color0", "upgrade red on color0 -> color1 on color0");
|
||||
|
||||
c = Color ("color1 on black");
|
||||
t.is ((std::string) c, "color1 on color0", "upgrade color1 on black -> color1 on color0");
|
||||
|
||||
c = Color ("bold red on color0");
|
||||
t.is ((std::string) c, "color9 on color0", "upgrade bold red on color0 -> color9 on color0");
|
||||
|
||||
c = Color ("color1 on bright black");
|
||||
t.is ((std::string) c, "color1 on color8", "upgrade color1 on bright black -> color1 on color8");
|
||||
|
||||
// Simple blending.
|
||||
c = Color ("red");
|
||||
c.blend (Color ("on white"));
|
||||
t.is ((std::string) c, "red on white", "red + on white -> red on white");
|
||||
|
||||
c = Color ("bold underline red");
|
||||
c.blend (Color ("on bright white"));
|
||||
t.is ((std::string) c, "bold underline red on bright white", "bold underline red + on bright white -> bold underline red on bright white");
|
||||
|
||||
// Blending with conflicts.
|
||||
c = Color ("red on white");
|
||||
c.blend (Color ("on blue"));
|
||||
t.is ((std::string) c, "red on blue", "red on white + on blue -> red on blue");
|
||||
|
||||
c = Color ("red on white");
|
||||
c.blend (Color ("blue on magenta"));
|
||||
t.is ((std::string) c, "blue on magenta", "red on white + blue on magenta -> blue on magenta");
|
||||
|
||||
// Blending with upgrades.
|
||||
c = Color ("color1 on color0");
|
||||
c.blend (Color ("blue"));
|
||||
t.is ((std::string) c, "color4 on color0", "color1 on color0 + blue -> color4 on color0");
|
||||
|
||||
// Now the dumb show of every color and its code.
|
||||
t.is (Color::colorize ("foo", "red"), std::string ("\033[31mfoo\033[0m"), "red -> ^[[31m");
|
||||
t.is (Color::colorize ("foo", "bold red"), std::string ("\033[1;31mfoo\033[0m"), "bold red -> ^[[1;31m");
|
||||
t.is (Color::colorize ("foo", "underline red"), std::string ("\033[4;31mfoo\033[0m"), "underline red -> ^[[4;31m");
|
||||
t.is (Color::colorize ("foo", "underline bold red"), std::string ("\033[1;4;31mfoo\033[0m"), "underline bold red -> ^[[1;4;31m");
|
||||
|
||||
// 16-color foregrounds.
|
||||
t.is (Color::colorize ("foo", ""), std::string ("foo"), "'' -> ''");
|
||||
|
||||
t.is (Color::colorize ("foo", "black"), std::string ("\033[30mfoo\033[0m"), "black -> ^[[30m");
|
||||
t.is (Color::colorize ("foo", "red"), std::string ("\033[31mfoo\033[0m"), "red -> ^[[31m");
|
||||
t.is (Color::colorize ("foo", "green"), std::string ("\033[32mfoo\033[0m"), "green -> ^[[32m");
|
||||
t.is (Color::colorize ("foo", "yellow"), std::string ("\033[33mfoo\033[0m"), "yellow -> ^[[33m");
|
||||
t.is (Color::colorize ("foo", "blue"), std::string ("\033[34mfoo\033[0m"), "blue -> ^[[34m");
|
||||
t.is (Color::colorize ("foo", "magenta"), std::string ("\033[35mfoo\033[0m"), "magenta -> ^[[35m");
|
||||
t.is (Color::colorize ("foo", "cyan"), std::string ("\033[36mfoo\033[0m"), "cyan -> ^[[36m");
|
||||
t.is (Color::colorize ("foo", "white"), std::string ("\033[37mfoo\033[0m"), "white -> ^[[37m");
|
||||
|
||||
// 16-color backgrounds.
|
||||
t.is (Color::colorize ("foo", "on bright black"), std::string ("\033[100mfoo\033[0m"), "on bright black -> ^[[100m");
|
||||
|
||||
t.is (Color::colorize ("foo", "on black"), std::string ("\033[40mfoo\033[0m"), "on black -> ^[[40m");
|
||||
t.is (Color::colorize ("foo", "on red"), std::string ("\033[41mfoo\033[0m"), "on red -> ^[[41m");
|
||||
t.is (Color::colorize ("foo", "on green"), std::string ("\033[42mfoo\033[0m"), "on green -> ^[[42m");
|
||||
t.is (Color::colorize ("foo", "on yellow"), std::string ("\033[43mfoo\033[0m"), "on yellow -> ^[[43m");
|
||||
t.is (Color::colorize ("foo", "on blue"), std::string ("\033[44mfoo\033[0m"), "on blue -> ^[[44m");
|
||||
t.is (Color::colorize ("foo", "on magenta"), std::string ("\033[45mfoo\033[0m"), "on magenta -> ^[[45m");
|
||||
t.is (Color::colorize ("foo", "on cyan"), std::string ("\033[46mfoo\033[0m"), "on cyan -> ^[[46m");
|
||||
t.is (Color::colorize ("foo", "on white"), std::string ("\033[47mfoo\033[0m"), "on white -> ^[[47m");
|
||||
|
||||
// 256-color, basic colors.
|
||||
char color [24];
|
||||
char codes [64];
|
||||
char description [64];
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
sprintf (color, "color%d", i);
|
||||
sprintf (codes, "\033[38;5;%dmfoo\033[0m", i);
|
||||
sprintf (description, "color%d -> ^[[38;5;%dm", i, i);
|
||||
|
||||
t.is (Color::colorize ("foo", color), std::string (codes), description);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
sprintf (color, "on color%d", i);
|
||||
sprintf (codes, "\033[48;5;%dmfoo\033[0m", i);
|
||||
sprintf (description, "on color%d -> ^[[48;5;%dm", i, i);
|
||||
|
||||
t.is (Color::colorize ("foo", color), std::string (codes), description);
|
||||
}
|
||||
|
||||
// RGB Color Cube.
|
||||
for (int r = 0; r < 6; ++r)
|
||||
for (int g = 0; g < 6; ++g)
|
||||
for (int b = 0; b < 6; ++b)
|
||||
{
|
||||
int code = 16 + (r*36 + g*6 + b);
|
||||
sprintf (color, "rgb%d%d%d", r, g, b);
|
||||
sprintf (codes, "\033[38;5;%dmfoo\033[0m", code);
|
||||
sprintf (description, "rgb%d%d%d -> ^[[38;5;%dm", r, g, b, code);
|
||||
|
||||
t.is (Color::colorize ("foo", color), std::string (codes), description);
|
||||
}
|
||||
|
||||
for (int r = 0; r < 6; ++r)
|
||||
for (int g = 0; g < 6; ++g)
|
||||
for (int b = 0; b < 6; ++b)
|
||||
{
|
||||
int code = 16 + (r*36 + g*6 + b);
|
||||
sprintf (color, "on rgb%d%d%d", r, g, b);
|
||||
sprintf (codes, "\033[48;5;%dmfoo\033[0m", code);
|
||||
sprintf (description, "on rgb%d%d%d -> ^[[48;5;%dm", r, g, b, code);
|
||||
|
||||
t.is (Color::colorize ("foo", color), std::string (codes), description);
|
||||
}
|
||||
|
||||
// 256-color, grays.
|
||||
// grey == gray.
|
||||
t.is (Color::colorize ("foo", "grey0"), std::string ("\033[38;5;232mfoo\033[0m"), "grey0 -> ^[[38;5;232m");
|
||||
|
||||
for (int i = 0; i < 24; ++i)
|
||||
{
|
||||
sprintf (color, "gray%d", i);
|
||||
sprintf (codes, "\033[38;5;%dmfoo\033[0m", i + 232);
|
||||
sprintf (description, "gray%d -> ^[[38;5;%dm", i + 232, i + 232);
|
||||
|
||||
t.is (Color::colorize ("foo", color), std::string (codes), description);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 24; ++i)
|
||||
{
|
||||
sprintf (color, "on gray%d", i);
|
||||
sprintf (codes, "\033[48;5;%dmfoo\033[0m", i + 232);
|
||||
sprintf (description, "on gray%d -> ^[[48;5;%dm", i + 232, i + 232);
|
||||
|
||||
t.is (Color::colorize ("foo", color), std::string (codes), description);
|
||||
}
|
||||
|
||||
// std::string Color::strip (const std::string&);
|
||||
t.is (Color::strip (""), "", "Color::strip '' -> ''");
|
||||
t.is (Color::strip ("foo"), "foo", "Color::strip 'foo' -> 'foo'");
|
||||
t.is (Color::strip ("f\033[1mo\033[0mo"), "foo", "Color::strip 'f<b>o</b>o' -> 'foo'");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
|
@ -1,13 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
printf "C++: %5d\n" $(ls *.t.cpp | wc -l)
|
||||
printf "Python: %5d\n" $(head -n1 *.t | grep -a '\bpython' | wc -l)
|
||||
printf "Perl: %5d\n" $(head -n1 *.t | grep -a '\bperl\b' | wc -l)
|
||||
if [ "$1" = "-v" ]; then
|
||||
echo "Perl left: " $(grep -l '^#\! \?/usr/bin/env perl\b' *.t)
|
||||
fi
|
||||
echo
|
||||
printf "Feature %5d\n" $(ls feature.*.t | wc -l)
|
||||
printf "Bug %5d\n" $(ls {tw-,bug.}*.t | wc -l)
|
||||
echo
|
||||
printf "Total: %5d\n" $(ls *.t | wc -l)
|
281
test/fs.t.cpp
281
test/fs.t.cpp
|
@ -1,281 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2006 - 2016, 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 <algorithm>
|
||||
#include <stdlib.h>
|
||||
#include <FS.h>
|
||||
#include <test.h>
|
||||
|
||||
int main (int, char**)
|
||||
{
|
||||
UnitTest t (108);
|
||||
|
||||
// Ensure environment has no influence.
|
||||
unsetenv ("TASKDATA");
|
||||
unsetenv ("TASKRC");
|
||||
|
||||
// Path ();
|
||||
Path p0;
|
||||
t.is (p0._data, "", "Path::Path");
|
||||
|
||||
// Path (const Path&);
|
||||
Path p1 = Path ("foo");
|
||||
t.is (p1._data, Directory::cwd () + "/foo", "Path::operator=");
|
||||
|
||||
// Path (const std::string&);
|
||||
Path p2 ("~");
|
||||
t.ok (p2._data != "~", "~ expanded to " + p2._data);
|
||||
|
||||
Path p3 ("/tmp");
|
||||
t.ok (p3._data == "/tmp", "/tmp -> /tmp");
|
||||
|
||||
// Path& operator= (const Path&);
|
||||
Path p3_copy (p3);
|
||||
t.is (p3._data, p3_copy._data, "Path::Path (Path&)");
|
||||
|
||||
// operator (std::string) const;
|
||||
t.is ((std::string) p3, "/tmp", "Path::operator (std::string) const");
|
||||
|
||||
// std::string name () const;
|
||||
Path p4 ("/a/b/c/file.ext");
|
||||
t.is (p4.name (), "file.ext", "/a/b/c/file.ext name is file.ext");
|
||||
|
||||
// std::string parent () const;
|
||||
t.is (p4.parent (), "/a/b/c", "/a/b/c/file.ext parent is /a/b/c");
|
||||
|
||||
// std::string extension () const;
|
||||
t.is (p4.extension (), "ext", "/a/b/c/file.ext extension is ext");
|
||||
|
||||
// bool exists () const;
|
||||
t.ok (p2.exists (), "~ exists");
|
||||
t.ok (p3.exists (), "/tmp exists");
|
||||
|
||||
// bool is_directory () const;
|
||||
t.ok (p2.is_directory (), "~ is_directory");
|
||||
t.ok (p3.is_directory (), "/tmp is_directory");
|
||||
|
||||
// bool readable () const;
|
||||
t.ok (p2.readable (), "~ readable");
|
||||
t.ok (p3.readable (), "/tmp readable");
|
||||
|
||||
// bool writable () const;
|
||||
t.ok (p2.writable (), "~ writable");
|
||||
t.ok (p3.writable (), "/tmp writable");
|
||||
|
||||
// bool executable () const;
|
||||
t.ok (p2.executable (), "~ executable");
|
||||
t.ok (p3.executable (), "/tmp executable");
|
||||
|
||||
// static std::string expand (const std::string&);
|
||||
t.ok (Path::expand ("~") != "~", "Path::expand ~ != ~");
|
||||
t.ok (Path::expand ("~/") != "~/", "Path::expand ~/ != ~/");
|
||||
|
||||
// static std::vector <std::string> glob (const std::string&);
|
||||
std::vector <std::string> out = Path::glob ("/tmp");
|
||||
t.ok (out.size () == 1, "/tmp -> 1 result");
|
||||
t.is (out[0], "/tmp", "/tmp -> /tmp");
|
||||
|
||||
out = Path::glob ("/t?p");
|
||||
t.ok (out.size () == 1, "/t?p -> 1 result");
|
||||
t.is (out[0], "/tmp", "/t?p -> /tmp");
|
||||
|
||||
out = Path::glob ("/[s-u]mp");
|
||||
t.ok (out.size () == 1, "/[s-u]mp -> 1 result");
|
||||
t.is (out[0], "/tmp", "/[s-u]mp -> /tmp");
|
||||
|
||||
// bool is_absolute () const;
|
||||
t.notok (p0.is_absolute (), "'' !is_absolute");
|
||||
t.ok (p1.is_absolute (), "foo is_absolute");
|
||||
t.ok (p2.is_absolute (), "~ is_absolute (after expansion)");
|
||||
t.ok (p3.is_absolute (), "/tmp is_absolute");
|
||||
t.ok (p4.is_absolute (), "/a/b/c/file.ext is_absolute");
|
||||
|
||||
Directory tmp ("tmp");
|
||||
tmp.create ();
|
||||
t.ok (tmp.exists (), "tmp dir created.");
|
||||
|
||||
File::write ("tmp/file.t.txt", "This is a test\n");
|
||||
File f6 ("tmp/file.t.txt");
|
||||
t.ok (f6.size () == 15, "File::size tmp/file.t.txt good");
|
||||
t.ok (f6.mode () & S_IRUSR, "File::mode tmp/file.t.txt good");
|
||||
t.ok (File::remove ("tmp/file.t.txt"), "File::remove tmp/file.t.txt good");
|
||||
|
||||
// operator (std::string) const;
|
||||
t.is ((std::string) f6, Directory::cwd () + "/tmp/file.t.txt", "File::operator (std::string) const");
|
||||
|
||||
t.ok (File::create ("tmp/file.t.create"), "File::create tmp/file.t.create good");
|
||||
t.ok (File::remove ("tmp/file.t.create"), "File::remove tmp/file.t.create good");
|
||||
|
||||
// basename (std::string) const;
|
||||
t.is (f6.name (), "file.t.txt", "File::basename tmp/file.t.txt --> file.t.txt");
|
||||
|
||||
// dirname (std::string) const;
|
||||
t.is (f6.parent (), Directory::cwd () + "/tmp", "File::dirname tmp/file.t.txt --> tmp");
|
||||
|
||||
// bool rename (const std::string&);
|
||||
File f7 ("tmp/file.t.2.txt");
|
||||
f7.append ("something\n");
|
||||
f7.close ();
|
||||
|
||||
t.ok (f7.rename ("tmp/file.t.3.txt"), "File::rename did not fail");
|
||||
t.is (f7._data, Directory::cwd () + "/tmp/file.t.3.txt", "File::rename stored new name");
|
||||
t.ok (f7.exists (), "File::rename new file exists");
|
||||
t.ok (f7.remove (), "File::remove tmp/file.t.3.txt good");
|
||||
t.notok (f7.exists (), "File::remove new file no longer exists");
|
||||
|
||||
// Test permissions.
|
||||
File f8 ("tmp/file.t.perm.txt");
|
||||
f8.create (0744);
|
||||
t.ok (f8.exists (), "File::create perm file exists");
|
||||
mode_t m = f8.mode ();
|
||||
t.ok (m & S_IFREG, "File::mode tmp/file.t.perm.txt S_IFREG good");
|
||||
t.ok (m & S_IRUSR, "File::mode tmp/file.t.perm.txt r-------- good");
|
||||
t.ok (m & S_IWUSR, "File::mode tmp/file.t.perm.txt -w------- good");
|
||||
t.ok (m & S_IXUSR, "File::mode tmp/file.t.perm.txt --x------ good");
|
||||
t.ok (m & S_IRGRP, "File::mode tmp/file.t.perm.txt ---r----- good");
|
||||
t.notok (m & S_IWGRP, "File::mode tmp/file.t.perm.txt ----w---- good");
|
||||
t.notok (m & S_IXGRP, "File::mode tmp/file.t.perm.txt -----x--- good");
|
||||
t.ok (m & S_IROTH, "File::mode tmp/file.t.perm.txt ------r-- good");
|
||||
t.notok (m & S_IWOTH, "File::mode tmp/file.t.perm.txt -------w- good");
|
||||
t.notok (m & S_IXOTH, "File::mode tmp/file.t.perm.txt --------x good");
|
||||
f8.remove ();
|
||||
t.notok (f8.exists (), "File::remove perm file no longer exists");
|
||||
|
||||
tmp.remove ();
|
||||
t.notok (tmp.exists (), "tmp dir removed.");
|
||||
tmp.create ();
|
||||
t.ok (tmp.exists (), "tmp dir created.");
|
||||
|
||||
// Directory (const File&);
|
||||
// Directory (const Path&);
|
||||
Directory d0 (Path ("tmp"));
|
||||
Directory d1 (File ("tmp"));
|
||||
Directory d2 (File (Path ("tmp")));
|
||||
t.is (d0._data, d1._data, "Directory(std::string) == Directory (File&)");
|
||||
t.is (d0._data, d2._data, "Directory(std::string) == Directory (File (Path &))");
|
||||
t.is (d1._data, d2._data, "Directory(File&)) == Directory (File (Path &))");
|
||||
|
||||
// Directory (const Directory&);
|
||||
Directory d3 (d2);
|
||||
t.is (d3._data, Directory::cwd () + "/tmp", "Directory (Directory&)");
|
||||
|
||||
// Directory (const std::string&);
|
||||
Directory d4 ("tmp/test_directory");
|
||||
|
||||
// Directory& operator= (const Directory&);
|
||||
Directory d5 = d4;
|
||||
t.is (d5._data, Directory::cwd () + "/tmp/test_directory", "Directory::operator=");
|
||||
|
||||
// operator (std::string) const;
|
||||
t.is ((std::string) d3, Directory::cwd () + "/tmp", "Directory::operator (std::string) const");
|
||||
|
||||
// virtual bool create ();
|
||||
t.ok (d5.create (), "Directory::create tmp/test_directory");
|
||||
t.ok (d5.exists (), "Directory::exists tmp/test_directory");
|
||||
|
||||
Directory d6 (d5._data + "/dir");
|
||||
t.ok (d6.create (), "Directory::create tmp/test_directory/dir");
|
||||
|
||||
File::create (d5._data + "/f0");
|
||||
File::create (d6._data + "/f1");
|
||||
|
||||
// std::vector <std::string> list ();
|
||||
std::vector <std::string> files = d5.list ();
|
||||
std::sort (files.begin (), files.end ());
|
||||
t.is ((int)files.size (), 2, "Directory::list 1 file");
|
||||
t.is (files[0], Directory::cwd () + "/tmp/test_directory/dir", "file[0] is tmp/test_directory/dir");
|
||||
t.is (files[1], Directory::cwd () + "/tmp/test_directory/f0", "file[1] is tmp/test_directory/f0");
|
||||
|
||||
// std::vector <std::string> listRecursive ();
|
||||
files = d5.listRecursive ();
|
||||
std::sort (files.begin (), files.end ());
|
||||
t.is ((int)files.size (), 2, "Directory::list 1 file");
|
||||
t.is (files[0], Directory::cwd () + "/tmp/test_directory/dir/f1", "file is tmp/test_directory/dir/f1");
|
||||
t.is (files[1], Directory::cwd () + "/tmp/test_directory/f0", "file is tmp/test_directory/f0");
|
||||
|
||||
// virtual bool remove ();
|
||||
t.ok (File::remove (d5._data + "/f0"), "File::remove tmp/test_directory/f0");
|
||||
t.ok (File::remove (d6._data + "/f1"), "File::remove tmp/test_directory/dir/f1");
|
||||
|
||||
t.ok (d6.remove (), "Directory::remove tmp/test_directory/dir");
|
||||
t.notok (d6.exists (), "Directory::exists tmp/test_directory/dir - no");
|
||||
|
||||
t.ok (d5.remove (), "Directory::remove tmp/test_directory");
|
||||
t.notok (d5.exists (), "Directory::exists tmp/test_directory - no");
|
||||
|
||||
// bool remove (const std::string&);
|
||||
Directory d7 ("tmp/to_be_removed");
|
||||
t.ok (d7.create (), "Directory::create tmp/to_be_removed");
|
||||
File::create (d7._data + "/f0");
|
||||
Directory d8 (d7._data + "/another");
|
||||
t.ok (d8.create (), "Directory::create tmp/to_be_removed/another");
|
||||
File::create (d8._data + "/f1");
|
||||
t.ok (d7.remove (), "Directory::remove tmp/to_be_removed");
|
||||
t.notok (d7.exists (), "Directory tmp/to_be_removed gone");
|
||||
|
||||
// static std::string cwd ();
|
||||
std::string cwd = Directory::cwd ();
|
||||
t.ok (cwd.length () > 0, "Directory::cwd returned a value");
|
||||
|
||||
// bool parent (std::string&) const;
|
||||
Directory d9 ("/one/two/three/four.txt");
|
||||
t.ok (d9.up (), "parent /one/two/three/four.txt --> true");
|
||||
t.is (d9._data, "/one/two/three", "parent /one/two/three/four.txt --> /one/two/three");
|
||||
t.ok (d9.up (), "parent /one/two/three --> true");
|
||||
t.is (d9._data, "/one/two", "parent /one/two/three --> /one/two");
|
||||
t.ok (d9.up (), "parent /one/two --> true");
|
||||
t.is (d9._data, "/one", "parent /one/two --> /one");
|
||||
t.ok (d9.up (), "parent /one --> true");
|
||||
t.is (d9._data, "/", "parent /one --> /");
|
||||
t.notok (d9.up (), "parent / --> false");
|
||||
|
||||
// Test permissions.
|
||||
umask (0022);
|
||||
Directory d10 ("tmp/dir.perm");
|
||||
d10.create (0750);
|
||||
t.ok (d10.exists (), "Directory::create perm file exists");
|
||||
m = d10.mode ();
|
||||
t.ok (m & S_IFDIR, "Directory::mode tmp/dir.perm S_IFDIR good");
|
||||
t.ok (m & S_IRUSR, "Directory::mode tmp/dir.perm r-------- good");
|
||||
t.ok (m & S_IWUSR, "Directory::mode tmp/dir.perm -w------- good");
|
||||
t.ok (m & S_IXUSR, "Directory::mode tmp/dir.perm --x------ good");
|
||||
t.ok (m & S_IRGRP, "Directory::mode tmp/dir.perm ---r----- good");
|
||||
t.notok (m & S_IWGRP, "Directory::mode tmp/dir.perm ----w---- good");
|
||||
t.ok (m & S_IXGRP, "Directory::mode tmp/dir.perm -----x--- good");
|
||||
t.notok (m & S_IROTH, "Directory::mode tmp/dir.perm ------r-- good");
|
||||
t.notok (m & S_IWOTH, "Directory::mode tmp/dir.perm -------w- good");
|
||||
t.notok (m & S_IXOTH, "Directory::mode tmp/dir.perm --------x good");
|
||||
d10.remove ();
|
||||
t.notok (d10.exists (), "Directory::remove temp/dir.perm file no longer exists");
|
||||
|
||||
tmp.remove ();
|
||||
t.notok (tmp.exists (), "tmp dir removed.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
Loading…
Add table
Add a link
Reference in a new issue