Common: Removed src/common

- This code is now relocated to common.git, and included via CMake's
  ExternalProject command.
This commit is contained in:
Paul Beckingham 2016-01-18 22:55:28 -05:00
parent 77f51a41bd
commit 2d420397f9
20 changed files with 0 additions and 3690 deletions

View file

@ -1 +0,0 @@
libcommon.a

View file

@ -1,21 +0,0 @@
cmake_minimum_required (VERSION 2.8)
include_directories (${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/src/common
${TIMEW_INCLUDE_DIRS})
set (common_SRCS Color.cpp Color.h
FS.cpp FS.h
RX.cpp RX.h
Table.cpp Table.h
Timer.cpp Timer.h
text.cpp text.h
unicode.cpp unicode.h
utf8.cpp utf8.h
wcwidth6.cpp)
add_library (common STATIC ${common_SRCS})
#SET(CMAKE_BUILD_TYPE gcov)
#SET(CMAKE_CXX_FLAGS_GCOV "--coverage")
#SET(CMAKE_C_FLAGS_GCOV "--coverage")
#SET(CMAKE_EXE_LINKER_FLAGS_GCOV "--coverage")

View file

@ -1,599 +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 <sstream>
#include <vector>
#include <cstdlib>
//#include <Lexer.h>
#include <text.h>
// uint to string lookup table for Color::_colorize()
// _colorize() gets called _a lot_, having this lookup table is a cheap
// performance optimization.
const char *colorstring[] = {
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
"20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
"30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
"50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
"60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
"70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
"80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
"90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
"100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
"110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
"120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
"130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
"140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
"150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
"160", "161", "162", "163", "164", "165", "166", "167", "168", "169",
"170", "171", "172", "173", "174", "175", "176", "177", "178", "179",
"180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
"190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
"200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
"210", "211", "212", "213", "214", "215", "216", "217", "218", "219",
"220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
"230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
"240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
"250", "251", "252", "253", "254", "255"
};
////////////////////////////////////////////////////////////////////////////////
static struct
{
Color::color_id id;
std::string english_name;
int index; // offset red=3 (therefore fg=33, bg=43)
} allColors[] =
{
// Color.h enum English Index
{ Color::nocolor, "none", 0},
{ Color::black, "black", 1}, // fg 29+0 bg 39+0
{ Color::red, "red", 2},
{ Color::green, "green", 3},
{ Color::yellow, "yellow", 4},
{ Color::blue, "blue", 5},
{ Color::magenta, "magenta", 6},
{ Color::cyan, "cyan", 7},
{ Color::white, "white", 8},
};
#define NUM_COLORS (sizeof (allColors) / sizeof (allColors[0]))
////////////////////////////////////////////////////////////////////////////////
Color::Color ()
: _value (0)
{
}
////////////////////////////////////////////////////////////////////////////////
Color::Color (const Color& other)
{
_value = other._value;
}
////////////////////////////////////////////////////////////////////////////////
Color::Color (unsigned int c)
: _value (0)
{
if (!(c & _COLOR_HASFG)) _value &= ~_COLOR_FG;
if (!(c & _COLOR_HASBG)) _value &= ~_COLOR_BG;
_value = c & (_COLOR_256 | _COLOR_HASBG | _COLOR_HASFG |_COLOR_UNDERLINE |
_COLOR_INVERSE | _COLOR_BOLD | _COLOR_BRIGHT | _COLOR_BG |
_COLOR_FG);
}
////////////////////////////////////////////////////////////////////////////////
// Supports the following constructs:
// [bright] [color] [on color] [bright] [underline]
//
// Where [color] is one of:
// black
// red
// ...
// grayN 0 <= N <= 23 fg 38;5;232 + N bg 48;5;232 + N
// greyN 0 <= N <= 23 fg 38;5;232 + N bg 48;5;232 + N
// colorN 0 <= N <= 255 fg 38;5;N bg 48;5;N
// rgbRGB 0 <= R,G,B <= 5 fg 38;5;16 + R*36 + G*6 + B bg 48;5;16 + R*36 + G*6 + B
Color::Color (const std::string& spec)
: _value (0)
{
// Split spec into words.
auto words = split (spec, ' ');
// Construct the color as two separate colors, then blend them later. This
// make it possible to declare a color such as "color1 on black", and have
// the upgrade work properly.
unsigned int fg_value = 0;
unsigned int bg_value = 0;
bool bg = false;
int index;
for (auto& word : words)
{
// word = Lexer::lowerCase (Lexer::trim (word));
if (word == "bold") fg_value |= _COLOR_BOLD;
else if (word == "bright") bg_value |= _COLOR_BRIGHT;
else if (word == "underline") fg_value |= _COLOR_UNDERLINE;
else if (word == "inverse") fg_value |= _COLOR_INVERSE;
else if (word == "on") bg = true;
// X where X is one of black, red, blue ...
else if ((index = find (word)) != -1)
{
if (index)
{
if (bg)
{
bg_value |= _COLOR_HASBG;
bg_value |= index << 8;
}
else
{
fg_value |= _COLOR_HASFG;
fg_value |= index;
}
}
}
// greyN/grayN, where 0 <= N <= 23.
else if (! word.compare (0, 4, "grey", 4) ||
! word.compare (0, 4, "gray", 4))
{
index = strtol (word.substr (4).c_str (), NULL, 10);
if (index < 0 || index > 23)
throw format ("The color '{1}' is not recognized.", word);
if (bg)
{
bg_value |= _COLOR_HASBG;
bg_value |= (index + 232) << 8;
bg_value |= _COLOR_256;
}
else
{
fg_value |= _COLOR_HASFG;
fg_value |= index + 232;
fg_value |= _COLOR_256;
}
}
// rgbRGB, where 0 <= R,G,B <= 5.
else if (! word.compare (0, 3, "rgb", 3))
{
index = strtol (word.substr (3).c_str (), NULL, 10);
if (word.length () != 6 ||
index < 0 || index > 555)
throw format ("The color '{1}' is not recognized.", word);
int r = strtol (word.substr (3, 1).c_str (), NULL, 10);
int g = strtol (word.substr (4, 1).c_str (), NULL, 10);
int b = strtol (word.substr (5, 1).c_str (), NULL, 10);
if (r < 0 || r > 5 ||
g < 0 || g > 5 ||
b < 0 || b > 5)
throw format ("The color '{1}' is not recognized.", word);
index = 16 + r*36 + g*6 + b;
if (bg)
{
bg_value |= _COLOR_HASBG;
bg_value |= index << 8;
bg_value |= _COLOR_256;
}
else
{
fg_value |= _COLOR_HASFG;
fg_value |= index;
fg_value |= _COLOR_256;
}
}
// colorN, where 0 <= N <= 255.
else if (! word.compare (0, 5, "color", 5))
{
index = strtol (word.substr (5).c_str (), NULL, 10);
if (index < 0 || index > 255)
throw format ("The color '{1}' is not recognized.", word);
upgrade ();
if (bg)
{
bg_value |= _COLOR_HASBG;
bg_value |= index << 8;
bg_value |= _COLOR_256;
}
else
{
fg_value |= _COLOR_HASFG;
fg_value |= index;
fg_value |= _COLOR_256;
}
}
else if (word != "")
throw format ("The color '{1}' is not recognized.", word);
}
// Now combine the fg and bg into a single color.
_value = fg_value;
blend (Color (bg_value));
}
////////////////////////////////////////////////////////////////////////////////
Color::Color (color_id fg)
: _value (0)
{
if (fg != Color::nocolor)
{
_value |= _COLOR_HASFG;
_value |= fg;
}
}
////////////////////////////////////////////////////////////////////////////////
Color::Color (color_id fg, color_id bg, bool underline, bool bold, bool bright)
: _value (0)
{
_value |= ((underline ? 1 : 0) << 18)
| ((bold ? 1 : 0) << 17)
| ((bright ? 1 : 0) << 16);
if (bg != Color::nocolor)
{
_value |= _COLOR_HASBG;
_value |= (bg << 8);
}
if (fg != Color::nocolor)
{
_value |= _COLOR_HASFG;
_value |= fg;
}
}
////////////////////////////////////////////////////////////////////////////////
Color::operator std::string () const
{
std::string description;
if (_value & _COLOR_BOLD) description += "bold";
if (_value & _COLOR_UNDERLINE)
description += std::string (description.length () ? " " : "") + "underline";
if (_value & _COLOR_INVERSE)
description += std::string (description.length () ? " " : "") + "inverse";
if (_value & _COLOR_HASFG)
description += std::string (description.length () ? " " : "") + fg ();
if (_value & _COLOR_HASBG)
{
description += std::string (description.length () ? " " : "") + "on";
if (_value & _COLOR_BRIGHT)
description += std::string (description.length () ? " " : "") + "bright";
description += " " + bg ();
}
return description;
}
////////////////////////////////////////////////////////////////////////////////
Color::operator int () const
{
return (int) _value;
}
////////////////////////////////////////////////////////////////////////////////
// If 'other' has styles that are compatible, merge them into this. Colors in
// other take precedence.
void Color::blend (const Color& other)
{
if (!other.nontrivial ())
return;
Color c (other);
_value |= (c._value & _COLOR_UNDERLINE); // Always inherit underline.
_value |= (c._value & _COLOR_INVERSE); // Always inherit inverse.
// 16 <-- 16.
if (!(_value & _COLOR_256) &&
!(c._value & _COLOR_256))
{
_value |= (c._value & _COLOR_BOLD); // Inherit bold.
_value |= (c._value & _COLOR_BRIGHT); // Inherit bright.
if (c._value & _COLOR_HASFG)
{
_value |= _COLOR_HASFG; // There is now a color.
_value &= ~_COLOR_FG; // Remove previous color.
_value |= (c._value & _COLOR_FG); // Apply other color.
}
if (c._value & _COLOR_HASBG)
{
_value |= _COLOR_HASBG; // There is now a color.
_value &= ~_COLOR_BG; // Remove previous color.
_value |= (c._value & _COLOR_BG); // Apply other color.
}
return;
}
else
{
// Upgrade either color, if necessary.
if (!(_value & _COLOR_256)) upgrade ();
if (!(c._value & _COLOR_256)) c.upgrade ();
// 256 <-- 256.
if (c._value & _COLOR_HASFG)
{
_value |= _COLOR_HASFG; // There is now a color.
_value &= ~_COLOR_FG; // Remove previous color.
_value |= (c._value & _COLOR_FG); // Apply other color.
}
if (c._value & _COLOR_HASBG)
{
_value |= _COLOR_HASBG; // There is now a color.
_value &= ~_COLOR_BG; // Remove previous color.
_value |= (c._value & _COLOR_BG); // Apply other color.
}
}
}
////////////////////////////////////////////////////////////////////////////////
void Color::upgrade ()
{
if (!(_value & _COLOR_256))
{
if (_value & _COLOR_HASFG)
{
bool bold = _value & _COLOR_BOLD;
unsigned int fg = _value & _COLOR_FG;
_value &= ~_COLOR_FG;
_value &= ~_COLOR_BOLD;
_value |= (bold ? fg + 7 : fg - 1);
}
if (_value & _COLOR_HASBG)
{
bool bright = _value & _COLOR_BRIGHT;
unsigned int bg = (_value & _COLOR_BG) >> 8;
_value &= ~_COLOR_BG;
_value &= ~_COLOR_BRIGHT;
_value |= (bright ? bg + 7 : bg - 1) << 8;
}
_value |= _COLOR_256;
}
}
////////////////////////////////////////////////////////////////////////////////
std::string Color::colorize (const std::string& input) const
{
std::string result;
_colorize (result, input);
return result;
}
////////////////////////////////////////////////////////////////////////////////
// Sample color codes:
// red \033[31m
// bold red \033[91m
// underline red \033[4;31m
// bold underline red \033[1;4;31m
//
// on red \033[41m
// on bright red \033[101m
//
// 256 fg \033[38;5;Nm
// 256 bg \033[48;5;Nm
void Color::_colorize (std::string &result, const std::string& input) const
{
if (!nontrivial ())
{
result += input;
return;
}
int count = 0;
// 256 color
if (_value & _COLOR_256)
{
if (_value & _COLOR_UNDERLINE)
result += "\033[4m";
if (_value & _COLOR_INVERSE)
result += "\033[7m";
if (_value & _COLOR_HASFG)
{
result += "\033[38;5;";
result += colorstring[(_value & _COLOR_FG)];
result += "m";
}
if (_value & _COLOR_HASBG)
{
result += "\033[48;5;";
result += colorstring[((_value & _COLOR_BG) >> 8)];
result += "m";
}
result += input;
result += "\033[0m";
}
// 16 color
else
{
result += "\033[";
if (_value & _COLOR_BOLD)
{
if (count++) result += ";";
result += "1";
}
if (_value & _COLOR_UNDERLINE)
{
if (count++) result += ";";
result += "4";
}
if (_value & _COLOR_INVERSE)
{
if (count++) result += ";";
result += "7";
}
if (_value & _COLOR_HASFG)
{
if (count++) result += ";";
result += colorstring[(29 + (_value & _COLOR_FG))];
}
if (_value & _COLOR_HASBG)
{
if (count++) result += ";";
result += colorstring[((_value & _COLOR_BRIGHT ? 99 : 39) + ((_value & _COLOR_BG) >> 8))];
}
result += "m";
result += input;
result += "\033[0m";
}
}
////////////////////////////////////////////////////////////////////////////////
// Remove color codes from a string.
std::string Color::strip (const std::string& input)
{
int length = input.length ();
bool inside = false;
std::string output;
for (int i = 0; i < length; ++i)
{
if (inside)
{
if (input[i] == 'm')
inside = false;
}
else
{
if (input[i] == 033)
inside = true;
else
output += input[i];
}
}
return output;
}
////////////////////////////////////////////////////////////////////////////////
std::string Color::colorize (const std::string& input, const std::string& spec)
{
Color c (spec);
return c.colorize (input);
}
////////////////////////////////////////////////////////////////////////////////
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 "";
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -1,77 +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, bool, bool, bool); // fg, bg, underline, bold, bright
operator std::string () const;
operator int () const;
void upgrade ();
void blend (const Color&);
std::string colorize (const std::string&) const;
static std::string colorize (const std::string&, const std::string&);
void _colorize (std::string&, const std::string&) const;
static std::string strip (const std::string&);
bool nontrivial () const;
private:
int find (const std::string&);
std::string fg () const;
std::string bg () const;
private:
unsigned int _value;
};
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -1,892 +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>
#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 == NULL)
{
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, NULL, &g))
#else
if (!::glob (pattern.c_str (), GLOB_ERR | GLOB_BRACE | GLOB_TILDE, NULL, &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 (NULL)
, _h (-1)
, _locked (false)
{
}
////////////////////////////////////////////////////////////////////////////////
File::File (const Path& other)
: Path::Path (other)
, _fh (NULL)
, _h (-1)
, _locked (false)
{
}
////////////////////////////////////////////////////////////////////////////////
File::File (const File& other)
: Path::Path (other)
, _fh (NULL)
, _h (-1)
, _locked (false)
{
}
////////////////////////////////////////////////////////////////////////////////
File::File (const std::string& in)
: Path::Path (in)
, _fh (NULL)
, _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 ("Insufficient permissions for '{1}'.", _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 = NULL;
_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 != NULL)
{
struct dirent* de;
while ((de = readdir (dp)) != NULL)
{
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 == NULL)
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 != NULL)
{
struct dirent* de;
while ((de = readdir (dp)) != NULL)
{
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);
}
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -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
////////////////////////////////////////////////////////////////////////////////

View file

@ -1,16 +0,0 @@
Common
======
The 'common' library is a set of reusable objects to be shared as-is between
various programs, including Taskwarrior, Taskserver, Tasksh and Timewarrior.
In order to be reusable, and therefore a member of libcommon, rules must be
followed:
- Common objects may only make use of other common objects, or external
dependencies (libuuid, gettext, readline etc), but no objects from the
parent directories.
- Common objects can assume only an external 'cmake.h' that defines
platform/portability constants.

View file

@ -1,159 +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 <RX.h>
#include <cstdlib>
#include <cstring>
////////////////////////////////////////////////////////////////////////////////
RX::RX ()
{
}
////////////////////////////////////////////////////////////////////////////////
RX::RX (
const std::string& pattern,
bool case_sensitive /* = true */)
: _compiled (false)
, _pattern (pattern)
, _case_sensitive (case_sensitive)
{
compile ();
}
////////////////////////////////////////////////////////////////////////////////
RX::RX (const RX& other)
{
_compiled = false;
_pattern = other._pattern;
_case_sensitive = other._case_sensitive;
}
////////////////////////////////////////////////////////////////////////////////
RX::~RX ()
{
if (_compiled)
regfree (&_regex);
}
////////////////////////////////////////////////////////////////////////////////
RX& RX::operator= (const RX& other)
{
_compiled = false;
_pattern = other._pattern;
_case_sensitive = other._case_sensitive;
return *this;
}
////////////////////////////////////////////////////////////////////////////////
void RX::compile ()
{
if (! _compiled)
{
memset (&_regex, 0, sizeof (regex_t));
int result;
if ((result = regcomp (&_regex, _pattern.c_str (),
#if defined REG_ENHANCED
REG_ENHANCED | REG_EXTENDED | REG_NEWLINE |
#else
REG_EXTENDED | REG_NEWLINE |
#endif
(_case_sensitive ? 0 : REG_ICASE))) != 0)
{
char message[256];
regerror (result, &_regex, message, 256);
throw std::string (message);
}
_compiled = true;
}
}
////////////////////////////////////////////////////////////////////////////////
bool RX::match (const std::string& in)
{
if (! _compiled)
compile ();
return regexec (&_regex, in.c_str (), 0, NULL, 0) == 0 ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
bool RX::match (
std::vector<std::string>& matches,
const std::string& in)
{
if (! _compiled)
compile ();
regmatch_t rm[2];
int offset = 0;
int length = in.length ();
while (regexec (&_regex, in.c_str () + offset, 2, &rm[0], 0) == 0 &&
offset < length)
{
matches.push_back (in.substr (rm[0].rm_so + offset, rm[0].rm_eo - rm[0].rm_so));
offset += rm[0].rm_eo;
// Protection against zero-width patterns causing infinite loops.
if (rm[0].rm_so == rm[0].rm_eo)
++offset;
}
return matches.size () ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
bool RX::match (
std::vector <int>& start,
std::vector <int>& end,
const std::string& in)
{
if (! _compiled)
compile ();
regmatch_t rm[2];
int offset = 0;
int length = in.length ();
while (regexec (&_regex, in.c_str () + offset, 2, &rm[0], 0) == 0 &&
offset < length)
{
start.push_back (rm[0].rm_so + offset);
end.push_back (rm[0].rm_eo + offset);
offset += rm[0].rm_eo;
// Protection against zero-width patterns causing infinite loops.
if (rm[0].rm_so == rm[0].rm_eo)
++offset;
}
return start.size () ? true : false;
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -1,58 +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_RX
#define INCLUDED_RX
#include <string>
#include <vector>
#include <regex.h>
class RX
{
public:
RX ();
RX (const std::string&, bool caseSensitive = true);
RX (const RX&);
~RX ();
RX& operator= (const RX&);
bool match (const std::string&);
bool match (std::vector<std::string>&, const std::string&);
bool match (std::vector <int>&, std::vector <int>&, const std::string&);
private:
void compile ();
private:
bool _compiled {false};
std::string _pattern {};
bool _case_sensitive {false};
regex_t _regex;
};
#endif

View file

@ -1,312 +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 <Table.h>
#include <text.h>
#include <utf8.h>
////////////////////////////////////////////////////////////////////////////////
Table::Table ()
: _width (0)
, _left_margin (0)
, _header (0)
, _odd (0)
, _even (0)
, _intra_padding (1)
, _intra_odd (0)
, _intra_even (0)
, _extra_padding (0)
, _extra_odd (0)
, _extra_even (0)
, _truncate_lines (0)
, _truncate_rows (0)
, _lines (0)
, _rows (0)
{
}
////////////////////////////////////////////////////////////////////////////////
int Table::addRow ()
{
_data.push_back (std::vector <std::string> (_columns.size (), ""));
_color.push_back (std::vector <Color> (_columns.size (), Color::nocolor));
return _data.size () - 1;
}
////////////////////////////////////////////////////////////////////////////////
void Table::set (int row, int col, const std::string& value, const Color color)
{
_data[row][col] = value;
if (color.nontrivial ())
_color[row][col] = color;
}
////////////////////////////////////////////////////////////////////////////////
void Table::set (int row, int col, int value, const Color color)
{
std::string string_value = format (value);
_data[row][col] = string_value;
if (color.nontrivial ())
_color[row][col] = color;
}
////////////////////////////////////////////////////////////////////////////////
void Table::set (int row, int col, const Color color)
{
if (color.nontrivial ())
_color[row][col] = color;
}
////////////////////////////////////////////////////////////////////////////////
std::string Table::render ()
{
// Determine minimal, ideal column widths.
std::vector <int> minimal;
std::vector <int> ideal;
for (unsigned int col = 0; col < _columns.size (); ++col)
{
// Headers factor in to width calculations.
unsigned int global_min = utf8_width (_columns[col]);
unsigned int global_ideal = global_min;
for (unsigned int row = 0; row < _data.size (); ++row)
{
// Determine minimum and ideal width for this column.
unsigned int min = 0;
unsigned int ideal = 0;
measureCell (_data[row][col], min, ideal);
if (min > global_min) global_min = min;
if (ideal > global_ideal) global_ideal = ideal;
}
minimal.push_back (global_min);
ideal.push_back (global_ideal);
}
// Sum the minimal widths.
int sum_minimal = 0;
for (auto& c : minimal)
sum_minimal += c;
// Sum the ideal widths.
int sum_ideal = 0;
for (auto& c : ideal)
sum_ideal += c;
// Calculate final column widths.
int overage = _width
- _left_margin
- (2 * _extra_padding)
- ((_columns.size () - 1) * _intra_padding);
std::vector <int> widths;
if (sum_ideal <= overage)
widths = ideal;
else if (sum_minimal > overage || overage < 0)
widths = minimal;
else if (overage > 0)
{
widths = minimal;
overage -= sum_minimal;
// Spread 'overage' among columns where width[i] < ideal[i]
while (overage)
{
for (unsigned int i = 0; i < _columns.size () && overage; ++i)
{
if (widths[i] < ideal[i])
{
++widths[i];
--overage;
}
}
}
}
// Compose column headers.
unsigned int max_lines = 0;
std::vector <std::vector <std::string>> headers;
for (unsigned int c = 0; c < _columns.size (); ++c)
{
headers.push_back ({});
renderCell (headers[c], _columns[c], widths[c], _align[c], _header);
if (headers[c].size () > max_lines)
max_lines = headers[c].size ();
}
// Output string.
std::string out;
_lines = 0;
// Render column headers.
std::string left_margin = std::string (_left_margin, ' ');
std::string extra = std::string (_extra_padding, ' ');
std::string intra = std::string (_intra_padding, ' ');
std::string extra_odd = _extra_odd.colorize (extra);
std::string extra_even = _extra_even.colorize (extra);
std::string intra_odd = _intra_odd.colorize (intra);
std::string intra_even = _intra_even.colorize (intra);
for (unsigned int i = 0; i < max_lines; ++i)
{
out += left_margin + extra;
for (unsigned int c = 0; c < _columns.size (); ++c)
{
if (c)
out += intra;
if (headers[c].size () < max_lines - i)
out += _header.colorize (std::string (widths[c], ' '));
else
out += headers[c][i];
}
out += extra;
// Trim right.
out.erase (out.find_last_not_of (" ") + 1);
out += "\n";
// Stop if the line limit is exceeded.
if (++_lines >= _truncate_lines && _truncate_lines != 0)
return out;
}
// Compose, render columns, in sequence.
_rows = 0;
std::vector <std::vector <std::string>> cells;
for (unsigned int row = 0; row < _data.size (); ++row)
{
max_lines = 0;
// Alternate rows based on |s % 2|
bool odd = (row % 2) ? true : false;
Color row_color = odd ? _odd : _even;
// TODO row_color.blend (provided color);
// TODO Problem: colors for columns are specified, not rows,
// therefore there are only cell colors, not intra colors.
Color cell_color;
for (unsigned int col = 0; col < _columns.size (); ++col)
{
cell_color = row_color;
cell_color.blend (_color[row][col]);
cells.push_back (std::vector <std::string> ());
renderCell (cells[col], _data[row][col], widths[col], _align[col], cell_color);
if (cells[col].size () > max_lines)
max_lines = cells[col].size ();
}
for (unsigned int i = 0; i < max_lines; ++i)
{
out += left_margin + (odd ? extra_odd : extra_even);
for (unsigned int col = 0; col < _columns.size (); ++col)
{
if (col)
{
if (row_color.nontrivial ())
out += row_color.colorize (intra);
else
out += (odd ? intra_odd : intra_even);
}
if (i < cells[col].size ())
out += cells[col][i];
else
{
cell_color = row_color;
cell_color.blend (_color[row][col]);
out += cell_color.colorize (std::string (widths[col], ' '));
}
}
out += (odd ? extra_odd : extra_even);
// Trim right.
out.erase (out.find_last_not_of (" ") + 1);
out += "\n";
// Stop if the line limit is exceeded.
if (++_lines >= _truncate_lines && _truncate_lines != 0)
return out;
}
cells.clear ();
// Stop if the row limit is exceeded.
if (++_rows >= _truncate_rows && _truncate_rows != 0)
return out;
}
return out;
}
////////////////////////////////////////////////////////////////////////////////
void Table::measureCell (
const std::string& data,
unsigned int& minimum,
unsigned int& maximum) const
{
std::string stripped = Color::strip (data);
maximum = longestLine (stripped);
minimum = longestWord (stripped);
}
////////////////////////////////////////////////////////////////////////////////
void Table::renderCell (
std::vector <std::string>& lines,
const std::string& value,
int width,
bool alignLeft,
const Color& color) const
{
std::vector <std::string> raw;
wrapText (raw, value, width, false);
for (auto& line : raw)
if (alignLeft)
lines.push_back (
color.colorize (
leftJustify (line, width)));
else
lines.push_back (
color.colorize (
rightJustify (line, width)));
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -1,94 +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_VIEWTEXT
#define INCLUDED_VIEWTEXT
#include <string>
#include <vector>
#include <Color.h>
class Table
{
public:
Table ();
// View specifications.
void add (const std::string& col, bool alignLeft = true) { _columns.push_back (col); _align.push_back (alignLeft); }
void width (int width) { _width = width; }
void leftMargin (int margin) { _left_margin = margin; }
void colorHeader (const Color& c) { _header = c; }
void colorOdd (const Color& c) { _odd = c; }
void colorEven (const Color& c) { _even = c; }
void intraPadding (int padding) { _intra_padding = padding; }
void intraColorOdd (const Color& c) { _intra_odd = c; }
void intraColorEven (const Color& c) { _intra_even = c; }
void extraPadding (int padding) { _extra_padding = padding; }
void extraColorOdd (const Color& c) { _extra_odd = c; }
void extraColorEven (const Color& c) { _extra_even = c; }
void truncateLines (int n) { _truncate_lines = n; }
void truncateRows (int n) { _truncate_rows = n; }
int lines () { return _lines; }
int rows () { return (int) _data.size (); }
// Data provision.
int addRow ();
void set (int, int, const std::string&, const Color color = Color::nocolor);
void set (int, int, int, const Color color = Color::nocolor);
void set (int, int, const Color);
// View rendering.
std::string render ();
private:
void measureCell (const std::string&, unsigned int&, unsigned int&) const;
void renderCell (std::vector <std::string>&, const std::string&, int, bool, const Color&) const;
private:
std::vector <std::vector <std::string>> _data;
std::vector <std::vector <Color>> _color;
std::vector <std::string> _columns;
std::vector <bool> _align;
int _width;
int _left_margin;
Color _header;
Color _odd;
Color _even;
int _intra_padding;
Color _intra_odd;
Color _intra_even;
int _extra_padding;
Color _extra_odd;
Color _extra_even;
int _truncate_lines;
int _truncate_rows;
int _lines;
int _rows;
};
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -1,119 +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 <Timer.h>
#include <iostream>
#include <iomanip>
#include <sstream>
////////////////////////////////////////////////////////////////////////////////
// Timer starts when the object is constructed.
Timer::Timer ()
: _description ("-")
, _running (false)
, _total (0)
{
}
////////////////////////////////////////////////////////////////////////////////
// Timer starts when the object is constructed with a description.
Timer::Timer (const std::string& description)
: _description (description)
, _running (false)
, _total (0)
{
start ();
}
////////////////////////////////////////////////////////////////////////////////
// Timer stops when the object is destructed.
Timer::~Timer ()
{
stop ();
std::stringstream s;
s << "Timer " // No i18n
<< _description
<< " "
<< std::setprecision (6)
#ifndef HAIKU
// Haiku fails on this - don't know why.
<< std::fixed
#endif
<< _total / 1000000.0
<< " sec\n";
std::cerr << s.str ();
}
////////////////////////////////////////////////////////////////////////////////
void Timer::start ()
{
if (!_running)
{
gettimeofday (&_start, NULL);
_running = true;
}
}
////////////////////////////////////////////////////////////////////////////////
void Timer::stop ()
{
if (_running)
{
struct timeval end;
gettimeofday (&end, NULL);
_running = false;
_total += (end.tv_sec - _start.tv_sec) * 1000000
+ (end.tv_usec - _start.tv_usec);
}
}
////////////////////////////////////////////////////////////////////////////////
unsigned long Timer::total () const
{
return _total;
}
////////////////////////////////////////////////////////////////////////////////
void Timer::subtract (unsigned long value)
{
if (value > _total)
_total = 0;
else
_total -= value;
}
////////////////////////////////////////////////////////////////////////////////
unsigned long Timer::now ()
{
struct timeval now;
gettimeofday (&now, NULL);
return now.tv_sec * 1000000 + now.tv_usec;
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -1,59 +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_TIMER
#define INCLUDED_TIMER
#include <string>
#include <sys/time.h>
// Timer is a scope-activated timer that dumps to std::cout at end of scope.
class Timer
{
public:
Timer ();
Timer (const std::string&);
~Timer ();
Timer (const Timer&);
Timer& operator= (const Timer&);
void start ();
void stop ();
unsigned long total () const;
void subtract (unsigned long);
static unsigned long now ();
private:
std::string _description;
bool _running;
struct timeval _start;
unsigned long _total;
};
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -1,354 +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 <utf8.h>
#include <sstream>
#include <iomanip>
#include <math.h>
///////////////////////////////////////////////////////////////////////////////
void wrapText (
std::vector <std::string>& lines,
const std::string& text,
const int width,
bool hyphenate)
{
std::string line;
unsigned int offset = 0;
while (extractLine (line, text, width, hyphenate, offset))
lines.push_back (line);
}
////////////////////////////////////////////////////////////////////////////////
std::vector <std::string> split (const std::string& input, const char delimiter)
{
std::vector <std::string> results;
std::string::size_type start = 0;
std::string::size_type i;
while ((i = input.find (delimiter, start)) != std::string::npos)
{
results.push_back (input.substr (start, i - start));
start = i + 1;
}
if (input.length ())
results.push_back (input.substr (start));
return results;
}
////////////////////////////////////////////////////////////////////////////////
int longestWord (const std::string& input)
{
int longest = 0;
int length = 0;
std::string::size_type i = 0;
int character;
while ((character = utf8_next_char (input, i)))
{
if (character == ' ')
{
if (length > longest)
longest = length;
length = 0;
}
else
length += mk_wcwidth (character);
}
if (length > longest)
longest = length;
return longest;
}
////////////////////////////////////////////////////////////////////////////////
int longestLine (const std::string& input)
{
int longest = 0;
int length = 0;
std::string::size_type i = 0;
int character;
while ((character = utf8_next_char (input, i)))
{
if (character == '\n')
{
if (length > longest)
longest = length;
length = 0;
}
else
length += mk_wcwidth (character);
}
if (length > longest)
longest = length;
return longest;
}
////////////////////////////////////////////////////////////////////////////////
// Walk the input text looking for a break point. A break point is one of:
// - EOS
// - \n
// - last space before 'length' characters
// - last punctuation (, ; . :) before 'length' characters, even if not
// followed by a space
// - first 'length' characters
//
// text "one two three\n four"
// bytes 0123456789012 3456789
// characters 1234567890a23 4567890
//
// leading_ws
// ws ^ ^ ^^
// punct
// break ^
bool extractLine (
std::string& line,
const std::string& text,
int width,
bool hyphenate,
unsigned int& offset)
{
// Terminate processing.
// Note: bytes vs bytes.
if (offset >= text.length ())
return false;
std::string::size_type last_last_bytes = offset;
std::string::size_type last_bytes = offset;
std::string::size_type bytes = offset;
unsigned int last_ws = 0;
int character;
int char_width = 0;
int line_width = 0;
while (1)
{
last_last_bytes = last_bytes;
last_bytes = bytes;
character = utf8_next_char (text, bytes);
if (character == 0 ||
character == '\n')
{
line = text.substr (offset, last_bytes - offset);
offset = bytes;
break;
}
else if (character == ' ')
last_ws = last_bytes;
char_width = mk_wcwidth (character);
if (line_width + char_width > width)
{
int last_last_character = text[last_last_bytes];
int last_character = text[last_bytes];
// [case 1] one| two --> last_last != 32, last == 32, ws == 0
if (last_last_character != ' ' &&
last_character == ' ')
{
line = text.substr (offset, last_bytes - offset);
offset = last_bytes + 1;
break;
}
// [case 2] one |two --> last_last == 32, last != 32, ws != 0
else if (last_last_character == ' ' &&
last_character != ' ' &&
last_ws != 0)
{
line = text.substr (offset, last_bytes - offset - 1);
offset = last_bytes;
break;
}
else if (last_last_character != ' ' &&
last_character != ' ')
{
// [case 3] one t|wo --> last_last != 32, last != 32, ws != 0
if (last_ws != 0)
{
line = text.substr (offset, last_ws - offset);
offset = last_ws + 1;
break;
}
// [case 4] on|e two --> last_last != 32, last != 32, ws == 0
else
{
if (hyphenate)
{
line = text.substr (offset, last_bytes - offset - 1) + "-";
offset = last_last_bytes;
}
else
{
line = text.substr (offset, last_bytes - offset);
offset = last_bytes;
}
}
break;
}
}
line_width += char_width;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
const std::string format (std::string& value)
{
return value;
}
////////////////////////////////////////////////////////////////////////////////
const std::string format (const char* value)
{
std::string s (value);
return s;
}
////////////////////////////////////////////////////////////////////////////////
const std::string formatHex (int value)
{
std::stringstream s;
s.setf (std::ios::hex, std::ios::basefield);
s << value;
return s.str ();
}
////////////////////////////////////////////////////////////////////////////////
const std::string format (float value, int width, int precision)
{
std::stringstream s;
s.width (width);
s.precision (precision);
if (0 < value && value < 1)
{
// For value close to zero, width - 2 (2 accounts for the first zero and
// the dot) is the number of digits after zero that are significant
double factor = 1;
for (int i = 2; i < width; i++)
factor *= 10;
value = roundf (value * factor) / factor;
}
s << value;
return s.str ();
}
////////////////////////////////////////////////////////////////////////////////
const std::string format (double value, int width, int precision)
{
std::stringstream s;
s.width (width);
s.precision (precision);
if (0 < value && value < 1)
{
// For value close to zero, width - 2 (2 accounts for the first zero and
// the dot) is the number of digits after zero that are significant
double factor = 1;
for (int i = 2; i < width; i++)
factor *= 10;
value = round (value * factor) / factor;
}
s << value;
return s.str ();
}
////////////////////////////////////////////////////////////////////////////////
const std::string format (double value)
{
std::stringstream s;
s << std::fixed << value;
return s.str ();
}
////////////////////////////////////////////////////////////////////////////////
void replace_positional (
std::string& fmt,
const std::string& from,
const std::string& to)
{
std::string::size_type pos = 0;
while ((pos = fmt.find (from, pos)) != std::string::npos)
{
fmt.replace (pos, from.length (), to);
pos += to.length ();
}
}
////////////////////////////////////////////////////////////////////////////////
std::string leftJustify (const int input, const int width)
{
std::stringstream s;
s << input;
std::string output = s.str ();
return output + std::string (width - output.length (), ' ');
}
////////////////////////////////////////////////////////////////////////////////
std::string leftJustify (const std::string& input, const int width)
{
return input + std::string (width - utf8_text_width (input), ' ');
}
////////////////////////////////////////////////////////////////////////////////
std::string rightJustifyZero (const int input, const int width)
{
std::stringstream s;
s << std::setw (width) << std::setfill ('0') << input;
return s.str ();
}
////////////////////////////////////////////////////////////////////////////////
std::string rightJustify (const int input, const int width)
{
std::stringstream s;
s << std::setw (width) << std::setfill (' ') << input;
return s.str ();
}
////////////////////////////////////////////////////////////////////////////////
std::string rightJustify (const std::string& input, const int width)
{
unsigned int len = utf8_text_width (input);
return (((unsigned int) width > len)
? std::string (width - len, ' ')
: "")
+ input;
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -1,100 +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_TEXT
#define INCLUDED_TEXT
#include <sstream>
#include <algorithm>
#include <string>
#include <vector>
// text.cpp, Non-UTF-8 aware.
void wrapText (std::vector <std::string>&, const std::string&, const int, bool);
int longestWord (const std::string&);
int longestLine (const std::string&);
bool extractLine (std::string&, const std::string&, int, bool, unsigned int&);
std::vector <std::string> split (const std::string&, const char);
const std::string format (std::string&);
const std::string format (const char*);
const std::string formatHex (int);
const std::string format (float, int, int);
const std::string format (double, int, int);
const std::string format (double);
void replace_positional (std::string&, const std::string&, const std::string&);
template<typename T>
const std::string format (T value)
{
std::stringstream s;
s << value;
return s.str ();
}
template<typename T>
const std::string format (int fmt_num, const std::string& fmt, T arg)
{
std::string output = fmt;
replace_positional (output, "{" + format (fmt_num) + "}", format (arg));
return output;
}
template<typename T, typename... Args>
const std::string format (int fmt_num, const std::string& fmt, T arg, Args... args)
{
const std::string fmt_replaced (format (fmt_num, fmt, arg));
return format (fmt_num+1, fmt_replaced, args...);
}
template<typename... Args>
const std::string format (const std::string& fmt, Args... args)
{
return format (1, fmt, args...);
}
std::string leftJustify (const int, const int);
std::string leftJustify (const std::string&, const int);
std::string rightJustifyZero (const int, const int);
std::string rightJustify (const int, const int);
std::string rightJustify (const std::string&, const int);
// List operations.
template <class T> void listDiff (
const T& left, const T& right, T& leftOnly, T& rightOnly)
{
leftOnly.clear ();
for (auto& l : left)
if (std::find (right.begin (), right.end (), l) == right.end ())
leftOnly.push_back (l);
rightOnly.clear ();
for (auto& r : right)
if (std::find (left.begin (), left.end (), r) == left.end ())
rightOnly.push_back (r);
}
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -1,98 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2015 - 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 <unicode.h>
////////////////////////////////////////////////////////////////////////////////
// Complete Unicode whitespace list.
//
// http://en.wikipedia.org/wiki/Whitespace_character
// Updated 2015-09-13
// Static
//
// TODO This list should be derived from the Unicode database.
bool unicodeWhitespace (unsigned int c)
{
return (c == 0x0020 || // space Common Separator, space
c == 0x0009 || // Common Other, control HT, Horizontal Tab
c == 0x000A || // Common Other, control LF, Line feed
c == 0x000B || // Common Other, control VT, Vertical Tab
c == 0x000C || // Common Other, control FF, Form feed
c == 0x000D || // Common Other, control CR, Carriage return
c == 0x0085 || // Common Other, control NEL, Next line
c == 0x00A0 || // no-break space Common Separator, space
c == 0x1680 || // ogham space mark Ogham Separator, space
c == 0x180E || // mongolian vowel separator Mongolian Separator, space
c == 0x2000 || // en quad Common Separator, space
c == 0x2001 || // em quad Common Separator, space
c == 0x2002 || // en space Common Separator, space
c == 0x2003 || // em space Common Separator, space
c == 0x2004 || // three-per-em space Common Separator, space
c == 0x2005 || // four-per-em space Common Separator, space
c == 0x2006 || // six-per-em space Common Separator, space
c == 0x2007 || // figure space Common Separator, space
c == 0x2008 || // punctuation space Common Separator, space
c == 0x2009 || // thin space Common Separator, space
c == 0x200A || // hair space Common Separator, space
c == 0x200B || // zero width space
c == 0x200C || // zero width non-joiner
c == 0x200D || // zero width joiner
c == 0x2028 || // line separator Common Separator, line
c == 0x2029 || // paragraph separator Common Separator, paragraph
c == 0x202F || // narrow no-break space Common Separator, space
c == 0x205F || // medium mathematical space Common Separator, space
c == 0x2060 || // word joiner
c == 0x3000); // ideographic space Common Separator, space
}
////////////////////////////////////////////////////////////////////////////////
// TODO Needs better definition.
bool unicodeLatinAlpha (unsigned int c)
{
return (c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z');
}
////////////////////////////////////////////////////////////////////////////////
// Digits 0-9.
//
// TODO This list should be derived from the Unicode database.
bool unicodeLatinDigit (unsigned int c)
{
return c >= 0x30 && c <= 0x39;
}
////////////////////////////////////////////////////////////////////////////////
// Digits 0-9 a-f A-F.
bool unicodeHexDigit (unsigned int c)
{
return (c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F');
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -1,36 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2015 - 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_UNICODE
#define INCLUDED_UNICODE
bool unicodeWhitespace (unsigned int);
bool unicodeLatinAlpha (unsigned int);
bool unicodeLatinDigit (unsigned int);
bool unicodeHexDigit (unsigned int);
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -1,295 +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>
////////////////////////////////////////////////////////////////////////////////
// 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;
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -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
////////////////////////////////////////////////////////////////////////////////

View file

@ -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
)
);
}