mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-06-26 10:54:26 +02:00
453 lines
11 KiB
C++
453 lines
11 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
// taskwarrior - a command line task list manager.
|
|
//
|
|
// Copyright 2006-2012, 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
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define L10N // Localization complete.
|
|
|
|
#include <text.h>
|
|
#include <i18n.h>
|
|
#include <utf8.h>
|
|
#include <JSON.h>
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
json::value* json::value::parse (Nibbler& nibbler)
|
|
{
|
|
json::value* v;
|
|
if ((v = json::object::parse (nibbler)) ||
|
|
(v = json::array::parse (nibbler)) ||
|
|
(v = json::string::parse (nibbler)) ||
|
|
(v = json::number::parse (nibbler)) ||
|
|
(v = json::literal::parse (nibbler)))
|
|
return v;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
json::jtype json::value::type ()
|
|
{
|
|
return json::j_value;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
std::string json::value::dump ()
|
|
{
|
|
return "<value>";
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
json::string::string (const std::string& other)
|
|
{
|
|
_data = other;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
json::string* json::string::parse (Nibbler& nibbler)
|
|
{
|
|
std::string value;
|
|
if (nibbler.getQuoted ('"', value, false))
|
|
{
|
|
json::string* s = new json::string ();
|
|
s->_data = value;
|
|
return s;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
json::jtype json::string::type ()
|
|
{
|
|
return json::j_string;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
std::string json::string::dump ()
|
|
{
|
|
return std::string ("\"") + _data + "\"";
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
json::number* json::number::parse (Nibbler& nibbler)
|
|
{
|
|
double d;
|
|
if (nibbler.getNumber (d))
|
|
{
|
|
json::number* s = new json::number ();
|
|
s->_dvalue = d;
|
|
return s;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
json::jtype json::number::type ()
|
|
{
|
|
return json::j_number;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
std::string json::number::dump ()
|
|
{
|
|
return format (_dvalue);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
json::number::operator double () const
|
|
{
|
|
return _dvalue;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
json::literal* json::literal::parse (Nibbler& nibbler)
|
|
{
|
|
if (nibbler.getLiteral ("null"))
|
|
{
|
|
json::literal* s = new json::literal ();
|
|
s->_lvalue = nullvalue;
|
|
return s;
|
|
}
|
|
else if (nibbler.getLiteral ("false"))
|
|
{
|
|
json::literal* s = new json::literal ();
|
|
s->_lvalue = falsevalue;
|
|
return s;
|
|
}
|
|
else if (nibbler.getLiteral ("true"))
|
|
{
|
|
json::literal* s = new json::literal ();
|
|
s->_lvalue = truevalue;
|
|
return s;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
json::jtype json::literal::type ()
|
|
{
|
|
return json::j_literal;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
std::string json::literal::dump ()
|
|
{
|
|
if (_lvalue == nullvalue) return "null";
|
|
else if (_lvalue == falsevalue) return "false";
|
|
else return "true";
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
json::array::~array ()
|
|
{
|
|
std::vector <json::value*>::iterator i;
|
|
for (i = _data.begin (); i != _data.end (); ++i)
|
|
delete *i;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
json::array* json::array::parse (Nibbler& nibbler)
|
|
{
|
|
Nibbler n (nibbler);
|
|
n.skipWS ();
|
|
if (n.skip ('['))
|
|
{
|
|
n.skipWS ();
|
|
|
|
json::array* arr = new json::array ();
|
|
|
|
json::value* value;
|
|
if ((value = json::value::parse (n)))
|
|
{
|
|
arr->_data.push_back (value);
|
|
value = NULL; // Not a leak. Looks like a leak.
|
|
n.skipWS ();
|
|
while (n.skip (','))
|
|
{
|
|
n.skipWS ();
|
|
|
|
if ((value = json::value::parse (n)))
|
|
{
|
|
arr->_data.push_back (value);
|
|
n.skipWS ();
|
|
}
|
|
else
|
|
{
|
|
delete arr;
|
|
throw format (STRING_JSON_MISSING_VALUE, (int) n.cursor ());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (n.skip (']'))
|
|
{
|
|
nibbler = n;
|
|
return arr;
|
|
}
|
|
else
|
|
throw format (STRING_JSON_MISSING_BRACKET, (int) n.cursor ());
|
|
|
|
delete arr;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
json::jtype json::array::type ()
|
|
{
|
|
return json::j_array;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
std::string json::array::dump ()
|
|
{
|
|
std::string output;
|
|
output += "[";
|
|
|
|
std::vector <json::value*>::iterator i;
|
|
for (i = _data.begin ();
|
|
i != _data.end ();
|
|
++i)
|
|
{
|
|
if (i != _data.begin ())
|
|
output += ",";
|
|
|
|
output += (*i)->dump ();
|
|
}
|
|
|
|
output += "]";
|
|
return output;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
json::object::~object ()
|
|
{
|
|
std::map <std::string, json::value*>::iterator i;
|
|
for (i = _data.begin (); i != _data.end (); ++i)
|
|
delete i->second;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
json::object* json::object::parse (Nibbler& nibbler)
|
|
{
|
|
Nibbler n (nibbler);
|
|
n.skipWS ();
|
|
if (n.skip ('{'))
|
|
{
|
|
n.skipWS ();
|
|
|
|
json::object* obj = new json::object ();
|
|
|
|
std::string name;
|
|
json::value* value;
|
|
if (json::object::parse_pair (n, name, value))
|
|
{
|
|
obj->_data.insert (std::pair <std::string, json::value*> (name, value));
|
|
value = NULL; // Not a leak. Looks like a leak.
|
|
|
|
n.skipWS ();
|
|
while (n.skip (','))
|
|
{
|
|
n.skipWS ();
|
|
|
|
if (json::object::parse_pair (n, name, value))
|
|
{
|
|
obj->_data.insert (std::pair <std::string, json::value*> (name, value));
|
|
n.skipWS ();
|
|
}
|
|
else
|
|
{
|
|
delete obj;
|
|
throw format (STRING_JSON_MISSING_VALUE, (int) n.cursor ());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (n.skip ('}'))
|
|
{
|
|
nibbler = n;
|
|
return obj;
|
|
}
|
|
else
|
|
throw format (STRING_JSON_MISSING_BRACE, (int) n.cursor ());
|
|
|
|
delete obj;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool json::object::parse_pair (
|
|
Nibbler& nibbler,
|
|
std::string& name,
|
|
json::value*& val)
|
|
{
|
|
Nibbler n (nibbler);
|
|
|
|
if (n.getQuoted ('"', name, false))
|
|
{
|
|
n.skipWS ();
|
|
if (n.skip (':'))
|
|
{
|
|
n.skipWS ();
|
|
if ((val = json::value::parse (n)))
|
|
{
|
|
nibbler = n;
|
|
return true;
|
|
}
|
|
else
|
|
throw format (STRING_JSON_MISSING_VALUE2, (int) n.cursor ());
|
|
}
|
|
else
|
|
throw format (STRING_JSON_MISSING_COLON, (int) n.cursor ());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
json::jtype json::object::type ()
|
|
{
|
|
return json::j_object;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
std::string json::object::dump ()
|
|
{
|
|
std::string output;
|
|
output += "{";
|
|
|
|
std::map <std::string, json::value*>::iterator i;
|
|
for (i = _data.begin (); i != _data.end (); ++i)
|
|
{
|
|
if (i != _data.begin ())
|
|
output += ",";
|
|
|
|
output += "\"" + i->first + "\":";
|
|
output += i->second->dump ();
|
|
}
|
|
|
|
output += "}";
|
|
return output;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
json::value* json::parse (const std::string& input)
|
|
{
|
|
json::value* root = NULL;
|
|
|
|
Nibbler n (input);
|
|
n.skipWS ();
|
|
|
|
if (n.next () == '{') root = json::object::parse (n);
|
|
else if (n.next () == '[') root = json::array::parse (n);
|
|
else
|
|
throw format (STRING_JSON_MISSING_OPEN, (int) n.cursor ());
|
|
|
|
// Check for end condition.
|
|
n.skipWS ();
|
|
if (!n.depleted ())
|
|
{
|
|
delete root;
|
|
throw format (STRING_JSON_EXTRA_CHARACTERS, (int) n.cursor ());
|
|
}
|
|
|
|
return root;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
std::string json::encode (const std::string& input)
|
|
{
|
|
std::string output;
|
|
|
|
for (std::string::size_type i = 0; i < input.length (); ++i)
|
|
{
|
|
switch (input[i])
|
|
{
|
|
// Simple translations.
|
|
case '"': output += "\\\""; break;
|
|
case '\\': output += "\\\\"; break;
|
|
case '/': output += "\\/"; break;
|
|
case '\b': output += "\\b"; break;
|
|
case '\f': output += "\\f"; break;
|
|
case '\n': output += "\\n"; break;
|
|
case '\r': output += "\\r"; break;
|
|
case '\t': output += "\\t"; break;
|
|
|
|
// Default NOP.
|
|
default: output += input[i]; break;
|
|
}
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// TODO Pointers might speed this up.
|
|
std::string json::decode (const std::string& input)
|
|
{
|
|
std::string output;
|
|
for (unsigned int i = 0; i < input.length (); ++i)
|
|
{
|
|
if (input[i] == '\\')
|
|
{
|
|
++i;
|
|
switch (input[i])
|
|
{
|
|
// Simple translations.
|
|
case '"': output += '"'; break;
|
|
case '\\': output += '\\'; break;
|
|
case '/': output += '/'; break;
|
|
case 'b': output += '\b'; break;
|
|
case 'f': output += '\f'; break;
|
|
case 'n': output += '\n'; break;
|
|
case 'r': output += '\r'; break;
|
|
case 't': output += '\t'; break;
|
|
|
|
// Compose a UTF8 unicode character.
|
|
case 'u':
|
|
output += utf8_character (utf8_codepoint (input.substr (++i)));
|
|
i += 3;
|
|
break;
|
|
|
|
// If it is an unrecognized sequence, do nothing.
|
|
default:
|
|
output += '\\';
|
|
output += input[i];
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
output += input[i];
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|