Switch to side-by-side view

--- a/src/cdplugins/cmdtalk.cpp
+++ b/src/cdplugins/cmdtalk.cpp
@@ -1 +1,208 @@
-/home/dockes/projets/docklib/cmdtalk/cmdtalk.cpp+/* Copyright (C) 2016 J.F.Dockes 
+ *   This program 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 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program 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 this program; if not, write to the
+ *   Free Software Foundation, Inc.,
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#include "cmdtalk.h"
+
+#include <stdio.h>
+
+#include <iostream>
+#include <sstream>
+#include <mutex>
+
+#include "smallut.h"
+#include "execmd.h"
+#include "log.h"
+
+using namespace std;
+
+class CmdTalk::Internal {
+public:
+    Internal()
+	: cmd(0) {
+    }
+    ~Internal() {
+	delete cmd;
+    }
+    bool readDataElement(string& name, string &data);
+
+    bool talk(const pair<string, string>& arg0,
+	      const unordered_map<string, string>& args,
+	      unordered_map<string, string>& rep);
+    ExecCmd *cmd;
+    std::mutex mmutex;
+};
+
+CmdTalk::CmdTalk()
+{
+    m = new Internal;
+}
+CmdTalk::~CmdTalk()
+{
+    delete m;
+}
+
+bool CmdTalk::startCmd(const string& cmdname,
+		       const vector<string>& args,
+		       const vector<string>& env,
+		       const vector<string>& path)
+{
+    LOGDEB("CmdTalk::startCmd\n" );
+
+    delete m->cmd;
+    m->cmd = new ExecCmd;
+    
+    for (const auto& it : env) {
+	m->cmd->putenv(it);
+    }
+
+    string acmdname(cmdname);
+    if (!path.empty()) {
+	string colonpath;
+	for (const auto& it: path) {
+	    colonpath += it + ":";
+	}
+	if (!colonpath.empty()) {
+	    colonpath.erase(colonpath.size()-1);
+	}
+	LOGDEB("CmdTalk::startCmd: PATH: [" << colonpath << "]\n");
+	ExecCmd::which(cmdname, acmdname, colonpath.c_str());
+    }
+
+    if (m->cmd->startExec(acmdname, args, 1, 1) < 0) {
+        return false;
+    }
+    return true;
+}
+
+// Messages are made of data elements. Each element is like:
+// name: len\ndata
+// An empty line signals the end of the message, so the whole thing
+// would look like:
+// Name1: Len1\nData1Name2: Len2\nData2\n
+bool CmdTalk::Internal::readDataElement(string& name, string &data)
+{
+    string ibuf;
+
+    // Read name and length
+    if (cmd->getline(ibuf) <= 0) {
+        LOGERR("CmdTalk: getline error\n" );
+        return false;
+    }
+    
+    LOGDEB1("CmdTalk:rde: line ["  << (ibuf) << "]\n" );
+
+    // Empty line (end of message) ?
+    if (!ibuf.compare("\n")) {
+        LOGDEB("CmdTalk: Got empty line\n" );
+        return true;
+    }
+
+    // We're expecting something like Name: len\n
+    vector<string> tokens;
+    stringToTokens(ibuf, tokens);
+    if (tokens.size() != 2) {
+        LOGERR("CmdTalk: bad line in filter output: ["  << (ibuf) << "]\n" );
+        return false;
+    }
+    vector<string>::iterator it = tokens.begin();
+    name = *it++;
+    string& slen = *it;
+    int len;
+    if (sscanf(slen.c_str(), "%d", &len) != 1) {
+        LOGERR("CmdTalk: bad line in filter output: ["  << (ibuf) << "]\n" );
+        return false;
+    }
+
+    // Read element data
+    data.erase();
+    if (len > 0 && cmd->receive(data, len) != len) {
+        LOGERR("CmdTalk: expected " << len << " bytes of data, got " <<
+	       data.length() << "\n");
+        return false;
+    }
+    LOGDEB1("CmdTalk:rde: got: name [" << name << "] len " << len <<"value ["<<
+	    (data.size() > 100 ? (data.substr(0, 100) + " ...") : data)<< endl);
+    return true;
+}
+
+bool CmdTalk::Internal::talk(const pair<string, string>& arg0,
+			     const unordered_map<string, string>& args,
+			     unordered_map<string, string>& rep)
+{
+    std::unique_lock<std::mutex> lock(mmutex);
+    if (cmd->getChildPid() <= 0) {
+	LOGERR("CmdTalk::talk: no process\n");
+        return false;
+    }
+
+    ostringstream obuf;
+    if (!arg0.first.empty()) {
+        obuf << arg0.first << ": " << arg0.second.size() << "\n" << arg0.second;
+    }
+    for (const auto& it : args) {
+        obuf << it.first << ": " << it.second.size() << "\n" << it.second;
+    }
+    obuf << "\n";
+
+    if (cmd->send(obuf.str()) < 0) {
+        cmd->zapChild();
+        LOGERR("CmdTalk: send error\n" );
+        return false;
+    }
+
+    // Read answer (multiple elements)
+    LOGDEB1("CmdTalk: reading answer\n" );
+    for (;;) {
+        string name, data;
+	if (!readDataElement(name, data)) {
+	    cmd->zapChild();
+	    return false;
+	}
+        if (name.empty()) {
+            break;
+	}
+	trimstring(name, ":");
+	LOGDEB("CmdTalk: got [" << name << "] -> [" << data << "]\n");
+	rep[name] = data;
+    }
+
+    if (rep.find("cmdtalkstatus") != rep.end()) {
+	return false;
+    } else {
+	return true;
+    }
+}
+
+bool CmdTalk::running()
+{
+    return m && m->cmd && m->cmd->getChildPid() > 0;
+}
+
+bool CmdTalk::talk(const unordered_map<string, string>& args,
+		   unordered_map<string, string>& rep)
+{
+    return m->talk({"",""}, args, rep);
+}
+
+bool CmdTalk::callproc(
+	const string& proc,
+	const unordered_map<std::string, std::string>& args,
+	unordered_map<std::string, std::string>& rep)
+{
+    return m->talk({"cmdtalk:proc", proc}, args, rep);
+}
+
+