33 #include <google/protobuf/descriptor.h>
34 #include <google/protobuf/text_format.h>
35 #include <google/protobuf/descriptor.pb.h>
37 #include <boost/algorithm/string.hpp>
39 #include "dccl/codec.h"
40 #include "dccl/cli_option.h"
41 #include "dccl/binary.h"
43 #include "dccl_tool.pb.h"
44 #include "dccl/version.h"
51 enum Action { NO_ACTION, ENCODE, DECODE, ANALYZE, DISP_PROTO };
52 enum Format { BINARY, TEXTFORMAT, HEX, BASE64 };
64 id_codec(dccl::Codec::default_id_codec_name()),
70 std::set<std::string> include;
71 std::vector<std::string> dlopen;
72 std::set<std::string> message;
73 std::set<std::string> proto_file;
89 void load_desc(
dccl::Codec*
dccl,
const google::protobuf::Descriptor* desc,
const std::string& name);
93 int main(
int argc,
char* argv[])
97 parse_options(argc, argv, &cfg);
100 dccl::dlog.
connect(dccl::logger::WARN_PLUS, &std::cerr);
102 dccl::dlog.
connect(dccl::logger::DEBUG1_PLUS, &std::cerr);
106 for(std::set<std::string>::const_iterator it = cfg.include.begin(),
107 end = cfg.include.end(); it != end; ++it)
112 std::string first_dl;
113 if(cfg.dlopen.size())
114 first_dl = cfg.dlopen[0];
118 if(cfg.dlopen.size() > 1)
120 for(std::vector<std::string>::iterator it = cfg.dlopen.begin()+1,
121 n = cfg.dlopen.end(); it != n; ++it)
122 dccl.load_library(*it);
124 bool no_messages_specified = cfg.message.empty();
125 for(std::set<std::string>::const_iterator it = cfg.proto_file.begin(),
126 end = cfg.proto_file.end(); it != end; ++it)
128 const google::protobuf::FileDescriptor* file_desc =
133 std::cerr <<
"failed to read in: " << *it << std::endl;
138 if(no_messages_specified)
140 for(
int i = 0, n = file_desc->message_type_count(); i < n; ++i)
142 cfg.message.insert(file_desc->message_type(i)->full_name());
143 if(i == 0 && cfg.action == ENCODE)
145 std::cerr <<
"Encoding assuming message: " << file_desc->message_type(i)->full_name() << std::endl;
153 for(std::set<std::string>::const_iterator it = cfg.message.begin(),
154 end = cfg.message.end(); it != end; ++it)
156 const google::protobuf::Descriptor* desc =
158 load_desc(&
dccl, desc, *it);
163 case ENCODE: encode(
dccl, cfg);
break;
164 case DECODE: decode(
dccl, cfg);
break;
165 case ANALYZE: analyze(
dccl, cfg);
break;
166 case DISP_PROTO: disp_proto(
dccl, cfg);
break;
168 std::cerr <<
"No action specified (e.g. analyze, decode, encode). Try --help." << std::endl;
179 dccl.info_all(&std::cout);
184 if(cfg.message.size() > 1)
186 std::cerr <<
"No more than one DCCL message can be specified with -m or --message for encoding." << std::endl;
189 else if(cfg.message.size() == 0)
191 std::cerr <<
"You must specify a DCCL message to encode with -m" << std::endl;
195 std::string command_line_name = *cfg.message.begin();
197 while(!std::cin.eof())
200 std::getline (std::cin, input);
209 std::string::size_type close_bracket_pos = input.find(
'|', 1);
210 if(close_bracket_pos == std::string::npos)
212 std::cerr <<
"Incorrectly formatted input: expected '|'" << std::endl;
216 name = input.substr(1, close_bracket_pos-1);
217 if(cfg.message.find(name) == cfg.message.end())
219 const google::protobuf::Descriptor* desc =
221 load_desc(&
dccl, desc, name);
222 cfg.message.insert(name);
226 if(input.size() > close_bracket_pos+1)
227 input = input.substr(close_bracket_pos+1);
233 if(cfg.message.size() == 0)
235 std::cerr <<
"Message name not given with -m or in the input (i.e. '[Name] field1: value field2: value')." << std::endl;
239 name = command_line_name;
245 std::cerr <<
"No descriptor with name " << name <<
" found! Make sure you have loaded all the necessary .proto files and/or shared libraries. Also make sure you specified the fully qualified name including the package, if any (e.g. 'goby.acomms.protobuf.NetworkAck', not just 'NetworkAck')." << std::endl;
251 google::protobuf::TextFormat::ParseFromString(input, msg.get());
253 if(msg->IsInitialized())
256 dccl.encode(&encoded, *msg);
262 std::ofstream fout(
"/dev/stdout", std::ios::binary | std::ios::app);
263 fout.write(encoded.data(), encoded.size());
270 dccl::tool::protobuf::ByteString s;
273 google::protobuf::TextFormat::PrintFieldValueToString(s, s.GetDescriptor()->FindFieldByNumber(1), -1, &output);
275 std::cout << output << std::endl;
284 std::stringstream instream(encoded);
285 std::stringstream outstream;
287 D.encode(instream, outstream);
288 std::cout << outstream.str();
290 std::cerr <<
"dccl was not compiled with libb64-dev, so no Base64 functionality is available." << std::endl;
303 if(cfg.format == BINARY)
305 std::ifstream fin(
"/dev/stdin", std::ios::binary);
306 std::ostringstream ostrm;
307 ostrm << fin.rdbuf();
312 while(!std::cin.eof())
315 std::getline (std::cin, line);
317 if(boost::trim_copy(line).empty())
328 boost::trim_if(line, boost::is_any_of(
"\""));
331 dccl::tool::protobuf::ByteString s;
332 google::protobuf::TextFormat::ParseFieldValueFromString(
"\"" + line +
"\"", s.GetDescriptor()->FindFieldByNumber(1), &s);
341 std::stringstream instream(line);
342 std::stringstream outstream;
344 D.decode(instream, outstream);
345 input += outstream.str();
348 std::cerr <<
"dccl was not compiled with libb64-dev, so no Base64 functionality is available." << std::endl;
355 while(!input.empty())
357 boost::shared_ptr<google::protobuf::Message> msg =
dccl.decode<boost::shared_ptr<google::protobuf::Message> >(&input);
359 std::cout <<
"|" << msg->GetDescriptor()->full_name() <<
"| ";
360 std::cout << msg->ShortDebugString() << std::endl;
366 std::cout <<
"Please note that for Google Protobuf versions < 2.5.0, the dccl extensions will not be show below, so you'll need to refer to the original .proto file." << std::endl;
367 for(std::set<std::string>::const_iterator it = cfg.message.begin(),
368 end = cfg.message.end(); it != end; ++it)
370 const google::protobuf::Descriptor* desc =
373 std::cout << desc->DebugString();
379 void load_desc(
dccl::Codec*
dccl,
const google::protobuf::Descriptor* desc,
const std::string& name)
383 try {
dccl->load(desc); }
384 catch(std::exception& e)
386 std::cerr <<
"Not a valid DCCL message: " << desc->full_name() <<
"\nWhy: " << e.what() << std::endl;
391 std::cerr <<
"No descriptor with name " << name <<
" found! Make sure you have loaded all the necessary .proto files and/or shared libraries. Try --help." << std::endl;
398 std::vector<dccl::Option> options;
399 options.push_back(
dccl::Option(
'e',
"encode", no_argument,
"Encode a DCCL message to STDOUT from STDIN"));
400 options.push_back(
dccl::Option(
'd',
"decode", no_argument,
"Decode a DCCL message to STDOUT from STDIN"));
401 options.push_back(
dccl::Option(
'a',
"analyze", no_argument,
"Provides information on a given DCCL message definition (e.g. field sizes)"));
402 options.push_back(
dccl::Option(
'p',
"display_proto", no_argument,
"Display the .proto definition of this message."));
403 options.push_back(
dccl::Option(
'h',
"help", no_argument,
"Gives help on the usage of 'dccl'"));
404 options.push_back(
dccl::Option(
'I',
"proto_path", required_argument,
"Add another search directory for .proto files"));
405 options.push_back(
dccl::Option(
'l',
"dlopen", required_argument,
"Open this shared library containing compiled DCCL messages."));
406 options.push_back(
dccl::Option(
'm',
"message", required_argument,
"Message name to encode, decode or analyze."));
407 options.push_back(
dccl::Option(
'f',
"proto_file", required_argument,
".proto file to load."));
408 options.push_back(
dccl::Option(0,
"format", required_argument,
"Format for encode output or decode input: 'bin' (default) is raw binary, 'hex' is ascii-encoded hexadecimal, 'textformat' is a Google Protobuf TextFormat byte string, 'base64' is ascii-encoded base 64."));
409 options.push_back(
dccl::Option(
'v',
"verbose", no_argument,
"Display extra debugging information."));
410 options.push_back(
dccl::Option(
'o',
"omit_prefix", no_argument,
"Omit the DCCL type name prefix from the output of decode."));
411 options.push_back(
dccl::Option(
'i',
"id_codec", required_argument,
"(Advanced) name for a nonstandard DCCL ID codec to use"));
412 options.push_back(
dccl::Option(
'V',
"version", no_argument,
"DCCL Version"));
414 std::vector<option> long_options;
415 std::string opt_string;
419 int option_index = 0;
421 int c = getopt_long(argc, argv, opt_string.c_str(),
422 &long_options[0], &option_index);
429 if (long_options[option_index].flag != 0)
432 if(!strcmp(long_options[option_index].name,
"format"))
434 if(!strcmp(optarg,
"textformat"))
435 cfg->format = TEXTFORMAT;
436 else if(!strcmp(optarg,
"hex"))
438 else if(!strcmp(optarg,
"base64"))
439 cfg->format = BASE64;
440 else if(!strcmp(optarg,
"bin"))
441 cfg->format = BINARY;
444 std::cerr <<
"Invalid format '" << optarg <<
"'" << std::endl;
450 std::cerr <<
"Try --help for valid options." << std::endl;
456 case 'e': cfg->action = ENCODE;
break;
457 case 'd': cfg->action = DECODE;
break;
458 case 'a': cfg->action = ANALYZE;
break;
459 case 'p': cfg->action = DISP_PROTO;
break;
460 case 'I': cfg->include.insert(optarg);
break;
461 case 'l': cfg->dlopen.push_back(optarg);
break;
462 case 'm': cfg->message.insert(optarg);
break;
465 char* proto_file_canonical_path = realpath(optarg, 0);
466 if(proto_file_canonical_path)
468 cfg->proto_file.insert(proto_file_canonical_path);
469 free(proto_file_canonical_path);
473 std::cerr <<
"Invalid proto file path: '" << optarg <<
"'" << std::endl;
478 case 'i': cfg->id_codec = optarg;
break;
479 case 'v': cfg->verbose =
true;
break;
480 case 'o': cfg->omit_prefix =
true;
break;
483 std::cout <<
"Usage of the Dynamic Compact Control Language (DCCL) tool ('dccl'): " << std::endl;
484 for(
int i = 0, n = options.size(); i < n; ++i)
485 std::cout <<
" " << options[i].usage() << std::endl;
490 std::cout << dccl::VERSION_STRING << std::endl;
496 std::cerr <<
"Try --help for valid options." << std::endl;
498 default: exit(EXIT_FAILURE);
505 std::cerr <<
"Unknown arguments: \n";
506 while (optind < argc)
507 std::cerr << argv[optind++];
508 std::cerr << std::endl;
509 std::cerr <<
"Try --help for valid options." << std::endl;