mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-07 20:06:36 +02:00
379 lines
9.4 KiB
C++
379 lines
9.4 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
// SOFTWARE.
|
|
//
|
|
// http://www.opensource.org/licenses/mit-license.php
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <cmake.h>
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <strings.h>
|
|
#include <Context.h>
|
|
#include <Lexer.h>
|
|
#include <math.h>
|
|
#include <util.h>
|
|
#include <text.h>
|
|
#include <utf8.h>
|
|
#include <i18n.h>
|
|
|
|
extern Context context;
|
|
|
|
static const char* newline = "\n";
|
|
static const char* noline = "";
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
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);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void split (
|
|
std::set<std::string>& results,
|
|
const std::string& input,
|
|
const char delimiter)
|
|
{
|
|
results.clear ();
|
|
std::string::size_type start = 0;
|
|
std::string::size_type i;
|
|
while ((i = input.find (delimiter, start)) != std::string::npos)
|
|
{
|
|
results.insert (input.substr (start, i - start));
|
|
start = i + 1;
|
|
}
|
|
|
|
if (input.length ())
|
|
results.insert (input.substr (start));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void split (
|
|
std::vector<std::string>& results,
|
|
const std::string& input,
|
|
const char delimiter)
|
|
{
|
|
results.clear ();
|
|
std::string::size_type start = 0;
|
|
std::string::size_type i;
|
|
while ((i = input.find (delimiter, start)) != std::string::npos)
|
|
{
|
|
results.push_back (input.substr (start, i - start));
|
|
start = i + 1;
|
|
}
|
|
|
|
if (input.length ())
|
|
results.push_back (input.substr (start));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void split (
|
|
std::vector<std::string>& results,
|
|
const std::string& input,
|
|
const std::string& delimiter)
|
|
{
|
|
results.clear ();
|
|
std::string::size_type length = delimiter.length ();
|
|
|
|
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 + length;
|
|
}
|
|
|
|
if (input.length ())
|
|
results.push_back (input.substr (start));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void join (
|
|
std::string& result,
|
|
const std::string& separator,
|
|
const std::vector<std::string>& items)
|
|
{
|
|
std::stringstream s;
|
|
unsigned int size = items.size ();
|
|
for (unsigned int i = 0; i < size; ++i)
|
|
{
|
|
s << items[i];
|
|
if (i < size - 1)
|
|
s << separator;
|
|
}
|
|
|
|
result = s.str ();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void join (
|
|
std::string& result,
|
|
const std::string& separator,
|
|
const std::vector<int>& items)
|
|
{
|
|
std::stringstream s;
|
|
unsigned int size = items.size ();
|
|
for (unsigned int i = 0; i < size; ++i)
|
|
{
|
|
s << items[i];
|
|
if (i < size - 1)
|
|
s << separator;
|
|
}
|
|
|
|
result = s.str ();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Remove enclosing balanced quotes. Assumes trimmed text.
|
|
std::string unquoteText (const std::string& input)
|
|
{
|
|
std::string output = input;
|
|
|
|
if (output.length () > 1)
|
|
{
|
|
char quote = output[0];
|
|
if ((quote == '\'' || quote == '"') &&
|
|
output[output.length () - 1] == quote)
|
|
return output.substr (1, output.length () - 2);
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
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;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Break UTF8 text into chunks no more than width characters.
|
|
bool extractLine (
|
|
std::string& line,
|
|
const std::string& text,
|
|
int width,
|
|
bool hyphenate,
|
|
unsigned int& offset)
|
|
{
|
|
// Terminate processing.
|
|
if (offset >= text.length ())
|
|
return false;
|
|
|
|
int line_length {0};
|
|
int character {0};
|
|
std::string::size_type lastWordEnd {std::string::npos};
|
|
bool something {false};
|
|
std::string::size_type cursor {offset};
|
|
std::string::size_type prior_cursor {offset};
|
|
while ((character = utf8_next_char (text, cursor)))
|
|
{
|
|
// Premature EOL.
|
|
if (character == '\n')
|
|
{
|
|
line = text.substr (offset, prior_cursor - offset);
|
|
offset = cursor;
|
|
return true;
|
|
}
|
|
|
|
if (! Lexer::isWhitespace (character))
|
|
{
|
|
something = true;
|
|
if (! text[cursor] || Lexer::isWhitespace (text[cursor]))
|
|
lastWordEnd = prior_cursor;
|
|
}
|
|
|
|
line_length += mk_wcwidth (character);
|
|
|
|
if (line_length >= width)
|
|
{
|
|
// Backtrack to previous word end.
|
|
if (lastWordEnd != std::string::npos)
|
|
{
|
|
// Eat one WS after lastWordEnd.
|
|
std::string::size_type lastBreak = lastWordEnd;
|
|
utf8_next_char (text, lastBreak);
|
|
|
|
// Position offset at following char.
|
|
std::string::size_type nextStart = lastBreak;
|
|
utf8_next_char (text, nextStart);
|
|
|
|
line = text.substr (offset, lastBreak - offset);
|
|
offset = nextStart;
|
|
return true;
|
|
}
|
|
|
|
// No backtrack, possible hyphenation.
|
|
else if (hyphenate)
|
|
{
|
|
line = text.substr (offset, prior_cursor - offset) + '-';
|
|
offset = prior_cursor;
|
|
return true;
|
|
}
|
|
|
|
// No hyphenation, just truncation.
|
|
else
|
|
{
|
|
line = text.substr (offset, cursor - offset);
|
|
offset = cursor;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Hindsight.
|
|
prior_cursor = cursor;
|
|
}
|
|
|
|
// Residual text.
|
|
if (something)
|
|
{
|
|
line = text.substr (offset, cursor - offset);
|
|
offset = cursor;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
const char* optionalBlankLine ()
|
|
{
|
|
return context.verbose ("blank") ? newline : noline;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool nontrivial (const std::string& input)
|
|
{
|
|
std::string::size_type i = 0;
|
|
int character;
|
|
while ((character = utf8_next_char (input, i)))
|
|
if (! Lexer::isWhitespace (character))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Return the length, in characters, of the input, subtracting color control
|
|
// codes.
|
|
int strippedLength (const std::string& input)
|
|
{
|
|
int length = input.length ();
|
|
bool inside = false;
|
|
int count = 0;
|
|
for (int i = 0; i < length; ++i)
|
|
{
|
|
if (inside)
|
|
{
|
|
if (input[i] == 'm')
|
|
inside = false;
|
|
}
|
|
else
|
|
{
|
|
if (input[i] == 033)
|
|
inside = true;
|
|
else
|
|
++count;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
const std::string obfuscateText (const std::string& input)
|
|
{
|
|
std::stringstream output;
|
|
std::string::size_type i = 0;
|
|
int character;
|
|
bool inside = false;
|
|
|
|
while ((character = utf8_next_char (input, i)))
|
|
{
|
|
if (inside)
|
|
{
|
|
output << (char) character;
|
|
|
|
if (character == 'm')
|
|
inside = false;
|
|
}
|
|
else
|
|
{
|
|
if (character == 033)
|
|
inside = true;
|
|
|
|
if (inside || character == ' ')
|
|
output << (char) character;
|
|
else
|
|
output << 'x';
|
|
}
|
|
}
|
|
|
|
return output.str ();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
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 ();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|