c@116: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
c@118: /*
c@118:     Piper C++
c@118: 
c@118:     An API for audio analysis and feature extraction plugins.
c@118: 
c@118:     Centre for Digital Music, Queen Mary, University of London.
c@118:     Copyright 2006-2016 Chris Cannam and QMUL.
c@118:   
c@118:     Permission is hereby granted, free of charge, to any person
c@118:     obtaining a copy of this software and associated documentation
c@118:     files (the "Software"), to deal in the Software without
c@118:     restriction, including without limitation the rights to use, copy,
c@118:     modify, merge, publish, distribute, sublicense, and/or sell copies
c@118:     of the Software, and to permit persons to whom the Software is
c@118:     furnished to do so, subject to the following conditions:
c@118: 
c@118:     The above copyright notice and this permission notice shall be
c@118:     included in all copies or substantial portions of the Software.
c@118: 
c@118:     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
c@118:     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
c@118:     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
c@118:     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
c@118:     ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
c@118:     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
c@118:     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
c@118: 
c@118:     Except as contained in this notice, the names of the Centre for
c@118:     Digital Music; Queen Mary, University of London; and Chris Cannam
c@118:     shall not be used in advertising or otherwise to promote the sale,
c@118:     use or other dealings in this Software without prior written
c@118:     authorization.
c@118: */
c@75: 
c@75: #include "vamp-json/VampJson.h"
c@75: #include "vamp-capnp/VampnProto.h"
c@75: #include "vamp-support/RequestOrResponse.h"
c@75: #include "vamp-support/PreservingPluginHandleMapper.h"
c@75: 
c@75: #include <iostream>
c@75: #include <sstream>
c@75: #include <stdexcept>
c@75: 
c@91: #include <capnp/serialize.h>
c@91: 
c@123: // for _setmode stuff
c@123: #ifdef _WIN32
c@123: #include <io.h>
c@123: #include <fcntl.h>
c@123: #endif
c@123: 
c@75: using namespace std;
c@75: using namespace json11;
c@97: using namespace piper_vamp;
c@75: 
c@75: void usage()
c@75: {
c@75:     string myname = "piper-convert";
c@75:     cerr << "\n" << myname <<
c@116:         ": Validate and convert Piper request and response messages\n\n"
c@116:         "    Usage: " << myname << " [-i <informat>] [-o <outformat>] request\n"
c@116:         "           " << myname << " [-i <informat>] [-o <outformat>] response\n\n"
c@116:         "    where\n"
c@116:         "       <informat>: the format to read from stdin\n"
c@116:         "           (\"json\" or \"capnp\", default is \"json\")\n"
c@116:         "       <outformat>: the format to convert to and write to stdout\n"
c@116:         "           (\"json\", \"json-b64\" or \"capnp\", default is \"json\")\n"
c@116:         "       request|response: whether messages are Vamp request or response type\n\n"
c@116:         "If <informat> and <outformat> differ, convert from <informat> to <outformat>.\n"
c@116:         "If <informat> and <outformat> are the same, just check validity of incoming\n"
c@116:         "messages and pass them to output.\n\n"
c@116:         "Specifying \"json-b64\" as output format forces base64 encoding for process and\n"
c@116:         "feature blocks, unlike the \"json\" output format which uses text encoding.\n"
c@116:         "The \"json\" input format accepts either.\n\n";
c@75: 
c@75:     exit(2);
c@75: }
c@75: 
c@75: Json
c@75: convertRequestJson(string input, string &err)
c@75: {
c@75:     Json j = Json::parse(input, err);
c@75:     if (err != "") {
c@116:         err = "invalid json: " + err;
c@116:         return {};
c@75:     }
c@75:     if (!j.is_object()) {
c@116:         err = "object expected at top level";
c@75:     } else if (!j["method"].is_string()) {
c@116:         err = "string expected for method field";
c@75:     } else if (!j["params"].is_null() && !j["params"].is_object()) {
c@116:         err = "object expected for params field";
c@75:     }
c@75:     return j;
c@75: }
c@75: 
c@75: Json
c@75: convertResponseJson(string input, string &err)
c@75: {
c@75:     Json j = Json::parse(input, err);
c@75:     if (err != "") {
c@116:         err = "invalid json: " + err;
c@116:         return {};
c@75:     }
c@75:     if (!j.is_object()) {
c@116:         err = "object expected at top level";
c@75:     } else {
c@75:         if (!j["result"].is_object()) {
c@75:             if (!j["error"].is_object()) {
c@75:                 err = "expected either result or error object";
c@75:             }
c@75:         }
c@75:     }
c@75:     return j;
c@75: }
c@75: 
c@75: //!!! Lots of potential for refactoring the conversion classes based
c@75: //!!! on the common matter in the following eight functions...
c@75: 
c@75: PreservingPluginHandleMapper mapper;
c@75: 
c@75: static RequestOrResponse::RpcId
c@75: readJsonId(const Json &j)
c@75: {
c@75:     RequestOrResponse::RpcId id;
c@75: 
c@75:     if (j["id"].is_number()) {
c@75:         id.type = RequestOrResponse::RpcId::Number;
c@75:         id.number = j["id"].number_value();
c@75:     } else if (j["id"].is_string()) {
c@75:         id.type = RequestOrResponse::RpcId::Tag;
c@75:         id.tag = j["id"].string_value();
c@75:     } else {
c@75:         id.type = RequestOrResponse::RpcId::Absent;
c@75:     }
c@75: 
c@75:     return id;
c@75: }
c@75: 
c@75: static Json
c@75: writeJsonId(const RequestOrResponse::RpcId &id)
c@75: {
c@75:     if (id.type == RequestOrResponse::RpcId::Number) {
c@75:         return id.number;
c@75:     } else if (id.type == RequestOrResponse::RpcId::Tag) {
c@75:         return id.tag;
c@75:     } else {
c@75:         return Json();
c@75:     }
c@75: }
c@75: 
c@75: template <typename Reader>
c@75: static RequestOrResponse::RpcId
c@75: readCapnpId(const Reader &r)
c@75: {
c@75:     int number;
c@75:     string tag;
c@75:     switch (r.getId().which()) {
c@97:     case piper::RpcRequest::Id::Which::NUMBER:
c@75:         number = r.getId().getNumber();
c@75:         return { RequestOrResponse::RpcId::Number, number, "" };
c@97:     case piper::RpcRequest::Id::Which::TAG:
c@75:         tag = r.getId().getTag();
c@75:         return { RequestOrResponse::RpcId::Tag, 0, tag };
c@97:     case piper::RpcRequest::Id::Which::NONE:
c@75:         return { RequestOrResponse::RpcId::Absent, 0, "" };
c@75:     }
cannam@153:     return { RequestOrResponse::RpcId::Absent, 0, "" };
c@75: }
c@75: 
c@75: template <typename Builder>
c@75: static void
c@75: buildCapnpId(Builder &b, const RequestOrResponse::RpcId &id)
c@75: {
c@75:     switch (id.type) {
c@75:     case RequestOrResponse::RpcId::Number:
c@75:         b.getId().setNumber(id.number);
c@75:         break;
c@75:     case RequestOrResponse::RpcId::Tag:
c@75:         b.getId().setTag(id.tag);
c@75:         break;
c@75:     case RequestOrResponse::RpcId::Absent:
c@75:         b.getId().setNone();
c@75:         break;
c@75:     }
c@75: }
c@75: 
c@75: RequestOrResponse
cannam@158: readRequestJson(string &err, bool &eof)
c@75: {
c@75:     RequestOrResponse rr;
c@75:     rr.direction = RequestOrResponse::Request;
c@75: 
c@75:     string input;
c@75:     if (!getline(cin, input)) {
c@116:         // the EOF case, not actually an error
cannam@158:         eof = true;
c@116:         return rr;
c@75:     }
c@75:     
c@75:     Json j = convertRequestJson(input, err);
c@75:     if (err != "") return {};
c@75: 
c@75:     rr.type = VampJson::getRequestResponseType(j, err);
c@75:     if (err != "") return {};
c@75: 
c@75:     rr.id = readJsonId(j);
c@75: 
c@75:     VampJson::BufferSerialisation serialisation =
c@75:         VampJson::BufferSerialisation::Array;
c@75: 
c@75:     switch (rr.type) {
c@75: 
c@75:     case RRType::List:
c@130:         rr.listRequest = VampJson::toRpcRequest_List(j, err);
c@116:         break;
c@75:     case RRType::Load:
c@116:         rr.loadRequest = VampJson::toRpcRequest_Load(j, err);
c@116:         break;
c@75:     case RRType::Configure:
c@116:         rr.configurationRequest = VampJson::toRpcRequest_Configure(j, mapper, err);
c@116:         break;
c@75:     case RRType::Process:
c@116:         rr.processRequest = VampJson::toRpcRequest_Process(j, mapper, serialisation, err);
c@116:         break;
c@75:     case RRType::Finish:
c@116:         rr.finishRequest = VampJson::toRpcRequest_Finish(j, mapper, err);
c@116:         break;
c@75:     case RRType::NotValid:
c@116:         break;
c@75:     }
c@75: 
c@75:     return rr;
c@75: }
c@75: 
c@75: void
c@75: writeRequestJson(RequestOrResponse &rr, bool useBase64)
c@75: {
c@75:     Json j;
c@75: 
c@75:     VampJson::BufferSerialisation serialisation =
c@75:         (useBase64 ?
c@75:          VampJson::BufferSerialisation::Base64 :
c@75:          VampJson::BufferSerialisation::Array);
c@75: 
c@75:     Json id = writeJsonId(rr.id);
c@75:     
c@75:     switch (rr.type) {
c@75: 
c@75:     case RRType::List:
c@127:         j = VampJson::fromRpcRequest_List(rr.listRequest, id);
c@116:         break;
c@75:     case RRType::Load:
c@116:         j = VampJson::fromRpcRequest_Load(rr.loadRequest, id);
c@116:         break;
c@75:     case RRType::Configure:
c@116:         j = VampJson::fromRpcRequest_Configure(rr.configurationRequest, mapper, id);
c@116:         break;
c@75:     case RRType::Process:
c@116:         j = VampJson::fromRpcRequest_Process
c@116:             (rr.processRequest, mapper, serialisation, id);
c@116:         break;
c@75:     case RRType::Finish:
c@116:         j = VampJson::fromRpcRequest_Finish(rr.finishRequest, mapper, id);
c@116:         break;
c@75:     case RRType::NotValid:
c@116:         break;
c@75:     }
c@75: 
c@75:     cout << j.dump() << endl;
c@75: }
c@75: 
c@75: RequestOrResponse
cannam@158: readResponseJson(string &err, bool &eof)
c@75: {
c@75:     RequestOrResponse rr;
c@75:     rr.direction = RequestOrResponse::Response;
c@75: 
c@75:     string input;
c@75:     if (!getline(cin, input)) {
c@116:         // the EOF case, not actually an error
cannam@158:         eof = true;
c@116:         return rr;
c@75:     }
c@75: 
c@75:     Json j = convertResponseJson(input, err);
c@75:     if (err != "") return {};
c@75: 
c@75:     rr.type = VampJson::getRequestResponseType(j, err);
c@75:     if (err != "") return {};
c@75: 
c@75:     rr.id = readJsonId(j);
c@75:     
c@75:     VampJson::BufferSerialisation serialisation =
c@75:         VampJson::BufferSerialisation::Array;
c@75: 
dev@174:     const bool isSuccess = j["result"].is_object();
dev@174:     const bool isError = j["error"].is_object();
dev@174:     rr.success = isSuccess;
dev@174:     rr.errorText = isError ? j["error"]["message"].string_value() : "";
c@75: 
c@75:     switch (rr.type) {
c@75: 
c@75:     case RRType::List:
c@116:         rr.listResponse = VampJson::toRpcResponse_List(j, err);
c@116:         break;
c@75:     case RRType::Load:
c@116:         rr.loadResponse = VampJson::toRpcResponse_Load(j, mapper, err);
c@116:         break;
c@75:     case RRType::Configure:
c@116:         rr.configurationResponse = VampJson::toRpcResponse_Configure(j, mapper, err);
c@116:         break;
c@75:     case RRType::Process: 
c@116:         rr.processResponse = VampJson::toRpcResponse_Process(j, mapper, serialisation, err);
c@116:         break;
c@75:     case RRType::Finish:
c@116:         rr.finishResponse = VampJson::toRpcResponse_Finish(j, mapper, serialisation, err);
c@116:         break;
c@75:     case RRType::NotValid:
c@116:         break;
c@75:     }
c@75: 
c@75:     return rr;
c@75: }
c@75: 
c@75: void
c@75: writeResponseJson(RequestOrResponse &rr, bool useBase64)
c@75: {
c@75:     Json j;
c@75: 
c@75:     VampJson::BufferSerialisation serialisation =
c@75:         (useBase64 ?
c@75:          VampJson::BufferSerialisation::Base64 :
c@75:          VampJson::BufferSerialisation::Array);
c@75: 
c@75:     Json id = writeJsonId(rr.id);
c@75: 
c@75:     if (!rr.success) {
dev@176:          // errorText here likely contains a full message produced by simple-server
dev@176:          // setting writeVerbatimError to true avoids doubling error descriptions 
dev@176:         j = VampJson::fromError(rr.errorText, rr.type, id, true);
c@75: 
c@75:     } else {
c@116:         switch (rr.type) {
c@75: 
c@116:         case RRType::List:
c@116:             j = VampJson::fromRpcResponse_List(rr.listResponse, id);
c@116:             break;
c@116:         case RRType::Load:
c@116:             j = VampJson::fromRpcResponse_Load(rr.loadResponse, mapper, id);
c@116:             break;
c@116:         case RRType::Configure:
c@116:             j = VampJson::fromRpcResponse_Configure(rr.configurationResponse,
c@75:                                                     mapper, id);
c@116:             break;
c@116:         case RRType::Process:
c@116:             j = VampJson::fromRpcResponse_Process
c@116:                 (rr.processResponse, mapper, serialisation, id);
c@116:             break;
c@116:         case RRType::Finish:
c@116:             j = VampJson::fromRpcResponse_Finish
c@116:                 (rr.finishResponse, mapper, serialisation, id);
c@116:             break;
c@116:         case RRType::NotValid:
cannam@158:             j = VampJson::fromError(rr.errorText, rr.type, id);
c@116:             break;
c@116:         }
c@75:     }
c@75:     
c@75:     cout << j.dump() << endl;
c@75: }
c@75: 
c@75: RequestOrResponse
c@75: readRequestCapnp(kj::BufferedInputStreamWrapper &buffered)
c@75: {
c@75:     RequestOrResponse rr;
c@75:     rr.direction = RequestOrResponse::Request;
c@75: 
c@97:     capnp::InputStreamMessageReader message(buffered);
c@97:     piper::RpcRequest::Reader reader = message.getRoot<piper::RpcRequest>();
c@75:     
c@75:     rr.type = VampnProto::getRequestResponseType(reader);
c@75:     rr.id = readCapnpId(reader);
c@75: 
c@75:     switch (rr.type) {
c@75: 
c@75:     case RRType::List:
c@127:         VampnProto::readRpcRequest_List(rr.listRequest, reader);
c@116:         break;
c@75:     case RRType::Load:
c@116:         VampnProto::readRpcRequest_Load(rr.loadRequest, reader);
c@116:         break;
c@75:     case RRType::Configure:
c@116:         VampnProto::readRpcRequest_Configure(rr.configurationRequest,
c@116:                                              reader, mapper);
c@116:         break;
c@75:     case RRType::Process:
c@116:         VampnProto::readRpcRequest_Process(rr.processRequest, reader, mapper);
c@116:         break;
c@75:     case RRType::Finish:
c@116:         VampnProto::readRpcRequest_Finish(rr.finishRequest, reader, mapper);
c@116:         break;
c@75:     case RRType::NotValid:
c@116:         break;
c@75:     }
c@75: 
c@75:     return rr;
c@75: }
c@75: 
c@75: void
c@75: writeRequestCapnp(RequestOrResponse &rr)
c@75: {
c@97:     capnp::MallocMessageBuilder message;
c@97:     piper::RpcRequest::Builder builder = message.initRoot<piper::RpcRequest>();
c@75: 
c@75:     buildCapnpId(builder, rr.id);
c@75:     
c@75:     switch (rr.type) {
c@75: 
c@75:     case RRType::List:
c@127:         VampnProto::buildRpcRequest_List(builder, rr.listRequest);
c@116:         break;
c@75:     case RRType::Load:
c@116:         VampnProto::buildRpcRequest_Load(builder, rr.loadRequest);
c@116:         break;
c@75:     case RRType::Configure:
c@116:         VampnProto::buildRpcRequest_Configure(builder,
c@75:                                               rr.configurationRequest, mapper);
c@116:         break;
c@75:     case RRType::Process:
c@116:         VampnProto::buildRpcRequest_Process(builder, rr.processRequest, mapper);
c@116:         break;
c@75:     case RRType::Finish:
c@116:         VampnProto::buildRpcRequest_Finish(builder, rr.finishRequest, mapper);
c@116:         break;
c@75:     case RRType::NotValid:
c@116:         break;
c@75:     }
c@75: 
c@75:     writeMessageToFd(1, message);
c@75: }
c@75: 
c@75: RequestOrResponse
c@75: readResponseCapnp(kj::BufferedInputStreamWrapper &buffered)
c@75: {
c@75:     RequestOrResponse rr;
c@75:     rr.direction = RequestOrResponse::Response;
c@75: 
c@97:     capnp::InputStreamMessageReader message(buffered);
c@97:     piper::RpcResponse::Reader reader = message.getRoot<piper::RpcResponse>();
c@75:     
c@75:     rr.type = VampnProto::getRequestResponseType(reader);
c@75:     rr.success = true;
c@75:     rr.errorText = "";
c@75:     rr.id = readCapnpId(reader);
c@75:     int errorCode = 0;
c@75: 
c@75:     switch (rr.type) {
c@75: 
c@75:     case RRType::List:
c@116:         VampnProto::readRpcResponse_List(rr.listResponse, reader);
c@116:         break;
c@75:     case RRType::Load:
c@116:         VampnProto::readRpcResponse_Load(rr.loadResponse, reader, mapper);
c@116:         break;
c@75:     case RRType::Configure:
c@116:         VampnProto::readRpcResponse_Configure(rr.configurationResponse,
c@116:                                               reader, mapper);
c@116:         break;
c@75:     case RRType::Process:
c@116:         VampnProto::readRpcResponse_Process(rr.processResponse, reader, mapper);
c@116:         break;
c@75:     case RRType::Finish:
c@116:         VampnProto::readRpcResponse_Finish(rr.finishResponse, reader, mapper);
c@116:         break;
c@75:     case RRType::NotValid:
c@75:         VampnProto::readRpcResponse_Error(errorCode, rr.errorText, reader);
c@116:         break;
c@75:     }
c@75: 
c@75:     return rr;
c@75: }
c@75: 
c@75: void
c@75: writeResponseCapnp(RequestOrResponse &rr)
c@75: {
c@97:     capnp::MallocMessageBuilder message;
c@97:     piper::RpcResponse::Builder builder = message.initRoot<piper::RpcResponse>();
c@75: 
c@75:     buildCapnpId(builder, rr.id);
c@75: 
c@75:     if (!rr.success) {
c@75: 
c@116:         VampnProto::buildRpcResponse_Error(builder, rr.errorText, rr.type);
c@75: 
c@75:     } else {
c@116:         
c@116:         switch (rr.type) {
c@75: 
c@116:         case RRType::List:
c@116:             VampnProto::buildRpcResponse_List(builder, rr.listResponse);
c@116:             break;
c@116:         case RRType::Load:
c@116:             VampnProto::buildRpcResponse_Load(builder, rr.loadResponse, mapper);
c@116:             break;
c@116:         case RRType::Configure:
c@116:             VampnProto::buildRpcResponse_Configure(builder, rr.configurationResponse, mapper);
c@116:             break;
c@116:         case RRType::Process:
c@116:             VampnProto::buildRpcResponse_Process(builder, rr.processResponse, mapper);
c@116:             break;
c@116:         case RRType::Finish:
c@116:             VampnProto::buildRpcResponse_Finish(builder, rr.finishResponse, mapper);
c@116:             break;
c@116:         case RRType::NotValid:
cannam@158:             VampnProto::buildRpcResponse_Error(builder, rr.errorText, rr.type);
c@116:             break;
c@116:         }
c@75:     }
c@75:     
c@75:     writeMessageToFd(1, message);
c@75: }
c@75: 
c@75: RequestOrResponse
cannam@158: readInputJson(RequestOrResponse::Direction direction, string &err, bool &eof)
c@75: {
c@75:     if (direction == RequestOrResponse::Request) {
cannam@158:         return readRequestJson(err, eof);
c@75:     } else {
cannam@158:         return readResponseJson(err, eof);
c@75:     }
c@75: }
c@75: 
c@75: RequestOrResponse
cannam@158: readInputCapnp(RequestOrResponse::Direction direction, bool &eof)
c@75: {
c@75:     static kj::FdInputStream stream(0); // stdin
c@75:     static kj::BufferedInputStreamWrapper buffered(stream);
c@75: 
c@75:     if (buffered.tryGetReadBuffer() == nullptr) {
cannam@158:         eof = true;
c@116:         return {};
c@75:     }
c@75:     
c@75:     if (direction == RequestOrResponse::Request) {
c@116:         return readRequestCapnp(buffered);
c@75:     } else {
c@116:         return readResponseCapnp(buffered);
c@75:     }
c@75: }
c@75: 
c@75: RequestOrResponse
cannam@158: readInput(string format, RequestOrResponse::Direction direction, bool &eof)
c@75: {
cannam@158:     eof = false;
cannam@158:     
c@75:     if (format == "json") {
c@116:         string err;
cannam@158:         auto result = readInputJson(direction, err, eof);
dev@180:         if (err != "") throw runtime_error(err);
dev@180:         else return result;
c@75:     } else if (format == "capnp") {
cannam@158:         return readInputCapnp(direction, eof);
c@75:     } else {
c@116:         throw runtime_error("unknown input format \"" + format + "\"");
c@75:     }
c@75: }
c@75: 
c@75: void
c@75: writeOutput(string format, RequestOrResponse &rr)
c@75: {
c@75:     if (format == "json") {
c@116:         if (rr.direction == RequestOrResponse::Request) {
c@116:             writeRequestJson(rr, false);
c@116:         } else {
c@116:             writeResponseJson(rr, false);
c@116:         }
c@75:     } else if (format == "json-b64") {
c@116:         if (rr.direction == RequestOrResponse::Request) {
c@116:             writeRequestJson(rr, true);
c@116:         } else {
c@116:             writeResponseJson(rr, true);
c@116:         }
c@75:     } else if (format == "capnp") {
c@116:         if (rr.direction == RequestOrResponse::Request) {
c@116:             writeRequestCapnp(rr);
c@116:         } else {
c@116:             writeResponseCapnp(rr);
c@116:         }
c@75:     } else {
c@116:         throw runtime_error("unknown output format \"" + format + "\"");
c@75:     }
c@75: }
c@75: 
c@75: int main(int argc, char **argv)
c@75: {
c@75:     if (argc < 2) {
c@116:         usage();
c@75:     }
c@75: 
c@75:     string informat = "json", outformat = "json";
c@75:     RequestOrResponse::Direction direction = RequestOrResponse::Request;
c@75:     bool haveDirection = false;
c@75:     
c@75:     for (int i = 1; i < argc; ++i) {
c@75: 
c@116:         string arg = argv[i];
c@116:         bool final = (i + 1 == argc);
c@116:         
c@116:         if (arg == "-i") {
c@116:             if (final) usage();
c@116:             else informat = argv[++i];
c@75: 
c@116:         } else if (arg == "-o") {
c@116:             if (final) usage();
c@116:             else outformat = argv[++i];
c@75: 
c@116:         } else if (arg == "request") {
c@116:             direction = RequestOrResponse::Request;
c@116:             haveDirection = true;
c@75: 
c@116:         } else if (arg == "response") {
c@116:             direction = RequestOrResponse::Response;
c@116:             haveDirection = true;
c@116:             
c@116:         } else {
c@116:             usage();
c@116:         }
c@75:     }
c@75: 
c@75:     if (informat == "" || outformat == "" || !haveDirection) {
c@116:         usage();
c@75:     }
c@75: 
c@123: #ifdef _WIN32
c@123:     if (informat == "capnp") {
c@123:         int result = _setmode(_fileno(stdin), _O_BINARY);
c@123:         if (result == -1) {
c@123:             cerr << "Failed to set binary mode on stdin, necessary for capnp format" << endl;
c@123:             exit(1);
c@123:         }
c@123:     }
c@123:     if (outformat == "capnp") {
c@123:         int result = _setmode(_fileno(stdout), _O_BINARY);
c@123:         if (result == -1) {
c@123:             cerr << "Failed to set binary mode on stdout, necessary for capnp format" << endl;
c@123:             exit(1);
c@123:         }
c@123:     }
c@123: #endif
c@123: 
c@75:     while (true) {
c@75: 
c@116:         try {
c@75: 
cannam@158:             bool eof = false;
cannam@158:             RequestOrResponse rr = readInput(informat, direction, eof);
cannam@158:             if (eof) break;
c@75: 
c@116:             writeOutput(outformat, rr);
cannam@158: 
c@116:         } catch (std::exception &e) {
c@116:             cerr << "Error: " << e.what() << endl;
c@116:             exit(1);
c@116:         }
c@75:     }
c@75: 
c@75:     exit(0);
c@75: }
c@75: 
c@75: