/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see .
*/
/** @file main.cpp
* @author Gav Wood
* @date 2014
* RLP tool.
*/
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace dev;
void help()
{
cout
<< "Usage rlp [OPTIONS] [ | -- ]" << endl
<< "Options:" << endl
<< " -r,--render Render the given RLP. Options:" << endl
<< " --indent Use string as the level indentation (default ' ')." << endl
<< " --hex-ints Render integers in hex." << endl
<< " --ascii-strings Render data as C-style strings or hex depending on content being ASCII." << endl
<< " --force-string Force all data to be rendered as C-style strings." << endl
<< " --force-escape When rendering as C-style strings, force all characters to be escaped." << endl
<< " --force-hex Force all data to be rendered as raw hex." << endl
<< " -l,--list-archive List the items in the RLP list by hash and size." << endl
<< " -e,--extract-archive Extract all items in the RLP list, named by hash." << endl
<< "General options:" << endl
<< " -L,--lenience Try not to bomb out early if possible." << endl
<< " -x,--hex,--base-16 Treat input RLP as hex encoded data." << endl
<< " --64,--base-64 Treat input RLP as base-64 encoded data." << endl
<< " -b,--bin,--base-256 Treat input RLP as raw binary data." << endl
<< " -h,--help Print this help message and exit." << endl
<< " -V,--version Show the version and exit." << endl
;
exit(0);
}
void version()
{
cout << "rlp version " << dev::Version << endl;
exit(0);
}
enum class Mode {
ListArchive,
ExtractArchive,
Render,
};
enum class Encoding {
Auto,
Hex,
Base64,
Binary,
};
bool isAscii(string const& _s)
{
for (char c: _s)
if (c < 32)
return false;
return true;
}
class RLPStreamer
{
public:
struct Prefs
{
string indent = " ";
bool hexInts = false;
bool forceString = true;
bool escapeAll = false;
bool forceHex = false;
};
RLPStreamer(ostream& _out, Prefs _p): m_out(_out), m_prefs(_p) {}
void output(RLP const& _d, unsigned _level = 0)
{
if (_d.isNull())
m_out << "null";
else if (_d.isInt())
if (m_prefs.hexInts)
m_out << toHex(toCompactBigEndian(_d.toInt(RLP::LaisezFaire), 1), 1);
else
m_out << _d.toInt(RLP::LaisezFaire);
else if (_d.isData())
if (m_prefs.forceString || (!m_prefs.forceHex && isAscii(_d.toString())))
m_out << escaped(_d.toString(), m_prefs.escapeAll);
else
m_out << toHex(_d.data());
else if (_d.isList())
{
m_out << "[";
string newline = "\n";
for (unsigned i = 0; i < _level + 1; ++i)
newline += m_prefs.indent;
int j = 0;
for (auto i: _d)
{
m_out << (j++ ?
(m_prefs.indent.empty() ? ", " : ("," + newline)) :
(m_prefs.indent.empty() ? " " : newline));
output(i, _level + 1);
}
newline = newline.substr(0, newline.size() - m_prefs.indent.size());
m_out << (m_prefs.indent.empty() ? (j ? " ]" : "]") : (j ? newline + "]" : "]"));
}
}
private:
std::ostream& m_out;
Prefs m_prefs;
};
int main(int argc, char** argv)
{
Encoding encoding = Encoding::Auto;
Mode mode = Mode::Render;
string inputFile = "--";
bool lenience = false;
RLPStreamer::Prefs prefs;
for (int i = 1; i < argc; ++i)
{
string arg = argv[i];
if (arg == "-h" || arg == "--help")
help();
else if (arg == "-r" || arg == "--render")
mode = Mode::Render;
else if ((arg == "-i" || arg == "--indent") && argc > i)
prefs.indent = argv[++i];
else if (arg == "--hex-ints")
prefs.hexInts = true;
else if (arg == "--ascii-strings")
prefs.forceString = prefs.forceHex = false;
else if (arg == "--force-string")
prefs.forceString = true;
else if (arg == "--force-hex")
prefs.forceHex = true;
else if (arg == "--force-escape")
prefs.escapeAll = true;
else if (arg == "-l" || arg == "--list-archive")
mode = Mode::ListArchive;
else if (arg == "-e" || arg == "--extract-archive")
mode = Mode::ExtractArchive;
else if (arg == "-L" || arg == "--lenience")
lenience = true;
else if (arg == "-V" || arg == "--version")
version();
else if (arg == "-x" || arg == "--hex" || arg == "--base-16")
encoding = Encoding::Hex;
else if (arg == "--64" || arg == "--base-64")
encoding = Encoding::Base64;
else if (arg == "-b" || arg == "--bin" || arg == "--base-256")
encoding = Encoding::Binary;
else
inputFile = arg;
}
bytes in;
if (inputFile == "--")
for (int i = cin.get(); i != -1; i = cin.get())
in.push_back((byte)i);
else
in = contents(inputFile);
if (encoding == Encoding::Auto)
{
encoding = Encoding::Hex;
for (char b: in)
if (b != '\n' && b != ' ' && b != '\t')
{
if (encoding == Encoding::Hex && (b < '0' || b > '9' ) && (b < 'a' || b > 'f' ) && (b < 'A' || b > 'F' ))
{
cerr << "'" << b << "':" << (int)b << endl;
encoding = Encoding::Base64;
}
if (encoding == Encoding::Base64 && (b < '0' || b > '9' ) && (b < 'a' || b > 'z' ) && (b < 'A' || b > 'Z' ) && b != '+' && b != '/')
{
encoding = Encoding::Binary;
break;
}
}
}
bytes b;
switch (encoding)
{
case Encoding::Hex:
{
string s = asString(in);
boost::algorithm::replace_all(s, " ", "");
boost::algorithm::replace_all(s, "\n", "");
boost::algorithm::replace_all(s, "\t", "");
b = fromHex(s);
break;
}
case Encoding::Base64:
{
string s = asString(in);
boost::algorithm::replace_all(s, " ", "");
boost::algorithm::replace_all(s, "\n", "");
boost::algorithm::replace_all(s, "\t", "");
b = fromBase64(s);
break;
}
default:
swap(b, in);
break;
}
try
{
RLP rlp(b);
switch (mode)
{
case Mode::ListArchive:
{
if (!rlp.isList())
{
cout << "Error: Invalid format; RLP data is not a list." << endl;
exit(1);
}
cout << rlp.itemCount() << " items:" << endl;
for (auto i: rlp)
{
if (!i.isData())
{
cout << "Error: Invalid format; RLP list item is not data." << endl;
if (!lenience)
exit(1);
}
cout << " " << i.size() << " bytes: " << sha3(i.data()) << endl;
}
break;
}
case Mode::ExtractArchive:
{
if (!rlp.isList())
{
cout << "Error: Invalid format; RLP data is not a list." << endl;
exit(1);
}
cout << rlp.itemCount() << " items:" << endl;
for (auto i: rlp)
{
if (!i.isData())
{
cout << "Error: Invalid format; RLP list item is not data." << endl;
if (!lenience)
exit(1);
}
ofstream fout;
fout.open(toString(sha3(i.data())));
fout.write(reinterpret_cast(i.data().data()), i.data().size());
}
break;
}
case Mode::Render:
{
RLPStreamer s(cout, prefs);
s.output(rlp);
cout << endl;
break;
}
default:;
}
}
catch (...)
{
cerr << "Error: Invalid format; bad RLP." << endl;
exit(1);
}
return 0;
}