mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-07 20:06:36 +02:00
1279 lines
29 KiB
C++
1279 lines
29 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// Copyright 2006 - 2014, 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 <stdlib.h>
|
||
#include <string.h>
|
||
#include <ctype.h>
|
||
#include <time.h>
|
||
#include <inttypes.h>
|
||
#include <Nibbler.h>
|
||
#ifdef NIBBLER_FEATURE_DATE
|
||
#include <Date.h>
|
||
#endif
|
||
#ifdef NIBBLER_FEATURE_REGEX
|
||
#include <RX.h>
|
||
#endif
|
||
|
||
static const char* _uuid_pattern = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
|
||
static const unsigned int _uuid_min_length = 14;
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
Nibbler::Nibbler ()
|
||
: _input ("")
|
||
, _length (0)
|
||
, _cursor (0)
|
||
, _saved (0)
|
||
{
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
Nibbler::Nibbler (const std::string& input)
|
||
: _input (input)
|
||
, _length (input.length ())
|
||
, _cursor (0)
|
||
{
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
Nibbler::Nibbler (const Nibbler& other)
|
||
: _input (other._input)
|
||
, _length (other._length)
|
||
, _cursor (other._cursor)
|
||
{
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
Nibbler& Nibbler::operator= (const Nibbler& other)
|
||
{
|
||
if (this != &other)
|
||
{
|
||
_input = other._input;
|
||
_length = other._length;
|
||
_cursor = other._cursor;
|
||
}
|
||
|
||
return *this;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
Nibbler::~Nibbler ()
|
||
{
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// Extract up until the next c (but not including) or EOS.
|
||
bool Nibbler::getUntil (char c, std::string& result)
|
||
{
|
||
if (_cursor < _length)
|
||
{
|
||
std::string::size_type i = _input.find (c, _cursor);
|
||
if (i != std::string::npos)
|
||
{
|
||
result = _input.substr (_cursor, i - _cursor);
|
||
_cursor = i;
|
||
}
|
||
else
|
||
{
|
||
result = _input.substr (_cursor);
|
||
_cursor = _length;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::getUntil (const std::string& terminator, std::string& result)
|
||
{
|
||
if (_cursor < _length)
|
||
{
|
||
std::string::size_type i = _input.find (terminator, _cursor);
|
||
if (i != std::string::npos)
|
||
{
|
||
result = _input.substr (_cursor, i - _cursor);
|
||
_cursor = i;
|
||
}
|
||
else
|
||
{
|
||
result = _input.substr (_cursor);
|
||
_cursor = _length;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
#ifdef NIBBLER_FEATURE_REGEX
|
||
bool Nibbler::getUntilRx (const std::string& regex, std::string& result)
|
||
{
|
||
if (_cursor < _length)
|
||
{
|
||
RX r (regex, true);
|
||
std::vector <int> start;
|
||
std::vector <int> end;
|
||
if (r.match (start, end, _input.substr (_cursor)))
|
||
{
|
||
result = _input.substr (_cursor, start[0]);
|
||
_cursor += start[0];
|
||
}
|
||
else
|
||
{
|
||
result = _input.substr (_cursor);
|
||
_cursor = _length;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
#endif
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::getUntilOneOf (const std::string& chars, std::string& result)
|
||
{
|
||
if (_cursor < _length)
|
||
{
|
||
std::string::size_type i = _input.find_first_of (chars, _cursor);
|
||
if (i != std::string::npos)
|
||
{
|
||
result = _input.substr (_cursor, i - _cursor);
|
||
_cursor = i;
|
||
}
|
||
else
|
||
{
|
||
result = _input.substr (_cursor);
|
||
_cursor = _length;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::getUntilWS (std::string& result)
|
||
{
|
||
return this->getUntilOneOf (" \t\r\n\f", result);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::getUntilEOL (std::string& result)
|
||
{
|
||
return getUntil ('\n', result);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::getUntilEOS (std::string& result)
|
||
{
|
||
if (_cursor < _length)
|
||
{
|
||
result = _input.substr (_cursor);
|
||
_cursor = _length;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::getN (const int quantity, std::string& result)
|
||
{
|
||
if (_cursor + quantity <= _length)
|
||
{
|
||
result = _input.substr (_cursor, quantity);
|
||
_cursor += quantity;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::getQuoted (
|
||
char c,
|
||
std::string& result,
|
||
bool quote /* = false */)
|
||
{
|
||
bool inquote = false;
|
||
bool inescape = false;
|
||
char previous = 0;
|
||
char current = 0;
|
||
result = "";
|
||
|
||
if (_cursor >= _length ||
|
||
_input[_cursor] != c)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
for (std::string::size_type i = _cursor; i < _length; ++i)
|
||
{
|
||
current = _input[i];
|
||
|
||
if (current == '\\' && !inescape)
|
||
{
|
||
inescape = true;
|
||
previous = current;
|
||
continue;
|
||
}
|
||
|
||
if (current == c && !inescape)
|
||
{
|
||
if (quote)
|
||
result += current;
|
||
|
||
if (!inquote)
|
||
{
|
||
inquote = true;
|
||
}
|
||
else
|
||
{
|
||
_cursor = i + 1;
|
||
return true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (previous)
|
||
{
|
||
result += previous;
|
||
previous = 0;
|
||
}
|
||
|
||
result += current;
|
||
inescape = false;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::getDigit (int& result)
|
||
{
|
||
if (_cursor < _length &&
|
||
isdigit (_input[_cursor]))
|
||
{
|
||
result = _input[_cursor++] - '0';
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::getDigit6 (int& result)
|
||
{
|
||
std::string::size_type i = _cursor;
|
||
if (i < _length &&
|
||
_length - i >= 6)
|
||
{
|
||
if (isdigit (_input[i + 0]) &&
|
||
isdigit (_input[i + 1]) &&
|
||
isdigit (_input[i + 2]) &&
|
||
isdigit (_input[i + 3]) &&
|
||
isdigit (_input[i + 4]) &&
|
||
isdigit (_input[i + 5]))
|
||
{
|
||
result = strtoimax (_input.substr (_cursor, 6).c_str (), NULL, 10);
|
||
_cursor += 6;
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::getDigit4 (int& result)
|
||
{
|
||
std::string::size_type i = _cursor;
|
||
if (i < _length &&
|
||
_length - i >= 4)
|
||
{
|
||
if (isdigit (_input[i + 0]) &&
|
||
isdigit (_input[i + 1]) &&
|
||
isdigit (_input[i + 2]) &&
|
||
isdigit (_input[i + 3]))
|
||
{
|
||
result = strtoimax (_input.substr (_cursor, 4).c_str (), NULL, 10);
|
||
_cursor += 4;
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::getDigit3 (int& result)
|
||
{
|
||
std::string::size_type i = _cursor;
|
||
if (i < _length &&
|
||
_length - i >= 3)
|
||
{
|
||
if (isdigit (_input[i + 0]) &&
|
||
isdigit (_input[i + 1]) &&
|
||
isdigit (_input[i + 2]))
|
||
{
|
||
result = strtoimax (_input.substr (_cursor, 3).c_str (), NULL, 10);
|
||
_cursor += 3;
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::getDigit2 (int& result)
|
||
{
|
||
std::string::size_type i = _cursor;
|
||
if (i < _length &&
|
||
_length - i >= 2)
|
||
{
|
||
if (isdigit (_input[i + 0]) &&
|
||
isdigit (_input[i + 1]))
|
||
{
|
||
result = strtoimax (_input.substr (_cursor, 2).c_str (), NULL, 10);
|
||
_cursor += 2;
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::getInt (int& result)
|
||
{
|
||
std::string::size_type i = _cursor;
|
||
|
||
if (i < _length)
|
||
{
|
||
if (_input[i] == '-')
|
||
++i;
|
||
else if (_input[i] == '+')
|
||
++i;
|
||
}
|
||
|
||
// TODO Potential for use of find_first_not_of
|
||
while (i < _length && isdigit (_input[i]))
|
||
++i;
|
||
|
||
if (i > _cursor)
|
||
{
|
||
result = strtoimax (_input.substr (_cursor, i - _cursor).c_str (), NULL, 10);
|
||
_cursor = i;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::getHex (int& result)
|
||
{
|
||
std::string::size_type i = _cursor;
|
||
|
||
if (i < _length)
|
||
{
|
||
if (_input[i] == '-')
|
||
++i;
|
||
else if (_input[i] == '+')
|
||
++i;
|
||
}
|
||
|
||
// TODO Potential for use of find_first_not_of
|
||
while (i < _length && isxdigit (_input[i]))
|
||
++i;
|
||
|
||
if (i > _cursor)
|
||
{
|
||
result = strtoimax (_input.substr (_cursor, i - _cursor).c_str (), NULL, 16);
|
||
_cursor = i;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::getUnsignedInt (int& result)
|
||
{
|
||
std::string::size_type i = _cursor;
|
||
// TODO Potential for use of find_first_not_of
|
||
while (i < _length && isdigit (_input[i]))
|
||
++i;
|
||
|
||
if (i > _cursor)
|
||
{
|
||
result = strtoimax (_input.substr (_cursor, i - _cursor).c_str (), NULL, 10);
|
||
_cursor = i;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// number:
|
||
// int frac? exp?
|
||
//
|
||
// int:
|
||
// (-|+)? digit+
|
||
//
|
||
// frac:
|
||
// . digit+
|
||
//
|
||
// exp:
|
||
// e digit+
|
||
//
|
||
// e:
|
||
// e|E (+|-)?
|
||
//
|
||
bool Nibbler::getNumber (std::string& result)
|
||
{
|
||
std::string::size_type i = _cursor;
|
||
|
||
// [+-]?
|
||
if (i < _length && (_input[i] == '-' || _input[i] == '+'))
|
||
++i;
|
||
|
||
// digit+
|
||
if (i < _length && isdigit (_input[i]))
|
||
{
|
||
++i;
|
||
|
||
while (i < _length && isdigit (_input[i]))
|
||
++i;
|
||
|
||
// ( . digit+ )?
|
||
if (i < _length && _input[i] == '.')
|
||
{
|
||
++i;
|
||
|
||
while (i < _length && isdigit (_input[i]))
|
||
++i;
|
||
}
|
||
|
||
// ( [eE] [+-]? digit+ )?
|
||
if (i < _length && (_input[i] == 'e' || _input[i] == 'E'))
|
||
{
|
||
++i;
|
||
|
||
if (i < _length && (_input[i] == '+' || _input[i] == '-'))
|
||
++i;
|
||
|
||
if (i < _length && isdigit (_input[i]))
|
||
{
|
||
++i;
|
||
|
||
while (i < _length && isdigit (_input[i]))
|
||
++i;
|
||
|
||
result = _input.substr (_cursor, i - _cursor);
|
||
_cursor = i;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
result = _input.substr (_cursor, i - _cursor);
|
||
_cursor = i;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::getNumber (double &result)
|
||
{
|
||
bool isnumber;
|
||
std::string s;
|
||
|
||
isnumber = getNumber (s);
|
||
if (isnumber)
|
||
{
|
||
result = strtof (s.c_str (), NULL);
|
||
}
|
||
return isnumber;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// number:
|
||
// int frac? exp?
|
||
//
|
||
// int:
|
||
// digit+
|
||
//
|
||
// frac:
|
||
// . digit+
|
||
//
|
||
// exp:
|
||
// e digit+
|
||
//
|
||
// e:
|
||
// e|E (+|-)?
|
||
//
|
||
bool Nibbler::getUnsignedNumber (double& result)
|
||
{
|
||
std::string::size_type i = _cursor;
|
||
|
||
// digit+
|
||
if (i < _length && isdigit (_input[i]))
|
||
{
|
||
++i;
|
||
|
||
while (i < _length && isdigit (_input[i]))
|
||
++i;
|
||
|
||
// ( . digit+ )?
|
||
if (i < _length && _input[i] == '.')
|
||
{
|
||
++i;
|
||
|
||
while (i < _length && isdigit (_input[i]))
|
||
++i;
|
||
}
|
||
|
||
// ( [eE] [+-]? digit+ )?
|
||
if (i < _length && (_input[i] == 'e' || _input[i] == 'E'))
|
||
{
|
||
++i;
|
||
|
||
if (i < _length && (_input[i] == '+' || _input[i] == '-'))
|
||
++i;
|
||
|
||
if (i < _length && isdigit (_input[i]))
|
||
{
|
||
++i;
|
||
|
||
while (i < _length && isdigit (_input[i]))
|
||
++i;
|
||
|
||
result = strtof (_input.substr (_cursor, i - _cursor).c_str (), NULL);
|
||
_cursor = i;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
result = strtof (_input.substr (_cursor, i - _cursor).c_str (), NULL);
|
||
_cursor = i;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::getLiteral (const std::string& literal)
|
||
{
|
||
if (_cursor < _length &&
|
||
_input.find (literal, _cursor) == _cursor)
|
||
{
|
||
_cursor += literal.length ();
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
#ifdef NIBBLER_FEATURE_REGEX
|
||
bool Nibbler::getRx (const std::string& regex, std::string& result)
|
||
{
|
||
if (_cursor < _length)
|
||
{
|
||
// Regex may be anchored to the beginning and include capturing parentheses,
|
||
// otherwise they are added.
|
||
std::string modified_regex;
|
||
if (regex.substr (0, 2) != "^(")
|
||
modified_regex = "^(" + regex + ")";
|
||
else
|
||
modified_regex = regex;
|
||
|
||
RX r (modified_regex, true);
|
||
std::vector <std::string> results;
|
||
if (r.match (results, _input.substr (_cursor)))
|
||
{
|
||
result = results[0];
|
||
_cursor += result.length ();
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
#endif
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::getUUID (std::string& result)
|
||
{
|
||
std::string::size_type i = _cursor;
|
||
|
||
if (i < _length &&
|
||
_length - i >= 36)
|
||
{
|
||
// 88888888-4444-4444-4444-cccccccccccc
|
||
if (isxdigit (_input[i + 0]) &&
|
||
isxdigit (_input[i + 1]) &&
|
||
isxdigit (_input[i + 2]) &&
|
||
isxdigit (_input[i + 3]) &&
|
||
isxdigit (_input[i + 4]) &&
|
||
isxdigit (_input[i + 5]) &&
|
||
isxdigit (_input[i + 6]) &&
|
||
isxdigit (_input[i + 7]) &&
|
||
_input[i + 8] == '-' &&
|
||
isxdigit (_input[i + 9]) &&
|
||
isxdigit (_input[i + 10]) &&
|
||
isxdigit (_input[i + 11]) &&
|
||
isxdigit (_input[i + 12]) &&
|
||
_input[i + 13] == '-' &&
|
||
isxdigit (_input[i + 14]) &&
|
||
isxdigit (_input[i + 15]) &&
|
||
isxdigit (_input[i + 16]) &&
|
||
isxdigit (_input[i + 17]) &&
|
||
_input[i + 18] == '-' &&
|
||
isxdigit (_input[i + 19]) &&
|
||
isxdigit (_input[i + 20]) &&
|
||
isxdigit (_input[i + 21]) &&
|
||
isxdigit (_input[i + 22]) &&
|
||
_input[i + 23] == '-' &&
|
||
isxdigit (_input[i + 24]) &&
|
||
isxdigit (_input[i + 25]) &&
|
||
isxdigit (_input[i + 26]) &&
|
||
isxdigit (_input[i + 27]) &&
|
||
isxdigit (_input[i + 28]) &&
|
||
isxdigit (_input[i + 29]) &&
|
||
isxdigit (_input[i + 30]) &&
|
||
isxdigit (_input[i + 31]) &&
|
||
isxdigit (_input[i + 32]) &&
|
||
isxdigit (_input[i + 33]) &&
|
||
isxdigit (_input[i + 34]) &&
|
||
isxdigit (_input[i + 35]))
|
||
{
|
||
result = _input.substr (_cursor, 36);
|
||
_cursor = i + 36;
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::getPartialUUID (std::string& result)
|
||
{
|
||
std::string::size_type i;
|
||
for (i = 0; i < 36 && i < (_length - _cursor); i++)
|
||
{
|
||
if (_uuid_pattern[i] == 'x' && !isxdigit (_input[_cursor + i]))
|
||
break;
|
||
|
||
else if (_uuid_pattern[i] == '-' && _input[_cursor + i] != '-')
|
||
break;
|
||
}
|
||
|
||
// If the partial match found is long enough, consider it a match.
|
||
if (i >= _uuid_min_length)
|
||
{
|
||
// Fail if there is another hex digit.
|
||
if (_cursor + i < _length &&
|
||
isxdigit (_input[_cursor + i]))
|
||
return false;
|
||
|
||
result = _input.substr (_cursor, i);
|
||
_cursor += i;
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// 19980119T070000Z = YYYYMMDDThhmmssZ
|
||
bool Nibbler::getDateISO (time_t& t)
|
||
{
|
||
std::string::size_type i = _cursor;
|
||
|
||
if (i < _length &&
|
||
_length - i >= 16)
|
||
{
|
||
if (isdigit (_input[i + 0]) &&
|
||
isdigit (_input[i + 1]) &&
|
||
isdigit (_input[i + 2]) &&
|
||
isdigit (_input[i + 3]) &&
|
||
isdigit (_input[i + 4]) &&
|
||
isdigit (_input[i + 5]) &&
|
||
isdigit (_input[i + 6]) &&
|
||
isdigit (_input[i + 7]) &&
|
||
_input[i + 8] == 'T' &&
|
||
isdigit (_input[i + 9]) &&
|
||
isdigit (_input[i + 10]) &&
|
||
isdigit (_input[i + 11]) &&
|
||
isdigit (_input[i + 12]) &&
|
||
isdigit (_input[i + 13]) &&
|
||
isdigit (_input[i + 14]) &&
|
||
_input[i + 15] == 'Z')
|
||
{
|
||
_cursor += 16;
|
||
|
||
int year = (_input[i + 0] - '0') * 1000
|
||
+ (_input[i + 1] - '0') * 100
|
||
+ (_input[i + 2] - '0') * 10
|
||
+ (_input[i + 3] - '0');
|
||
|
||
int month = (_input[i + 4] - '0') * 10
|
||
+ (_input[i + 5] - '0');
|
||
|
||
int day = (_input[i + 6] - '0') * 10
|
||
+ (_input[i + 7] - '0');
|
||
|
||
int hour = (_input[i + 9] - '0') * 10
|
||
+ (_input[i + 10] - '0');
|
||
|
||
int minute = (_input[i + 11] - '0') * 10
|
||
+ (_input[i + 12] - '0');
|
||
|
||
int second = (_input[i + 13] - '0') * 10
|
||
+ (_input[i + 14] - '0');
|
||
|
||
// Convert to epoch.
|
||
struct tm tms = {0};
|
||
tms.tm_isdst = -1; // Requests that mktime determine summer time effect.
|
||
tms.tm_mon = month - 1;
|
||
tms.tm_mday = day;
|
||
tms.tm_year = year - 1900;
|
||
tms.tm_hour = hour;
|
||
tms.tm_min = minute;
|
||
tms.tm_sec = second;
|
||
#ifdef HAVE_TM_GMTOFF
|
||
tms.tm_gmtoff = 0;
|
||
#endif
|
||
|
||
t = timegm (&tms);
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// Parse the longest integer using the next 'limit' characters of 'result'
|
||
// following position 'i' (when strict is true, the number of digits must be
|
||
// equal to limit).
|
||
bool Nibbler::parseDigits(std::string::size_type& i,
|
||
int& result,
|
||
unsigned int limit,
|
||
bool strict /* = true */)
|
||
{
|
||
// If the result has already been set
|
||
if (result != -1)
|
||
return false;
|
||
for (unsigned int f = limit; f > 0; --f)
|
||
{
|
||
// Check that the nibbler has enough unparsed characters
|
||
if (i + f <= _length)
|
||
{
|
||
// Check that 'f' of them are digits
|
||
unsigned int g;
|
||
for (g = 0; g < f; g++)
|
||
if (! isdigit (_input[i + g]))
|
||
break;
|
||
// Parse the integer when it is the case
|
||
if (g == f)
|
||
{
|
||
if (f == 1)
|
||
result = _input[i] - '0';
|
||
else
|
||
result = atoi (_input.substr (i, f).c_str ());
|
||
// Update the global cursor before returning
|
||
i += f;
|
||
return true;
|
||
}
|
||
}
|
||
// Do not try smaller limits if the option is strict on the size
|
||
if (strict)
|
||
break;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
#ifdef NIBBLER_FEATURE_DATE
|
||
bool Nibbler::getDate (const std::string& format, time_t& t)
|
||
{
|
||
std::string::size_type i = _cursor;
|
||
|
||
int month = -1; // So we can check later.
|
||
int day = -1;
|
||
int year = -1;
|
||
int hour = -1;
|
||
int minute = -1;
|
||
int second = -1;
|
||
|
||
// For parsing, unused.
|
||
int wday = -1;
|
||
int week = -1;
|
||
|
||
for (unsigned int f = 0; f < format.length (); ++f)
|
||
{
|
||
switch (format[f])
|
||
{
|
||
case 'm':
|
||
case 'M':
|
||
if (! parseDigits(i, month, 2, format[f] == 'M'))
|
||
return false;
|
||
break;
|
||
|
||
case 'd':
|
||
case 'D':
|
||
if (! parseDigits(i, day, 2, format[f] == 'D'))
|
||
return false;
|
||
break;
|
||
|
||
case 'y':
|
||
case 'Y':
|
||
if (! parseDigits(i, year, format[f] == 'y' ? 2 : 4))
|
||
return false;
|
||
if (format[f] == 'y')
|
||
year += 2000;
|
||
break;
|
||
|
||
case 'h':
|
||
case 'H':
|
||
if (! parseDigits(i, hour, 2, format[f] == 'H'))
|
||
return false;
|
||
break;
|
||
|
||
case 'n':
|
||
case 'N':
|
||
if (! parseDigits(i, minute, 2, format[f] == 'N'))
|
||
return false;
|
||
break;
|
||
|
||
case 's':
|
||
case 'S':
|
||
if (! parseDigits(i, second, 2, format[f] == 'S'))
|
||
return false;
|
||
break;
|
||
|
||
// Merely parse, not extract.
|
||
case 'v':
|
||
case 'V':
|
||
if (! parseDigits(i, week, 2, format[f] == 'V'))
|
||
return false;
|
||
break;
|
||
|
||
// Merely parse, not extract.
|
||
case 'a':
|
||
case 'A':
|
||
if (i + 3 <= _length &&
|
||
! isdigit (_input[i + 0]) &&
|
||
! isdigit (_input[i + 1]) &&
|
||
! isdigit (_input[i + 2]))
|
||
{
|
||
wday = Date::dayOfWeek (_input.substr (i, 3).c_str ());
|
||
i += (format[f] == 'a') ? 3 : Date::dayName (wday).size ();
|
||
}
|
||
else
|
||
return false;
|
||
break;
|
||
|
||
case 'b':
|
||
case 'B':
|
||
if (i + 3 <= _length &&
|
||
! isdigit (_input[i + 0]) &&
|
||
! isdigit (_input[i + 1]) &&
|
||
! isdigit (_input[i + 2]))
|
||
{
|
||
if (month != -1)
|
||
return false;
|
||
month = Date::monthOfYear (_input.substr (i, 3).c_str());
|
||
i += (format[f] == 'b') ? 3 : Date::monthName (month).size ();
|
||
}
|
||
else
|
||
return false;
|
||
break;
|
||
|
||
default:
|
||
if (i + 1 <= _length &&
|
||
_input[i] == format[f])
|
||
++i;
|
||
else
|
||
return false;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// By default, the most global date variables that are undefined are put to
|
||
// the current date (for instance, the year to the current year for formats
|
||
// that lack Y/y). If even 'second' is undefined, then the date is parsed as
|
||
// now.
|
||
if (year == -1)
|
||
{
|
||
Date now = Date ();
|
||
year = now.year ();
|
||
if (month == -1)
|
||
{
|
||
month = now.month ();
|
||
if (day == -1)
|
||
{
|
||
day = now.day ();
|
||
if (hour == -1)
|
||
{
|
||
hour = now.hour ();
|
||
if (minute == -1)
|
||
{
|
||
minute = now.minute ();
|
||
if (second == -1)
|
||
second = now.second ();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Put all remaining undefined date variables to their default values (0 or
|
||
// 1).
|
||
month = (month == -1) ? 1 : month;
|
||
day = (day == -1) ? 1 : day;
|
||
hour = (hour == -1) ? 0 : hour;
|
||
minute = (minute == -1) ? 0 : minute;
|
||
second = (second == -1) ? 0 : second;
|
||
|
||
// Check that values are correct
|
||
if (! Date::valid (month, day, year, hour, minute, second))
|
||
return false;
|
||
|
||
// Convert to epoch.
|
||
struct tm tms = {0};
|
||
tms.tm_isdst = -1; // Requests that mktime determine summer time effect.
|
||
tms.tm_mon = month - 1;
|
||
tms.tm_mday = day;
|
||
tms.tm_year = year - 1900;
|
||
tms.tm_hour = hour;
|
||
tms.tm_min = minute;
|
||
tms.tm_sec = second;
|
||
|
||
t = mktime (&tms);
|
||
_cursor = i;
|
||
return true;
|
||
}
|
||
#endif
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// Assumes that the options are sorted by decreasing length, so that if the
|
||
// options contain 'fourteen' and 'four', the stream is first matched against
|
||
// the longer entry.
|
||
bool Nibbler::getOneOf (
|
||
const std::vector <std::string>& options,
|
||
std::string& found)
|
||
{
|
||
std::vector <std::string>::const_iterator option;
|
||
for (option = options.begin (); option != options.end (); ++option)
|
||
{
|
||
if (getLiteral (*option))
|
||
{
|
||
found = *option;
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// A name is a string of alpha-numeric characters.
|
||
bool Nibbler::getName (std::string& result)
|
||
{
|
||
std::string::size_type i = _cursor;
|
||
|
||
if (i < _length)
|
||
{
|
||
if (! isdigit (_input[i]) &&
|
||
! ispunct (_input[i]) &&
|
||
! isspace (_input[i]))
|
||
{
|
||
++i;
|
||
while (i < _length &&
|
||
! ispunct (_input[i]) &&
|
||
! isspace (_input[i]))
|
||
{
|
||
++i;
|
||
}
|
||
}
|
||
|
||
if (i > _cursor)
|
||
{
|
||
result = _input.substr (_cursor, i - _cursor);
|
||
_cursor = i;
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// A word is a contiguous string of non-space, non-digit, non-punct characters.
|
||
bool Nibbler::getWord (std::string& result)
|
||
{
|
||
std::string::size_type i = _cursor;
|
||
|
||
if (i < _length)
|
||
{
|
||
while (!isdigit (_input[i]) &&
|
||
!isPunctuation (_input[i]) &&
|
||
!isspace (_input[i]))
|
||
{
|
||
++i;
|
||
}
|
||
|
||
if (i > _cursor)
|
||
{
|
||
result = _input.substr (_cursor, i - _cursor);
|
||
_cursor = i;
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::skipN (const int quantity /* = 1 */)
|
||
{
|
||
if (_cursor < _length &&
|
||
_cursor <= _length - quantity)
|
||
{
|
||
_cursor += quantity;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::skip (char c)
|
||
{
|
||
if (_cursor < _length &&
|
||
_input[_cursor] == c)
|
||
{
|
||
++_cursor;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::skipAll (char c)
|
||
{
|
||
if (_cursor < _length)
|
||
{
|
||
std::string::size_type i = _input.find_first_not_of (c, _cursor);
|
||
if (i == _cursor)
|
||
return false;
|
||
|
||
if (i == std::string::npos)
|
||
_cursor = _length; // Yes, off the end.
|
||
else
|
||
_cursor = i;
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::skipWS ()
|
||
{
|
||
return this->skipAllOneOf (" \t\n\r\f");
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
#ifdef NIBBLER_FEATURE_REGEX
|
||
bool Nibbler::skipRx (const std::string& regex)
|
||
{
|
||
if (_cursor < _length)
|
||
{
|
||
// Regex may be anchored to the beginning and include capturing parentheses,
|
||
// otherwise they are added.
|
||
std::string modified_regex;
|
||
if (regex.substr (0, 2) != "^(")
|
||
modified_regex = "^(" + regex + ")";
|
||
else
|
||
modified_regex = regex;
|
||
|
||
RX r (modified_regex, true);
|
||
std::vector <std::string> results;
|
||
if (r.match (results, _input.substr (_cursor)))
|
||
{
|
||
_cursor += results[0].length ();
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
#endif
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::backN (const int quantity /*= 1*/)
|
||
{
|
||
if (_cursor >= (unsigned) quantity)
|
||
{
|
||
_cursor -= (unsigned) quantity;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
void Nibbler::getRemainder (std::string& result)
|
||
{
|
||
if (_cursor < _length)
|
||
result = _input.substr (_cursor);
|
||
}
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::skipAllOneOf (const std::string& chars)
|
||
{
|
||
if (_cursor < _length)
|
||
{
|
||
std::string::size_type i = _input.find_first_not_of (chars, _cursor);
|
||
if (i == _cursor)
|
||
return false;
|
||
|
||
if (i == std::string::npos)
|
||
_cursor = _length; // Yes, off the end.
|
||
else
|
||
_cursor = i;
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// Peeks ahead - does not move cursor.
|
||
char Nibbler::next ()
|
||
{
|
||
if (_cursor < _length)
|
||
return _input[_cursor];
|
||
|
||
return '\0';
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
std::string::size_type Nibbler::cursor ()
|
||
{
|
||
return _cursor;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// Peeks ahead - does not move cursor.
|
||
std::string Nibbler::next (const int quantity)
|
||
{
|
||
if ( _cursor < _length &&
|
||
(unsigned) quantity <= _length &&
|
||
_cursor <= _length - quantity)
|
||
return _input.substr (_cursor, quantity);
|
||
|
||
return "";
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
std::string::size_type Nibbler::save ()
|
||
{
|
||
return _saved = _cursor;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
std::string::size_type Nibbler::restore ()
|
||
{
|
||
return _cursor = _saved;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
const std::string& Nibbler::str () const
|
||
{
|
||
return _input;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
bool Nibbler::depleted ()
|
||
{
|
||
if (_cursor >= _length)
|
||
return true;
|
||
|
||
return false;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// Override of ispunct, that considers #, $ and @ not to be punctuation.
|
||
//
|
||
// ispunct: ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
|
||
// Punctuation: ! " % & ' ( ) * + , - . / : ; < = > ? [ \ ] ^ _ ` { | } ~
|
||
// delta: # $ @
|
||
//
|
||
bool Nibbler::isPunctuation (char c)
|
||
{
|
||
if (c == '@' || c == '#' || c == '$')
|
||
return false;
|
||
|
||
return ispunct (c);
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
std::string Nibbler::dump ()
|
||
{
|
||
return std::string ("Nibbler ‹")
|
||
+ _input.substr (_cursor)
|
||
+ "›";
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|