<commit_before>/*
 * SessionConsoleProcess.cpp
 *
 * Copyright (C) 2009-11 by RStudio, Inc.
 *
 * This program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */
#include "SessionConsoleProcess.hpp"

#include <core/json/JsonRpc.hpp>
#include <core/system/Process.hpp>
#include <core/system/ShellUtils.hpp>
#include <core/Exec.hpp>
#include <core/SafeConvert.hpp>
#include <core/Settings.hpp>

#include <core/system/Environment.hpp>

#include <session/SessionModuleContext.hpp>

#include "config.h"

#ifdef RSTUDIO_SERVER
#include <core/system/Crypto.hpp>
#endif

using namespace core;

namespace session {
namespace modules {
namespace console_process {

namespace {
   const size_t OUTPUT_BUFFER_SIZE = 8192;
   typedef std::map<std::string, boost::shared_ptr<ConsoleProcess> > ProcTable;
   ProcTable s_procs;

   core::system::ProcessOptions procOptions()
   {
      core::system::ProcessOptions options;
#ifndef _WIN32
      options.detachSession = true;
#endif
      return options;
   }
} // anonymous namespace

const int kDefaultMaxOutputLines = 500;

ConsoleProcess::ConsoleProcess()
   : dialog_(false), interactionMode_(InteractionNever),
     maxOutputLines_(kDefaultMaxOutputLines), started_(true),
     interrupt_(false), outputBuffer_(OUTPUT_BUFFER_SIZE)
{
   // When we retrieve from outputBuffer, we only want complete lines. Add a
   // dummy \n so we can tell the first line is a complete line.
   outputBuffer_.push_back('\n');
}

#ifndef _WIN32
ConsoleProcess::ConsoleProcess(const std::string& command,
                               const core::system::ProcessOptions& options,
                               const std::string& caption,
                               bool dialog,
                               InteractionMode interactionMode,
                               int maxOutputLines,
                               const boost::function<void()>& onExit)
   : command_(command), options_(options), caption_(caption), dialog_(dialog),
     interactionMode_(interactionMode), maxOutputLines_(maxOutputLines),
     started_(false), interrupt_(false),
     outputBuffer_(OUTPUT_BUFFER_SIZE),
     onExit_(onExit)
{
   commonInit();
}
#endif

ConsoleProcess::ConsoleProcess(const std::string& program,
                               const std::vector<std::string>& args,
                               const core::system::ProcessOptions& options,
                               const std::string& caption,
                               bool dialog,
                               InteractionMode interactionMode,
                               int maxOutputLines,
                               const boost::function<void()>& onExit)
   : program_(program), args_(args), options_(options), caption_(caption), dialog_(dialog),
     interactionMode_(interactionMode), maxOutputLines_(maxOutputLines),
     started_(false),  interrupt_(false),
     outputBuffer_(OUTPUT_BUFFER_SIZE),
     onExit_(onExit)
{
   commonInit();
}

void ConsoleProcess::commonInit()
{
   handle_ = core::system::generateUuid(false);

   // always redirect stderr to stdout so output is interleaved
   options_.redirectStdErrToStdOut = true;

   if (interactionMode() != InteractionNever)
   {
#ifdef _WIN32
      // NOTE: We use consoleio.exe here in order to make sure svn.exe password
      // prompting works properly
      options_.createNewConsole = true;

      // build new args
      shell_utils::ShellArgs args;
      args << program_;
      args << args_;

      // fixup program_ and args_ so we run the consoleio.exe proxy
      FilePath consoleIoPath = session::options().consoleIoPath();
      program_ = consoleIoPath.absolutePathNative();
      args_ = args;
#else
      // request a pseudoterminal if this is an interactive console process
      options_.pseudoterminal = core::system::Pseudoterminal(80, 1);

      // define TERM to dumb (but first make sure we have an environment
      // block to modify)
      if (!options_.environment)
      {
         core::system::Options childEnv;
         core::system::environment(&childEnv);
         options_.environment = childEnv;
      }
      core::system::setenv(&(options_.environment.get()), "TERM", "dumb");
#endif
   }

   // When we retrieve from outputBuffer, we only want complete lines. Add a
   // dummy \n so we can tell the first line is a complete line.
   outputBuffer_.push_back('\n');
}

std::string ConsoleProcess::bufferedOutput() const
{
   boost::circular_buffer<char>::const_iterator pos =
         std::find(outputBuffer_.begin(), outputBuffer_.end(), '\n');

   std::string result;
   if (pos != outputBuffer_.end())
      pos++;
   std::copy(pos, outputBuffer_.end(), std::back_inserter(result));
   // Will be empty if the buffer was overflowed by a single line
   return result;
}

Error ConsoleProcess::start()
{
   if (started_)
      return Success();

   Error error;
   if (!command_.empty())
   {
      error = module_context::processSupervisor().runCommand(
                                 command_, options_, createProcessCallbacks());
   }
   else
   {
      error = module_context::processSupervisor().runProgram(
                          program_, args_, options_, createProcessCallbacks());
   }
   if (!error)
      started_ = true;
   return error;
}

void ConsoleProcess::enqueInput(const Input& input)
{
   inputQueue_.push(input);
}

void ConsoleProcess::interrupt()
{
   interrupt_ = true;
}

bool ConsoleProcess::onContinue(core::system::ProcessOperations& ops)
{
   // full stop interrupt if requested
   if (interrupt_)
      return false;

   // process input queue
   while (!inputQueue_.empty())
   {
      // pop input
      Input input = inputQueue_.front();
      inputQueue_.pop();

      // pty interrupt
      if (input.interrupt)
      {
         Error error = ops.ptyInterrupt();
         if (error)
            LOG_ERROR(error);

         if (input.echoInput)
            appendToOutputBuffer("^C");
      }

      // text input
      else
      {
         Error error = ops.writeToStdin(input.text, false);
         if (error)
            LOG_ERROR(error);

         if (input.echoInput)
            appendToOutputBuffer(input.text);
         else
            appendToOutputBuffer("\n");
      }
   }

   // continue
   return true;
}

void ConsoleProcess::appendToOutputBuffer(const std::string &str)
{
   std::copy(str.begin(), str.end(), std::back_inserter(outputBuffer_));
}

void ConsoleProcess::enqueOutputEvent(const std::string &output, bool error)
{
   // convert line endings to posix
   std::string convertedOutput = output;
   string_utils::convertLineEndings(&convertedOutput,
                                    string_utils::LineEndingPosix);

   // copy to output buffer
   appendToOutputBuffer(convertedOutput);

   // If there's more output than the client can even show, then
   // truncate it to the amount that the client can show. Too much
   // output can overwhelm the client, making it unresponsive.
   std::string trimmedOutput = convertedOutput;
   string_utils::trimLeadingLines(maxOutputLines_, &trimmedOutput);

   json::Object data;
   data["handle"] = handle_;
   data["error"] = error;
   data["output"] = trimmedOutput;
   module_context::enqueClientEvent(
         ClientEvent(client_events::kConsoleProcessOutput, data));
}

void ConsoleProcess::onStdout(core::system::ProcessOperations& ops,
                              const std::string& output)
{
   enqueOutputEvent(output, false);
}

void ConsoleProcess::onExit(int exitCode)
{
   exitCode_.reset(exitCode);

   json::Object data;
   data["handle"] = handle_;
   data["exitCode"] = exitCode;
   module_context::enqueClientEvent(
         ClientEvent(client_events::kConsoleProcessExit, data));

   if (onExit_)
      onExit_();
}

core::json::Object ConsoleProcess::toJson() const
{
   json::Object result;
   result["handle"] = handle_;
   result["caption"] = caption_;
   result["dialog"] = dialog_;
   result["interaction_mode"] = static_cast<int>(interactionMode_);
   result["max_output_lines"] = maxOutputLines_;
   result["buffered_output"] = bufferedOutput();
   if (exitCode_)
      result["exit_code"] = *exitCode_;
   else
      result["exit_code"] = json::Value();
   return result;
}

boost::shared_ptr<ConsoleProcess> ConsoleProcess::fromJson(
                                             core::json::Object &obj)
{
   boost::shared_ptr<ConsoleProcess> pProc(new ConsoleProcess());
   pProc->handle_ = obj["handle"].get_str();
   pProc->caption_ = obj["caption"].get_str();
   pProc->dialog_ = obj["dialog"].get_bool();

   json::Value mode = obj["interaction_mode"];
   if (!mode.is_null())
      pProc->interactionMode_ = static_cast<InteractionMode>(mode.get_int());
   else
      pProc->interactionMode_ = InteractionNever;

   json::Value maxLines = obj["max_output_lines"];
   if (!maxLines.is_null())
      pProc->maxOutputLines_ = maxLines.get_int();
   else
      pProc->maxOutputLines_ = kDefaultMaxOutputLines;

   std::string bufferedOutput = obj["buffered_output"].get_str();
   std::copy(bufferedOutput.begin(), bufferedOutput.end(),
             std::back_inserter(pProc->outputBuffer_));
   json::Value exitCode = obj["exit_code"];
   if (exitCode.is_null())
      pProc->exitCode_.reset();
   else
      pProc->exitCode_.reset(exitCode.get_int());

   return pProc;
}

core::system::ProcessCallbacks ConsoleProcess::createProcessCallbacks()
{
   core::system::ProcessCallbacks cb;
   cb.onContinue = boost::bind(&ConsoleProcess::onContinue, ConsoleProcess::shared_from_this(), _1);
   cb.onStdout = boost::bind(&ConsoleProcess::onStdout, ConsoleProcess::shared_from_this(), _1, _2);
   cb.onExit = boost::bind(&ConsoleProcess::onExit, ConsoleProcess::shared_from_this(), _1);
   return cb;
}

Error procStart(const json::JsonRpcRequest& request,
                json::JsonRpcResponse* pResponse)
{
   std::string handle;
   Error error = json::readParams(request.params, &handle);
   if (error)
      return error;
   ProcTable::const_iterator pos = s_procs.find(handle);
   if (pos != s_procs.end())
   {
      return pos->second->start();
   }
   else
   {
      return systemError(boost::system::errc::invalid_argument,
                         ERROR_LOCATION);
   }
}

Error procInterrupt(const json::JsonRpcRequest& request,
                    json::JsonRpcResponse* pResponse)
{
   std::string handle;
   Error error = json::readParams(request.params, &handle);
   if (error)
      return error;
   ProcTable::const_iterator pos = s_procs.find(handle);
   if (pos != s_procs.end())
   {
      pos->second->interrupt();
      return Success();
   }
   else
   {
      return systemError(boost::system::errc::invalid_argument,
                         ERROR_LOCATION);
   }
}

Error procReap(const json::JsonRpcRequest& request,
               json::JsonRpcResponse* pResponse)
{
   std::string handle;
   Error error = json::readParams(request.params, &handle);
   if (error)
      return error;

   if (!s_procs.erase(handle))
   {
      return systemError(boost::system::errc::invalid_argument,
                         ERROR_LOCATION);
   }
   else
   {
      return Success();
   }
}

Error procWriteStdin(const json::JsonRpcRequest& request,
                     json::JsonRpcResponse* pResponse)
{
   std::string handle;
   Error error = json::readParam(request.params, 0, &handle);
   if (error)
      return error;

   ConsoleProcess::Input input;
   error = json::readObjectParam(request.params, 1,
                                 "interrupt", &input.interrupt,
                                 "text", &input.text,
                                 "echo_input", &input.echoInput);
   if (error)
      return error;

   ProcTable::const_iterator pos = s_procs.find(handle);
   if (pos != s_procs.end())
   {
#ifdef RSTUDIO_SERVER
      if (session::options().programMode() == kSessionProgramModeServer)
      {
         if (!input.interrupt)
         {
            error = core::system::crypto::rsaPrivateDecrypt(input.text,
                                                            &input.text);
            if (error)
               return error;
         }
      }
#endif

      pos->second->enqueInput(input);

      return Success();
   }
   else
   {
      return systemError(boost::system::errc::invalid_argument,
                         ERROR_LOCATION);
   }
}

#ifndef _WIN32
boost::shared_ptr<ConsoleProcess> ConsoleProcess::create(
      const std::string& command,
      core::system::ProcessOptions options,
      const std::string& caption,
      bool dialog,
      InteractionMode interactionMode,
      int maxOutputLines,
      const boost::function<void()>& onExit)
{
   options.terminateChildren = true;
   boost::shared_ptr<ConsoleProcess> ptrProc(
         new ConsoleProcess(command,
                            options,
                            caption,
                            dialog,
                            interactionMode,
                            maxOutputLines,
                            onExit));
   s_procs[ptrProc->handle()] = ptrProc;
   return ptrProc;
}
#endif

boost::shared_ptr<ConsoleProcess> ConsoleProcess::create(
      const std::string& program,
      const std::vector<std::string>& args,
      core::system::ProcessOptions options,
      const std::string& caption,
      bool dialog,
      InteractionMode interactionMode,
      int maxOutputLines,
      const boost::function<void()>& onExit)
{
   options.terminateChildren = true;
   boost::shared_ptr<ConsoleProcess> ptrProc(
         new ConsoleProcess(program,
                            args,
                            options,
                            caption,
                            dialog,
                            interactionMode,
                            maxOutputLines,
                            onExit));
   s_procs[ptrProc->handle()] = ptrProc;
   return ptrProc;
}

core::json::Array processesAsJson()
{
   json::Array procInfos;
   for (ProcTable::const_iterator it = s_procs.begin();
        it != s_procs.end();
        it++)
   {
      procInfos.push_back(it->second->toJson());
   }
   return procInfos;
}

void onSuspend(core::Settings* pSettings)
{
   json::Array array;
   for (ProcTable::const_iterator it = s_procs.begin();
        it != s_procs.end();
        it++)
   {
      array.push_back(it->second->toJson());
   }

   std::ostringstream ostr;
   json::write(array, ostr);
   pSettings->set("console_procs", ostr.str());
}

void onResume(const core::Settings& settings)
{
   std::string strVal = settings.get("console_procs");
   if (strVal.empty())
      return;

   json::Value value;
   if (!json::parse(strVal, &value))
      return;

   json::Array procs = value.get_array();
   for (json::Array::iterator it = procs.begin();
        it != procs.end();
        it++)
   {
      boost::shared_ptr<ConsoleProcess> proc =
                                    ConsoleProcess::fromJson(it->get_obj());
      s_procs[proc->handle()] = proc;
   }
}

Error initialize()
{
   using boost::bind;
   using namespace module_context;

   // add suspend/resume handler
   addSuspendHandler(SuspendHandler(onSuspend, onResume));

   // install rpc methods
   ExecBlock initBlock ;
   initBlock.addFunctions()
      (bind(registerRpcMethod, "process_start", procStart))
      (bind(registerRpcMethod, "process_interrupt", procInterrupt))
      (bind(registerRpcMethod, "process_reap", procReap))
      (bind(registerRpcMethod, "process_write_stdin", procWriteStdin));

   return initBlock.execute();
}

} // namespace console_process
} // namespace modules
} // namespace session
<commit_msg>Use Windows line endings on ConsoleProcess input<commit_after>/*
 * SessionConsoleProcess.cpp
 *
 * Copyright (C) 2009-11 by RStudio, Inc.
 *
 * This program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */
#include "SessionConsoleProcess.hpp"

#include <core/json/JsonRpc.hpp>
#include <core/system/Process.hpp>
#include <core/system/ShellUtils.hpp>
#include <core/Exec.hpp>
#include <core/SafeConvert.hpp>
#include <core/Settings.hpp>

#include <core/system/Environment.hpp>

#include <session/SessionModuleContext.hpp>

#include "config.h"

#ifdef RSTUDIO_SERVER
#include <core/system/Crypto.hpp>
#endif

using namespace core;

namespace session {
namespace modules {
namespace console_process {

namespace {
   const size_t OUTPUT_BUFFER_SIZE = 8192;
   typedef std::map<std::string, boost::shared_ptr<ConsoleProcess> > ProcTable;
   ProcTable s_procs;

   core::system::ProcessOptions procOptions()
   {
      core::system::ProcessOptions options;
#ifndef _WIN32
      options.detachSession = true;
#endif
      return options;
   }
} // anonymous namespace

const int kDefaultMaxOutputLines = 500;

ConsoleProcess::ConsoleProcess()
   : dialog_(false), interactionMode_(InteractionNever),
     maxOutputLines_(kDefaultMaxOutputLines), started_(true),
     interrupt_(false), outputBuffer_(OUTPUT_BUFFER_SIZE)
{
   // When we retrieve from outputBuffer, we only want complete lines. Add a
   // dummy \n so we can tell the first line is a complete line.
   outputBuffer_.push_back('\n');
}

#ifndef _WIN32
ConsoleProcess::ConsoleProcess(const std::string& command,
                               const core::system::ProcessOptions& options,
                               const std::string& caption,
                               bool dialog,
                               InteractionMode interactionMode,
                               int maxOutputLines,
                               const boost::function<void()>& onExit)
   : command_(command), options_(options), caption_(caption), dialog_(dialog),
     interactionMode_(interactionMode), maxOutputLines_(maxOutputLines),
     started_(false), interrupt_(false),
     outputBuffer_(OUTPUT_BUFFER_SIZE),
     onExit_(onExit)
{
   commonInit();
}
#endif

ConsoleProcess::ConsoleProcess(const std::string& program,
                               const std::vector<std::string>& args,
                               const core::system::ProcessOptions& options,
                               const std::string& caption,
                               bool dialog,
                               InteractionMode interactionMode,
                               int maxOutputLines,
                               const boost::function<void()>& onExit)
   : program_(program), args_(args), options_(options), caption_(caption), dialog_(dialog),
     interactionMode_(interactionMode), maxOutputLines_(maxOutputLines),
     started_(false),  interrupt_(false),
     outputBuffer_(OUTPUT_BUFFER_SIZE),
     onExit_(onExit)
{
   commonInit();
}

void ConsoleProcess::commonInit()
{
   handle_ = core::system::generateUuid(false);

   // always redirect stderr to stdout so output is interleaved
   options_.redirectStdErrToStdOut = true;

   if (interactionMode() != InteractionNever)
   {
#ifdef _WIN32
      // NOTE: We use consoleio.exe here in order to make sure svn.exe password
      // prompting works properly
      options_.createNewConsole = true;

      // build new args
      shell_utils::ShellArgs args;
      args << program_;
      args << args_;

      // fixup program_ and args_ so we run the consoleio.exe proxy
      FilePath consoleIoPath = session::options().consoleIoPath();
      program_ = consoleIoPath.absolutePathNative();
      args_ = args;
#else
      // request a pseudoterminal if this is an interactive console process
      options_.pseudoterminal = core::system::Pseudoterminal(80, 1);

      // define TERM to dumb (but first make sure we have an environment
      // block to modify)
      if (!options_.environment)
      {
         core::system::Options childEnv;
         core::system::environment(&childEnv);
         options_.environment = childEnv;
      }
      core::system::setenv(&(options_.environment.get()), "TERM", "dumb");
#endif
   }

   // When we retrieve from outputBuffer, we only want complete lines. Add a
   // dummy \n so we can tell the first line is a complete line.
   outputBuffer_.push_back('\n');
}

std::string ConsoleProcess::bufferedOutput() const
{
   boost::circular_buffer<char>::const_iterator pos =
         std::find(outputBuffer_.begin(), outputBuffer_.end(), '\n');

   std::string result;
   if (pos != outputBuffer_.end())
      pos++;
   std::copy(pos, outputBuffer_.end(), std::back_inserter(result));
   // Will be empty if the buffer was overflowed by a single line
   return result;
}

Error ConsoleProcess::start()
{
   if (started_)
      return Success();

   Error error;
   if (!command_.empty())
   {
      error = module_context::processSupervisor().runCommand(
                                 command_, options_, createProcessCallbacks());
   }
   else
   {
      error = module_context::processSupervisor().runProgram(
                          program_, args_, options_, createProcessCallbacks());
   }
   if (!error)
      started_ = true;
   return error;
}

void ConsoleProcess::enqueInput(const Input& input)
{
   inputQueue_.push(input);
}

void ConsoleProcess::interrupt()
{
   interrupt_ = true;
}

bool ConsoleProcess::onContinue(core::system::ProcessOperations& ops)
{
   // full stop interrupt if requested
   if (interrupt_)
      return false;

   // process input queue
   while (!inputQueue_.empty())
   {
      // pop input
      Input input = inputQueue_.front();
      inputQueue_.pop();

      // pty interrupt
      if (input.interrupt)
      {
         Error error = ops.ptyInterrupt();
         if (error)
            LOG_ERROR(error);

         if (input.echoInput)
            appendToOutputBuffer("^C");
      }

      // text input
      else
      {
         std::string inputText = input.text;
#ifdef _WIN32
         string_utils::convertLineEndings(&inputText, string_utils::LineEndingWindows);
#endif
         Error error = ops.writeToStdin(inputText, false);
         if (error)
            LOG_ERROR(error);

         if (input.echoInput)
            appendToOutputBuffer(inputText);
         else
            appendToOutputBuffer("\n");
      }
   }

   // continue
   return true;
}

void ConsoleProcess::appendToOutputBuffer(const std::string &str)
{
   std::copy(str.begin(), str.end(), std::back_inserter(outputBuffer_));
}

void ConsoleProcess::enqueOutputEvent(const std::string &output, bool error)
{
   // convert line endings to posix
   std::string convertedOutput = output;
   string_utils::convertLineEndings(&convertedOutput,
                                    string_utils::LineEndingPosix);

   // copy to output buffer
   appendToOutputBuffer(convertedOutput);

   // If there's more output than the client can even show, then
   // truncate it to the amount that the client can show. Too much
   // output can overwhelm the client, making it unresponsive.
   std::string trimmedOutput = convertedOutput;
   string_utils::trimLeadingLines(maxOutputLines_, &trimmedOutput);

   json::Object data;
   data["handle"] = handle_;
   data["error"] = error;
   data["output"] = trimmedOutput;
   module_context::enqueClientEvent(
         ClientEvent(client_events::kConsoleProcessOutput, data));
}

void ConsoleProcess::onStdout(core::system::ProcessOperations& ops,
                              const std::string& output)
{
   enqueOutputEvent(output, false);
}

void ConsoleProcess::onExit(int exitCode)
{
   exitCode_.reset(exitCode);

   json::Object data;
   data["handle"] = handle_;
   data["exitCode"] = exitCode;
   module_context::enqueClientEvent(
         ClientEvent(client_events::kConsoleProcessExit, data));

   if (onExit_)
      onExit_();
}

core::json::Object ConsoleProcess::toJson() const
{
   json::Object result;
   result["handle"] = handle_;
   result["caption"] = caption_;
   result["dialog"] = dialog_;
   result["interaction_mode"] = static_cast<int>(interactionMode_);
   result["max_output_lines"] = maxOutputLines_;
   result["buffered_output"] = bufferedOutput();
   if (exitCode_)
      result["exit_code"] = *exitCode_;
   else
      result["exit_code"] = json::Value();
   return result;
}

boost::shared_ptr<ConsoleProcess> ConsoleProcess::fromJson(
                                             core::json::Object &obj)
{
   boost::shared_ptr<ConsoleProcess> pProc(new ConsoleProcess());
   pProc->handle_ = obj["handle"].get_str();
   pProc->caption_ = obj["caption"].get_str();
   pProc->dialog_ = obj["dialog"].get_bool();

   json::Value mode = obj["interaction_mode"];
   if (!mode.is_null())
      pProc->interactionMode_ = static_cast<InteractionMode>(mode.get_int());
   else
      pProc->interactionMode_ = InteractionNever;

   json::Value maxLines = obj["max_output_lines"];
   if (!maxLines.is_null())
      pProc->maxOutputLines_ = maxLines.get_int();
   else
      pProc->maxOutputLines_ = kDefaultMaxOutputLines;

   std::string bufferedOutput = obj["buffered_output"].get_str();
   std::copy(bufferedOutput.begin(), bufferedOutput.end(),
             std::back_inserter(pProc->outputBuffer_));
   json::Value exitCode = obj["exit_code"];
   if (exitCode.is_null())
      pProc->exitCode_.reset();
   else
      pProc->exitCode_.reset(exitCode.get_int());

   return pProc;
}

core::system::ProcessCallbacks ConsoleProcess::createProcessCallbacks()
{
   core::system::ProcessCallbacks cb;
   cb.onContinue = boost::bind(&ConsoleProcess::onContinue, ConsoleProcess::shared_from_this(), _1);
   cb.onStdout = boost::bind(&ConsoleProcess::onStdout, ConsoleProcess::shared_from_this(), _1, _2);
   cb.onExit = boost::bind(&ConsoleProcess::onExit, ConsoleProcess::shared_from_this(), _1);
   return cb;
}

Error procStart(const json::JsonRpcRequest& request,
                json::JsonRpcResponse* pResponse)
{
   std::string handle;
   Error error = json::readParams(request.params, &handle);
   if (error)
      return error;
   ProcTable::const_iterator pos = s_procs.find(handle);
   if (pos != s_procs.end())
   {
      return pos->second->start();
   }
   else
   {
      return systemError(boost::system::errc::invalid_argument,
                         ERROR_LOCATION);
   }
}

Error procInterrupt(const json::JsonRpcRequest& request,
                    json::JsonRpcResponse* pResponse)
{
   std::string handle;
   Error error = json::readParams(request.params, &handle);
   if (error)
      return error;
   ProcTable::const_iterator pos = s_procs.find(handle);
   if (pos != s_procs.end())
   {
      pos->second->interrupt();
      return Success();
   }
   else
   {
      return systemError(boost::system::errc::invalid_argument,
                         ERROR_LOCATION);
   }
}

Error procReap(const json::JsonRpcRequest& request,
               json::JsonRpcResponse* pResponse)
{
   std::string handle;
   Error error = json::readParams(request.params, &handle);
   if (error)
      return error;

   if (!s_procs.erase(handle))
   {
      return systemError(boost::system::errc::invalid_argument,
                         ERROR_LOCATION);
   }
   else
   {
      return Success();
   }
}

Error procWriteStdin(const json::JsonRpcRequest& request,
                     json::JsonRpcResponse* pResponse)
{
   std::string handle;
   Error error = json::readParam(request.params, 0, &handle);
   if (error)
      return error;

   ConsoleProcess::Input input;
   error = json::readObjectParam(request.params, 1,
                                 "interrupt", &input.interrupt,
                                 "text", &input.text,
                                 "echo_input", &input.echoInput);
   if (error)
      return error;

   ProcTable::const_iterator pos = s_procs.find(handle);
   if (pos != s_procs.end())
   {
#ifdef RSTUDIO_SERVER
      if (session::options().programMode() == kSessionProgramModeServer)
      {
         if (!input.interrupt)
         {
            error = core::system::crypto::rsaPrivateDecrypt(input.text,
                                                            &input.text);
            if (error)
               return error;
         }
      }
#endif

      pos->second->enqueInput(input);

      return Success();
   }
   else
   {
      return systemError(boost::system::errc::invalid_argument,
                         ERROR_LOCATION);
   }
}

#ifndef _WIN32
boost::shared_ptr<ConsoleProcess> ConsoleProcess::create(
      const std::string& command,
      core::system::ProcessOptions options,
      const std::string& caption,
      bool dialog,
      InteractionMode interactionMode,
      int maxOutputLines,
      const boost::function<void()>& onExit)
{
   options.terminateChildren = true;
   boost::shared_ptr<ConsoleProcess> ptrProc(
         new ConsoleProcess(command,
                            options,
                            caption,
                            dialog,
                            interactionMode,
                            maxOutputLines,
                            onExit));
   s_procs[ptrProc->handle()] = ptrProc;
   return ptrProc;
}
#endif

boost::shared_ptr<ConsoleProcess> ConsoleProcess::create(
      const std::string& program,
      const std::vector<std::string>& args,
      core::system::ProcessOptions options,
      const std::string& caption,
      bool dialog,
      InteractionMode interactionMode,
      int maxOutputLines,
      const boost::function<void()>& onExit)
{
   options.terminateChildren = true;
   boost::shared_ptr<ConsoleProcess> ptrProc(
         new ConsoleProcess(program,
                            args,
                            options,
                            caption,
                            dialog,
                            interactionMode,
                            maxOutputLines,
                            onExit));
   s_procs[ptrProc->handle()] = ptrProc;
   return ptrProc;
}

core::json::Array processesAsJson()
{
   json::Array procInfos;
   for (ProcTable::const_iterator it = s_procs.begin();
        it != s_procs.end();
        it++)
   {
      procInfos.push_back(it->second->toJson());
   }
   return procInfos;
}

void onSuspend(core::Settings* pSettings)
{
   json::Array array;
   for (ProcTable::const_iterator it = s_procs.begin();
        it != s_procs.end();
        it++)
   {
      array.push_back(it->second->toJson());
   }

   std::ostringstream ostr;
   json::write(array, ostr);
   pSettings->set("console_procs", ostr.str());
}

void onResume(const core::Settings& settings)
{
   std::string strVal = settings.get("console_procs");
   if (strVal.empty())
      return;

   json::Value value;
   if (!json::parse(strVal, &value))
      return;

   json::Array procs = value.get_array();
   for (json::Array::iterator it = procs.begin();
        it != procs.end();
        it++)
   {
      boost::shared_ptr<ConsoleProcess> proc =
                                    ConsoleProcess::fromJson(it->get_obj());
      s_procs[proc->handle()] = proc;
   }
}

Error initialize()
{
   using boost::bind;
   using namespace module_context;

   // add suspend/resume handler
   addSuspendHandler(SuspendHandler(onSuspend, onResume));

   // install rpc methods
   ExecBlock initBlock ;
   initBlock.addFunctions()
      (bind(registerRpcMethod, "process_start", procStart))
      (bind(registerRpcMethod, "process_interrupt", procInterrupt))
      (bind(registerRpcMethod, "process_reap", procReap))
      (bind(registerRpcMethod, "process_write_stdin", procWriteStdin));

   return initBlock.execute();
}

} // namespace console_process
} // namespace modules
} // namespace session
<|endoftext|>


<commit_before>/* /% C %/ */
/***********************************************************************
 * cint (C/C++ interpreter)
 ************************************************************************
 * Source file quote.c
 ************************************************************************
 * Description:
 *  Strip and add quotation
 ************************************************************************
 * Copyright(c) 1995~2002  Masaharu Goto
 *
 * For the licensing terms see the file COPYING
 *
 ************************************************************************/

#include "common.h"

#include <set>
#include <string>

using namespace Cint::Internal;

/**************************************************************************
* G__value G__asm_gen_strip_quotation(string)
*
*  remove " and ' from string
**************************************************************************/
void Cint::Internal::G__asm_gen_strip_quotation(G__value *pval)
{
  /**************************************
   * G__LD instruction
   * 0 LD
   * 1 address in data stack
   * put defined
   **************************************/
#ifdef G__ASM_DBG
   if(G__asm_dbg)
      G__fprinterr(G__serr, "%3x,%3x: LD %ld  %s:%d\n", G__asm_cp, G__asm_dt, G__int(*pval), __FILE__, __LINE__);
#endif
  G__asm_inst[G__asm_cp]=G__LD;
  G__asm_inst[G__asm_cp+1]=G__asm_dt;
  G__asm_stack[G__asm_dt] = *pval;
  G__inc_cp_asm(2,1);
}

#include <set>
#include <string>
#if (!defined(__hpux) && !defined(_MSC_VER)) || __HP_aCC >= 53000
using namespace std;
#endif
/******************************************************************
* char* G__savestring()
******************************************************************/
static const char* G__saveconststring(const char* s)
{
   static std::set<std::string> conststring;
   std::string str(s);
   return conststring.insert(str).first->c_str();
}

/******************************************************************
* G__value G__strip_quotation(string)
*
* Allocate memory and store const string expression. Then return
* the string type value.
*
******************************************************************/
G__value Cint::Internal::G__strip_quotation(char *string)
{
  int itemp,itemp2=0,hash;
  int templen = G__LONGLINE;
  char *temp = (char*)malloc(G__LONGLINE);
  G__value result = G__null;
  int lenm1 = strlen(string)-1;

  G__value_typenum(result) = ::Reflex::Type::ByName("const char*");  // result.isconst = G__CONSTVAR;
  if((string[0]=='"')||(string[0]=='\'')) {
    for(itemp=1;
        itemp<lenm1;
        itemp++ ) {
      /*
      temp[itemp2++] = string[itemp];
      */
      if(itemp2+1>templen) {
        temp = (char*)realloc(temp,2*templen);
        templen = 2*templen;
      }
      switch(string[itemp]) {
      case '\\' :
        switch(string[++itemp]) {
        /*
        case 'a':
          temp[itemp2++] = '\a' ;
          break;
        */
        case 'b':
          temp[itemp2++] = '\b' ;
          break;
        case 'f':
          temp[itemp2++] = '\f' ;
          break;
        case 'n':
          temp[itemp2++] = '\n' ;
          break;
        case 'r':
          temp[itemp2++] = '\r' ;
          break;
        case 't':
          temp[itemp2++] = '\t' ;
          break;
        case 'v':
          temp[itemp2++] = '\v' ;
          break;
        case 'x':
        case 'X':
          temp[itemp2]='0';
          temp[itemp2+1]='x';
          hash=1;
          while(hash) {
            switch(string[itemp+hash]) {
            case 'a':
            case 'A':
            case 'b':
            case 'B':
            case 'c':
            case 'C':
            case 'd':
            case 'D':
            case 'e':
            case 'E':
            case 'f':
            case 'F':
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
              temp[itemp2+hash+1]=
                string[itemp+hash];
              ++hash;
              break;
            default:
              itemp += (hash-1);
              temp[itemp2+hash+1]='\0';
              hash=0;
            }
          }
          temp[itemp2] = (char)G__int(G__checkBase(temp+itemp2 ,&hash));
          ++itemp2;
          break;
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
          temp[itemp2]='0';
          temp[itemp2+1]='o';
          hash=0;
          while(isdigit(string[itemp+hash])&&
                hash<3) {
            temp[itemp2+hash+2]=
              string[itemp+hash];
            ++hash;
          }
          itemp += (hash-1);
          temp[itemp2+hash+2]='\0';
          hash=0;
          temp[itemp2] = (char)G__int(G__checkBase(temp+itemp2 ,&hash));
          ++itemp2;
          break;
        case '\n':
          break;
        default:
          temp[itemp2++] = string[itemp];
          break;
        }
        break;
      case '"':
        if('"'==string[itemp+1]) {
          ++itemp;
        }
        else if(G__NOLINK==G__globalcomp)
          G__genericerror("Error: String literal syntax error");
        continue;
      default:
        temp[itemp2++] = string[itemp];
#ifdef G__MULTIBYTE
        if(G__IsDBCSLeadByte(string[itemp])) {
          temp[itemp2++] = string[++itemp];
          G__CheckDBCS2ndByte(string[itemp]);
        }
#endif
        break;
      }
    }
    temp[itemp2]='\0';
  }
  else {
    if(G__isvalue(string)) {
      /* string is a pointer */
      G__letint(&result,'C',atol(string));
      free((void*)temp);
      return(result);
    }
    else {
      /* return string */
      strcpy(temp,string);
    }
  }

  G__letint(&result,'C',(long)G__saveconststring(temp));

  free((void*)temp);
  return(result);
}

/******************************************************************
* char *G__charaddquote(c)
*
* Called by
*   G__tocharexpr()
*   G__valuemonitor()
******************************************************************/
char *Cint::Internal::G__charaddquote(char *string,char c)
{
  switch(c) {
  case '\\':
    sprintf(string,"'\\\\'");
    break;
  case '\'':
    sprintf(string,"'\\''");
    break;
  case '\0':
    sprintf(string,"'\\0'");
    break;
  case '\"':
    sprintf(string,"'\\\"'");
    break;
    /*
      case '\?':
      sprintf(string,"'\\?'");
      break;
      */
    /*
      case '\a':
      sprintf(string,"'\\a'");
      break;
      */
  case '\b':
    sprintf(string,"'\\b'");
    break;
  case '\f':
    sprintf(string,"'\\f'");
    break;
  case '\n':
    sprintf(string,"'\\n'");
    break;
  case '\r':
    sprintf(string,"'\\r'");
    break;
  case '\t':
    sprintf(string,"'\\t'");
    break;
  case '\v':
    sprintf(string,"'\\v'");
    break;
  default:
#ifdef G__MULTIBYTE
    if(G__IsDBCSLeadByte(c)) {
      G__genericerror("Limitation: Multi-byte char in single quote not handled property");
    }
#endif
    sprintf(string,"'%c'",c);
    break;
  }
  return(string);
}

/******************************************************************
* G__strip_singlequotation
*
* Called by
*   G__getitem()
*
******************************************************************/
G__value Cint::Internal::G__strip_singlequotation(char *string)
{
  G__value result = G__null;
  int i;
  G__value_typenum(result) = ::Reflex::Type::ByName("char"); // result.type='c';
  if(string[0]=='\'') {
    switch(string[1]) {
    case '\\':
      switch(string[2]) {
        /*
          case 'a' :
          result.obj.i='\a';
          break;
          */
      case 'b' :
        result.obj.ch='\b';
        break;
      case 'f' :
        result.obj.ch='\f';
        break;
      case 'n' :
        result.obj.ch='\n';
        break;
      case 'r' :
        result.obj.ch='\r';
        break;
      case 't' :
        result.obj.ch='\t';
        break;
      case 'v' :
        result.obj.ch='\v';
        break;
        /*
          case '0' :
          result.obj.ch='\0';
          break;
          */
      case 'x' :
      case 'X' :
        string[1]='0';
        string[strlen(string)-1]='\0';
        result.obj.ch=G__int(G__checkBase(string+1,&i));
        break;
      case '0' :
      case '1' :
      case '2' :
      case '3' :
      case '4' :
      case '5' :
      case '6' :
      case '7' :
        string[0]='0';
        string[1]='o';
        string[strlen(string)-1]='\0';
        result.obj.ch=G__int(G__checkBase(string,&i));
        break;
        default :
          result.obj.ch=string[2];
        break;
      }
      break;
    default:
      result.obj.ch=string[1];
#ifdef G__MULTIBYTE
      if(G__IsDBCSLeadByte(string[1])) {
        G__CheckDBCS2ndByte(string[2]);
        result.obj.i=result.obj.i*0x100+string[2];
        G__value_typenum(result) = G__find_typedef("wchar_t");
      }
#endif
      break;
    }
  }
  else {
    result.obj.ch=string[0];
  }
  return(result);
}

/******************************************************************
* char *G__add_quotation(string)
*
* Called by
*
******************************************************************/
char *Cint::Internal::G__add_quotation(char *string,char *temp)
{
  int c;
  short i=0,l=0;
  temp[i++]='"';
  while((c=string[l++])!='\0') {
    switch(c) {
    case '\n':
      temp[i++]='\\';
      temp[i++]='n';
      break;
    case '\r':
      temp[i++]='\\';
      temp[i++]='r';
      break;
    case '\\':
      temp[i++]='\\';
      temp[i++]='\\';
      break;
    case '"':
      temp[i++]='\\';
      temp[i++]='"';
      break;
    default:
      temp[i++]=c;
      break;
    }
  }
  temp[i++]='"';
  temp[i]='\0';
  return(temp);
}

/******************************************************************
* char *G__tocharexpr(result7)
******************************************************************/
char *Cint::Internal::G__tocharexpr(char *result7)
{
  if((result7[0]=='\\')&&(result7[1]=='\'')) {
    G__charaddquote(result7,result7[2]);
  }
  return(NULL);
}

/****************************************************************
* char *G__string()
*
****************************************************************/
char *Cint::Internal::G__string(G__value buf,char *temp)
{
  G__StrBuf temp1_sb(G__MAXNAME);
  char *temp1 = temp1_sb;
  switch(G__get_type(G__value_typenum(buf))) {
  case '\0':
    temp[0]='\0'; /* sprintf(temp,""); */
    break;
  case 'C': /* string */
    if(buf.obj.i) {
      G__add_quotation((char *)G__int(buf),temp);
    }
    else {
      temp[0]='\0';
    }
    break;
  case 'd':
  case 'f':
    sprintf(temp,"%.17e",buf.obj.d);
    break;
  case 'w':
    G__logicstring(buf,1,temp1);
    sprintf(temp,"0b%s",temp1);
    break;
  default:
    sprintf(temp,"%ld",buf.obj.i);
    break;
  }
  return(temp);
}

/****************************************************************
* char *G__quotedstring()
*
****************************************************************/
char *Cint::Internal::G__quotedstring(char *buf,char *result)
{
        int i=0,r=0;
        int c;
        while((c=buf[i])) {
                switch(c) {
                case '\\':
                case '"':
                        result[r++] = '\\';
                        result[r++] = c;
                        break;
                default:
                        result[r++] = c;
                        break;
                }
                ++i;
        }
        result[r]='\0';
        return(result);
}

/****************************************************************
* char *G__logicstring()
*
****************************************************************/
char *Cint::Internal::G__logicstring(G__value buf,int dig,char *result)
{
        G__StrBuf tristate_sb(G__MAXNAME);
        char *tristate = tristate_sb;
        unsigned int hilo,hiz,i,ii,flag;
        switch(G__get_type(G__value_typenum(buf))) {
        case 'd': /* double */
        case 'f': /* float */
        case 'w': /* logic */
                hilo = buf.obj.i;
                hiz = *(&buf.obj.i+1);
                G__getbase(hilo,2,32,result);
                G__getbase(hiz,2,32,tristate);
                break;
        default:
                hilo = buf.obj.i;
                hiz = 0;
                G__getbase(hilo,2,32,result);
                G__getbase(hiz,2,32,tristate);
                break;
        }
        flag=0;
        ii=0;
        for(i=0;i<32;i++) {
                if((int)(32-i)<=dig) flag=1;
                switch(result[i]){
                case '0':
                        if(tristate[i]=='0') {
                                if(flag!=0) {
                                        result[ii++]='0';
                                }
                        }
                        else {
                                flag=1;
                                result[ii++]='x';
                        }
                        break;
                case '1':
                        flag=1;
                        if(tristate[i]=='0') {
                                result[ii++]='1';
                        }
                        else {
                                result[ii++]='z';
                        }
                        break;
                }
        }
        if(ii!=0) result[ii]='\0';
        else      result[1]='\0';
        return(result);
}

/*
 * Local Variables:
 * c-tab-always-indent:nil
 * c-indent-level:2
 * c-continued-statement-offset:2
 * c-brace-offset:-2
 * c-brace-imaginary-offset:0
 * c-argdecl-indent:0
 * c-label-offset:-2
 * compile-command:"make -k"
 * End:
 */
<commit_msg>preserve constness<commit_after>/* /% C %/ */
/***********************************************************************
 * cint (C/C++ interpreter)
 ************************************************************************
 * Source file quote.c
 ************************************************************************
 * Description:
 *  Strip and add quotation
 ************************************************************************
 * Copyright(c) 1995~2002  Masaharu Goto
 *
 * For the licensing terms see the file COPYING
 *
 ************************************************************************/

#include "common.h"
#include "Api.h"

#include <set>
#include <string>

using namespace Cint::Internal;

/**************************************************************************
* G__value G__asm_gen_strip_quotation(string)
*
*  remove " and ' from string
**************************************************************************/
void Cint::Internal::G__asm_gen_strip_quotation(G__value *pval)
{
  /**************************************
   * G__LD instruction
   * 0 LD
   * 1 address in data stack
   * put defined
   **************************************/
#ifdef G__ASM_DBG
   if(G__asm_dbg)
      G__fprinterr(G__serr, "%3x,%3x: LD %ld  %s:%d\n", G__asm_cp, G__asm_dt, G__int(*pval), __FILE__, __LINE__);
#endif
  G__asm_inst[G__asm_cp]=G__LD;
  G__asm_inst[G__asm_cp+1]=G__asm_dt;
  G__asm_stack[G__asm_dt] = *pval;
  G__inc_cp_asm(2,1);
}

#include <set>
#include <string>
#if (!defined(__hpux) && !defined(_MSC_VER)) || __HP_aCC >= 53000
using namespace std;
#endif
/******************************************************************
* char* G__savestring()
******************************************************************/
static const char* G__saveconststring(const char* s)
{
   static std::set<std::string> conststring;
   std::string str(s);
   return conststring.insert(str).first->c_str();
}

/******************************************************************
* G__value G__strip_quotation(string)
*
* Allocate memory and store const string expression. Then return
* the string type value.
*
******************************************************************/
G__value Cint::Internal::G__strip_quotation(char *string)
{
  int itemp,itemp2=0,hash;
  int templen = G__LONGLINE;
  char *temp = (char*)malloc(G__LONGLINE);
  G__value result = G__null;
  int lenm1 = strlen(string)-1;

  G__value_typenum(result) = ::Reflex::Type::ByName("const char*");  // result.isconst = G__CONSTVAR;
  if((string[0]=='"')||(string[0]=='\'')) {
    for(itemp=1;
        itemp<lenm1;
        itemp++ ) {
      /*
      temp[itemp2++] = string[itemp];
      */
      if(itemp2+1>templen) {
        temp = (char*)realloc(temp,2*templen);
        templen = 2*templen;
      }
      switch(string[itemp]) {
      case '\\' :
        switch(string[++itemp]) {
        /*
        case 'a':
          temp[itemp2++] = '\a' ;
          break;
        */
        case 'b':
          temp[itemp2++] = '\b' ;
          break;
        case 'f':
          temp[itemp2++] = '\f' ;
          break;
        case 'n':
          temp[itemp2++] = '\n' ;
          break;
        case 'r':
          temp[itemp2++] = '\r' ;
          break;
        case 't':
          temp[itemp2++] = '\t' ;
          break;
        case 'v':
          temp[itemp2++] = '\v' ;
          break;
        case 'x':
        case 'X':
          temp[itemp2]='0';
          temp[itemp2+1]='x';
          hash=1;
          while(hash) {
            switch(string[itemp+hash]) {
            case 'a':
            case 'A':
            case 'b':
            case 'B':
            case 'c':
            case 'C':
            case 'd':
            case 'D':
            case 'e':
            case 'E':
            case 'f':
            case 'F':
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
              temp[itemp2+hash+1]=
                string[itemp+hash];
              ++hash;
              break;
            default:
              itemp += (hash-1);
              temp[itemp2+hash+1]='\0';
              hash=0;
            }
          }
          temp[itemp2] = (char)G__int(G__checkBase(temp+itemp2 ,&hash));
          ++itemp2;
          break;
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
          temp[itemp2]='0';
          temp[itemp2+1]='o';
          hash=0;
          while(isdigit(string[itemp+hash])&&
                hash<3) {
            temp[itemp2+hash+2]=
              string[itemp+hash];
            ++hash;
          }
          itemp += (hash-1);
          temp[itemp2+hash+2]='\0';
          hash=0;
          temp[itemp2] = (char)G__int(G__checkBase(temp+itemp2 ,&hash));
          ++itemp2;
          break;
        case '\n':
          break;
        default:
          temp[itemp2++] = string[itemp];
          break;
        }
        break;
      case '"':
        if('"'==string[itemp+1]) {
          ++itemp;
        }
        else if(G__NOLINK==G__globalcomp)
          G__genericerror("Error: String literal syntax error");
        continue;
      default:
        temp[itemp2++] = string[itemp];
#ifdef G__MULTIBYTE
        if(G__IsDBCSLeadByte(string[itemp])) {
          temp[itemp2++] = string[++itemp];
          G__CheckDBCS2ndByte(string[itemp]);
        }
#endif
        break;
      }
    }
    temp[itemp2]='\0';
  }
  else {
    if(G__isvalue(string)) {
      /* string is a pointer */
       Cint::G__letpointer(&result,(long)string,G__value_typenum(result));
      free((void*)temp);
      return(result);
    }
    else {
      /* return string */
      strcpy(temp,string);
    }
  }

  G__letpointer(&result,(long)G__saveconststring(temp),G__value_typenum(result));

  free((void*)temp);
  return(result);
}

/******************************************************************
* char *G__charaddquote(c)
*
* Called by
*   G__tocharexpr()
*   G__valuemonitor()
******************************************************************/
char *Cint::Internal::G__charaddquote(char *string,char c)
{
  switch(c) {
  case '\\':
    sprintf(string,"'\\\\'");
    break;
  case '\'':
    sprintf(string,"'\\''");
    break;
  case '\0':
    sprintf(string,"'\\0'");
    break;
  case '\"':
    sprintf(string,"'\\\"'");
    break;
    /*
      case '\?':
      sprintf(string,"'\\?'");
      break;
      */
    /*
      case '\a':
      sprintf(string,"'\\a'");
      break;
      */
  case '\b':
    sprintf(string,"'\\b'");
    break;
  case '\f':
    sprintf(string,"'\\f'");
    break;
  case '\n':
    sprintf(string,"'\\n'");
    break;
  case '\r':
    sprintf(string,"'\\r'");
    break;
  case '\t':
    sprintf(string,"'\\t'");
    break;
  case '\v':
    sprintf(string,"'\\v'");
    break;
  default:
#ifdef G__MULTIBYTE
    if(G__IsDBCSLeadByte(c)) {
      G__genericerror("Limitation: Multi-byte char in single quote not handled property");
    }
#endif
    sprintf(string,"'%c'",c);
    break;
  }
  return(string);
}

/******************************************************************
* G__strip_singlequotation
*
* Called by
*   G__getitem()
*
******************************************************************/
G__value Cint::Internal::G__strip_singlequotation(char *string)
{
  G__value result = G__null;
  int i;
  G__value_typenum(result) = ::Reflex::Type::ByName("char"); // result.type='c';
  if(string[0]=='\'') {
    switch(string[1]) {
    case '\\':
      switch(string[2]) {
        /*
          case 'a' :
          result.obj.i='\a';
          break;
          */
      case 'b' :
        result.obj.ch='\b';
        break;
      case 'f' :
        result.obj.ch='\f';
        break;
      case 'n' :
        result.obj.ch='\n';
        break;
      case 'r' :
        result.obj.ch='\r';
        break;
      case 't' :
        result.obj.ch='\t';
        break;
      case 'v' :
        result.obj.ch='\v';
        break;
        /*
          case '0' :
          result.obj.ch='\0';
          break;
          */
      case 'x' :
      case 'X' :
        string[1]='0';
        string[strlen(string)-1]='\0';
        result.obj.ch=G__int(G__checkBase(string+1,&i));
        break;
      case '0' :
      case '1' :
      case '2' :
      case '3' :
      case '4' :
      case '5' :
      case '6' :
      case '7' :
        string[0]='0';
        string[1]='o';
        string[strlen(string)-1]='\0';
        result.obj.ch=G__int(G__checkBase(string,&i));
        break;
        default :
          result.obj.ch=string[2];
        break;
      }
      break;
    default:
      result.obj.ch=string[1];
#ifdef G__MULTIBYTE
      if(G__IsDBCSLeadByte(string[1])) {
        G__CheckDBCS2ndByte(string[2]);
        result.obj.i=result.obj.i*0x100+string[2];
        G__value_typenum(result) = G__find_typedef("wchar_t");
      }
#endif
      break;
    }
  }
  else {
    result.obj.ch=string[0];
  }
  return(result);
}

/******************************************************************
* char *G__add_quotation(string)
*
* Called by
*
******************************************************************/
char *Cint::Internal::G__add_quotation(char *string,char *temp)
{
  int c;
  short i=0,l=0;
  temp[i++]='"';
  while((c=string[l++])!='\0') {
    switch(c) {
    case '\n':
      temp[i++]='\\';
      temp[i++]='n';
      break;
    case '\r':
      temp[i++]='\\';
      temp[i++]='r';
      break;
    case '\\':
      temp[i++]='\\';
      temp[i++]='\\';
      break;
    case '"':
      temp[i++]='\\';
      temp[i++]='"';
      break;
    default:
      temp[i++]=c;
      break;
    }
  }
  temp[i++]='"';
  temp[i]='\0';
  return(temp);
}

/******************************************************************
* char *G__tocharexpr(result7)
******************************************************************/
char *Cint::Internal::G__tocharexpr(char *result7)
{
  if((result7[0]=='\\')&&(result7[1]=='\'')) {
    G__charaddquote(result7,result7[2]);
  }
  return(NULL);
}

/****************************************************************
* char *G__string()
*
****************************************************************/
char *Cint::Internal::G__string(G__value buf,char *temp)
{
  G__StrBuf temp1_sb(G__MAXNAME);
  char *temp1 = temp1_sb;
  switch(G__get_type(G__value_typenum(buf))) {
  case '\0':
    temp[0]='\0'; /* sprintf(temp,""); */
    break;
  case 'C': /* string */
    if(buf.obj.i) {
      G__add_quotation((char *)G__int(buf),temp);
    }
    else {
      temp[0]='\0';
    }
    break;
  case 'd':
  case 'f':
    sprintf(temp,"%.17e",buf.obj.d);
    break;
  case 'w':
    G__logicstring(buf,1,temp1);
    sprintf(temp,"0b%s",temp1);
    break;
  default:
    sprintf(temp,"%ld",buf.obj.i);
    break;
  }
  return(temp);
}

/****************************************************************
* char *G__quotedstring()
*
****************************************************************/
char *Cint::Internal::G__quotedstring(char *buf,char *result)
{
        int i=0,r=0;
        int c;
        while((c=buf[i])) {
                switch(c) {
                case '\\':
                case '"':
                        result[r++] = '\\';
                        result[r++] = c;
                        break;
                default:
                        result[r++] = c;
                        break;
                }
                ++i;
        }
        result[r]='\0';
        return(result);
}

/****************************************************************
* char *G__logicstring()
*
****************************************************************/
char *Cint::Internal::G__logicstring(G__value buf,int dig,char *result)
{
        G__StrBuf tristate_sb(G__MAXNAME);
        char *tristate = tristate_sb;
        unsigned int hilo,hiz,i,ii,flag;
        switch(G__get_type(G__value_typenum(buf))) {
        case 'd': /* double */
        case 'f': /* float */
        case 'w': /* logic */
                hilo = buf.obj.i;
                hiz = *(&buf.obj.i+1);
                G__getbase(hilo,2,32,result);
                G__getbase(hiz,2,32,tristate);
                break;
        default:
                hilo = buf.obj.i;
                hiz = 0;
                G__getbase(hilo,2,32,result);
                G__getbase(hiz,2,32,tristate);
                break;
        }
        flag=0;
        ii=0;
        for(i=0;i<32;i++) {
                if((int)(32-i)<=dig) flag=1;
                switch(result[i]){
                case '0':
                        if(tristate[i]=='0') {
                                if(flag!=0) {
                                        result[ii++]='0';
                                }
                        }
                        else {
                                flag=1;
                                result[ii++]='x';
                        }
                        break;
                case '1':
                        flag=1;
                        if(tristate[i]=='0') {
                                result[ii++]='1';
                        }
                        else {
                                result[ii++]='z';
                        }
                        break;
                }
        }
        if(ii!=0) result[ii]='\0';
        else      result[1]='\0';
        return(result);
}

/*
 * Local Variables:
 * c-tab-always-indent:nil
 * c-indent-level:2
 * c-continued-statement-offset:2
 * c-brace-offset:-2
 * c-brace-imaginary-offset:0
 * c-argdecl-indent:0
 * c-label-offset:-2
 * compile-command:"make -k"
 * End:
 */
<|endoftext|>


<commit_before>#ifndef regex_impl_hh_INCLUDED
#define regex_impl_hh_INCLUDED

#include "unicode.hh"
#include "utf8.hh"
#include "utf8_iterator.hh"
#include "vector.hh"

namespace Kakoune
{

struct CompiledRegex
{
    enum Op : char
    {
        Match,
        Literal,
        LiteralIgnoreCase,
        AnyChar,
        Matcher,
        Jump,
        Split_PrioritizeParent,
        Split_PrioritizeChild,
        Save,
        LineStart,
        LineEnd,
        WordBoundary,
        NotWordBoundary,
        SubjectBegin,
        SubjectEnd,
        LookAhead,
        LookBehind,
        NegativeLookAhead,
        NegativeLookBehind,
    };

    using Offset = unsigned;
    static constexpr Offset search_prefix_size = 3 + 2 * sizeof(Offset);

    explicit operator bool() const { return not bytecode.empty(); }

    Vector<char> bytecode;
    Vector<std::function<bool (Codepoint)>> matchers;
    size_t save_count;
};

CompiledRegex compile_regex(StringView re);

template<typename Iterator>
struct ThreadedRegexVM
{
    ThreadedRegexVM(const CompiledRegex& program)
      : m_program{program} { kak_assert(m_program); }

    struct Thread
    {
        const char* inst;
        Vector<Iterator> saves = {};
    };

    enum class StepResult { Consumed, Matched, Failed };
    StepResult step(size_t thread_index)
    {
        const auto prog_start = m_program.bytecode.data();
        const auto prog_end = prog_start + m_program.bytecode.size();
        while (true)
        {
            auto& thread = m_threads[thread_index];
            const Codepoint cp = m_pos == m_end ? 0 : *m_pos;
            const CompiledRegex::Op op = (CompiledRegex::Op)*thread.inst++;
            switch (op)
            {
                case CompiledRegex::Literal:
                    if (utf8::read_codepoint(thread.inst, prog_end) == cp)
                        return StepResult::Consumed;
                    return StepResult::Failed;
                case CompiledRegex::LiteralIgnoreCase:
                    if (utf8::read_codepoint(thread.inst, prog_end) == to_lower(cp))
                        return StepResult::Consumed;
                    return StepResult::Failed;
                case CompiledRegex::AnyChar:
                    return StepResult::Consumed;
                case CompiledRegex::Jump:
                {
                    auto inst = prog_start + *reinterpret_cast<const CompiledRegex::Offset*>(thread.inst);
                    // if instruction is already going to be executed by another thread, drop this thread
                    if (std::find_if(m_threads.begin(), m_threads.end(),
                                     [inst](const Thread& t) { return t.inst == inst; }) != m_threads.end())
                        return StepResult::Failed;
                    thread.inst = inst;
                    break;
                }
                case CompiledRegex::Split_PrioritizeParent:
                {
                    add_thread(thread_index+1, *reinterpret_cast<const CompiledRegex::Offset*>(thread.inst), thread.saves);
                    // thread is invalidated now, as we mutated the m_thread vector
                    m_threads[thread_index].inst += sizeof(CompiledRegex::Offset);
                    break;
                }
                case CompiledRegex::Split_PrioritizeChild:
                {
                    add_thread(thread_index+1, thread.inst + sizeof(CompiledRegex::Offset) - prog_start, thread.saves);
                    // thread is invalidated now, as we mutated the m_thread vector
                    m_threads[thread_index].inst = prog_start + *reinterpret_cast<const CompiledRegex::Offset*>(m_threads[thread_index].inst);
                    break;
                }
                case CompiledRegex::Save:
                {
                    const char index = *thread.inst++;
                    thread.saves[index] = m_pos.base();
                    break;
                }
                case CompiledRegex::Matcher:
                {
                    const int matcher_id = *thread.inst++;
                    return m_program.matchers[matcher_id](*m_pos) ?
                        StepResult::Consumed : StepResult::Failed;
                }
                case CompiledRegex::LineStart:
                    if (not is_line_start())
                        return StepResult::Failed;
                    break;
                case CompiledRegex::LineEnd:
                    if (not is_line_end())
                        return StepResult::Failed;
                    break;
                case CompiledRegex::WordBoundary:
                    if (not is_word_boundary())
                        return StepResult::Failed;
                    break;
                case CompiledRegex::NotWordBoundary:
                    if (is_word_boundary())
                        return StepResult::Failed;
                    break;
                case CompiledRegex::SubjectBegin:
                    if (m_pos != m_begin)
                        return StepResult::Failed;
                    break;
                case CompiledRegex::SubjectEnd:
                    if (m_pos != m_end)
                        return StepResult::Failed;
                    break;
                case CompiledRegex::LookAhead:
                case CompiledRegex::NegativeLookAhead:
                {
                    int count = *thread.inst++;
                    for (auto it = m_pos; count and it != m_end; ++it, --count)
                        if (*it != utf8::read(thread.inst))
                            break;
                    if ((op == CompiledRegex::LookAhead and count != 0) or
                        (op == CompiledRegex::NegativeLookAhead and count == 0))
                        return StepResult::Failed;
                    thread.inst = utf8::advance(thread.inst, prog_end, CharCount{count - 1});
                    break;
                }
                case CompiledRegex::LookBehind:
                case CompiledRegex::NegativeLookBehind:
                {
                    int count = *thread.inst++;
                    for (auto it = m_pos-1; count and it >= m_begin; --it, --count)
                        if (*it != utf8::read(thread.inst))
                            break;
                    if ((op == CompiledRegex::LookBehind and count != 0) or
                        (op == CompiledRegex::NegativeLookBehind and count == 0))
                        return StepResult::Failed;
                    thread.inst = utf8::advance(thread.inst, prog_end, CharCount{count - 1});
                    break;
                }
                case CompiledRegex::Match:
                    return StepResult::Matched;
            }
        }
        return StepResult::Failed;
    }

    bool exec(Iterator begin, Iterator end, bool match = true, bool longest = false)
    {
        bool found_match = false;
        m_threads.clear();
        add_thread(0, match ? CompiledRegex::search_prefix_size : 0,
                   Vector<Iterator>(m_program.save_count, Iterator{}));

        m_begin = begin;
        m_end = end;

        for (m_pos = Utf8It{m_begin, m_begin, m_end}; m_pos != m_end; ++m_pos)
        {
            for (int i = 0; i < m_threads.size(); )
            {
                const auto res = step(i);
                if (res == StepResult::Matched)
                {
                    if (match)
                    {
                        m_threads.erase(m_threads.begin() + i);
                        continue; // We are not at end, this is not a full match
                    }

                    m_captures = std::move(m_threads[i].saves);
                    found_match = true;
                    m_threads.resize(i); // remove this and lower priority threads
                    if (not longest)
                        return true;
                }
                else if (res == StepResult::Failed)
                    m_threads.erase(m_threads.begin() + i);
                else
                {
                    auto it = m_threads.begin() + i;
                    if (std::find_if(m_threads.begin(), it, [inst = it->inst](auto& t)
                                     { return t.inst == inst; }) != it)
                        m_threads.erase(it);
                    else
                        ++i;
                }
            }
            // we should never have more than one thread on the same instruction
            kak_assert(m_threads.size() <= m_program.bytecode.size());
            if (m_threads.empty())
                return found_match;
        }

        // Step remaining threads to see if they match without consuming anything else
        for (int i = 0; i < m_threads.size(); ++i)
        {
            if (step(i) == StepResult::Matched)
            {
                m_captures = std::move(m_threads[i].saves);
                found_match = true;
                m_threads.resize(i); // remove this and lower priority threads
                if (not longest)
                    return true;
            }
        }
        return found_match;
    }

    void add_thread(int index, CompiledRegex::Offset pos, Vector<Iterator> saves)
    {
        const char* inst = m_program.bytecode.data() + pos;
        if (std::find_if(m_threads.begin(), m_threads.end(),
                         [inst](const Thread& t) { return t.inst == inst; }) == m_threads.end())
            m_threads.insert(m_threads.begin() + index, {inst, std::move(saves)});
        kak_assert(m_threads.size() < m_program.bytecode.size());
    }

    bool is_line_start() const
    {
        return m_pos == m_begin or *(m_pos-1) == '\n';
    }

    bool is_line_end() const
    {
        return m_pos == m_end or *m_pos == '\n';
    }

    bool is_word_boundary() const
    {
        return m_pos == m_begin or m_pos == m_end or
               is_word(*(m_pos-1)) != is_word(*m_pos);
    }

    const CompiledRegex& m_program;
    Vector<Thread> m_threads;

    using Utf8It = utf8::iterator<Iterator>;

    Iterator m_begin;
    Iterator m_end;
    Utf8It m_pos;

    Vector<Iterator> m_captures;
};

template<typename It>
bool regex_match(It begin, It end, const CompiledRegex& re)
{
    ThreadedRegexVM<It> vm{re};
    return vm.exec(begin, end, true, false);
}

template<typename It>
bool regex_match(It begin, It end, Vector<It>& captures, const CompiledRegex& re)
{
    ThreadedRegexVM<It> vm{re};
    if (vm.exec(begin, end, true, true))
    {
        captures = std::move(vm.m_captures);
        return true;
    }
    return false;
}

template<typename It>
bool regex_search(It begin, It end, const CompiledRegex& re)
{
    ThreadedRegexVM<It> vm{re};
    return vm.exec(begin, end, false, false);
}

template<typename It>
bool regex_search(It begin, It end, Vector<It>& captures, const CompiledRegex& re)
{
    ThreadedRegexVM<It> vm{re};
    if (vm.exec(begin, end, false, true))
    {
        captures = std::move(vm.m_captures);
        return true;
    }
    return false;
}

}

#endif // regex_impl_hh_INCLUDED
<commit_msg>Regex: small code tweak in ThreadedRegexVM<commit_after>#ifndef regex_impl_hh_INCLUDED
#define regex_impl_hh_INCLUDED

#include "unicode.hh"
#include "utf8.hh"
#include "utf8_iterator.hh"
#include "vector.hh"

namespace Kakoune
{

struct CompiledRegex
{
    enum Op : char
    {
        Match,
        Literal,
        LiteralIgnoreCase,
        AnyChar,
        Matcher,
        Jump,
        Split_PrioritizeParent,
        Split_PrioritizeChild,
        Save,
        LineStart,
        LineEnd,
        WordBoundary,
        NotWordBoundary,
        SubjectBegin,
        SubjectEnd,
        LookAhead,
        LookBehind,
        NegativeLookAhead,
        NegativeLookBehind,
    };

    using Offset = unsigned;
    static constexpr Offset search_prefix_size = 3 + 2 * sizeof(Offset);

    explicit operator bool() const { return not bytecode.empty(); }

    Vector<char> bytecode;
    Vector<std::function<bool (Codepoint)>> matchers;
    size_t save_count;
};

CompiledRegex compile_regex(StringView re);

template<typename Iterator>
struct ThreadedRegexVM
{
    ThreadedRegexVM(const CompiledRegex& program)
      : m_program{program} { kak_assert(m_program); }

    struct Thread
    {
        const char* inst;
        Vector<Iterator> saves = {};
    };

    enum class StepResult { Consumed, Matched, Failed };
    StepResult step(size_t thread_index)
    {
        const auto prog_start = m_program.bytecode.data();
        const auto prog_end = prog_start + m_program.bytecode.size();
        while (true)
        {
            auto& thread = m_threads[thread_index];
            const Codepoint cp = m_pos == m_end ? 0 : *m_pos;
            const CompiledRegex::Op op = (CompiledRegex::Op)*thread.inst++;
            switch (op)
            {
                case CompiledRegex::Literal:
                    if (utf8::read_codepoint(thread.inst, prog_end) == cp)
                        return StepResult::Consumed;
                    return StepResult::Failed;
                case CompiledRegex::LiteralIgnoreCase:
                    if (utf8::read_codepoint(thread.inst, prog_end) == to_lower(cp))
                        return StepResult::Consumed;
                    return StepResult::Failed;
                case CompiledRegex::AnyChar:
                    return StepResult::Consumed;
                case CompiledRegex::Jump:
                {
                    auto inst = prog_start + *reinterpret_cast<const CompiledRegex::Offset*>(thread.inst);
                    // if instruction is already going to be executed by another thread, drop this thread
                    if (std::find_if(m_threads.begin(), m_threads.end(),
                                     [inst](const Thread& t) { return t.inst == inst; }) != m_threads.end())
                        return StepResult::Failed;
                    thread.inst = inst;
                    break;
                }
                case CompiledRegex::Split_PrioritizeParent:
                {
                    auto new_thread_inst = prog_start + *reinterpret_cast<const CompiledRegex::Offset*>(thread.inst);
                    thread.inst += sizeof(CompiledRegex::Offset);
                    add_thread(thread_index+1, new_thread_inst, thread.saves);
                    break;
                }
                case CompiledRegex::Split_PrioritizeChild:
                {
                    auto new_thread_inst = thread.inst + sizeof(CompiledRegex::Offset);
                    thread.inst = prog_start + *reinterpret_cast<const CompiledRegex::Offset*>(thread.inst);
                    add_thread(thread_index+1, new_thread_inst, thread.saves);
                    break;
                }
                case CompiledRegex::Save:
                {
                    const char index = *thread.inst++;
                    thread.saves[index] = m_pos.base();
                    break;
                }
                case CompiledRegex::Matcher:
                {
                    const int matcher_id = *thread.inst++;
                    return m_program.matchers[matcher_id](*m_pos) ?
                        StepResult::Consumed : StepResult::Failed;
                }
                case CompiledRegex::LineStart:
                    if (not is_line_start())
                        return StepResult::Failed;
                    break;
                case CompiledRegex::LineEnd:
                    if (not is_line_end())
                        return StepResult::Failed;
                    break;
                case CompiledRegex::WordBoundary:
                    if (not is_word_boundary())
                        return StepResult::Failed;
                    break;
                case CompiledRegex::NotWordBoundary:
                    if (is_word_boundary())
                        return StepResult::Failed;
                    break;
                case CompiledRegex::SubjectBegin:
                    if (m_pos != m_begin)
                        return StepResult::Failed;
                    break;
                case CompiledRegex::SubjectEnd:
                    if (m_pos != m_end)
                        return StepResult::Failed;
                    break;
                case CompiledRegex::LookAhead:
                case CompiledRegex::NegativeLookAhead:
                {
                    int count = *thread.inst++;
                    for (auto it = m_pos; count and it != m_end; ++it, --count)
                        if (*it != utf8::read(thread.inst))
                            break;
                    if ((op == CompiledRegex::LookAhead and count != 0) or
                        (op == CompiledRegex::NegativeLookAhead and count == 0))
                        return StepResult::Failed;
                    thread.inst = utf8::advance(thread.inst, prog_end, CharCount{count - 1});
                    break;
                }
                case CompiledRegex::LookBehind:
                case CompiledRegex::NegativeLookBehind:
                {
                    int count = *thread.inst++;
                    for (auto it = m_pos-1; count and it >= m_begin; --it, --count)
                        if (*it != utf8::read(thread.inst))
                            break;
                    if ((op == CompiledRegex::LookBehind and count != 0) or
                        (op == CompiledRegex::NegativeLookBehind and count == 0))
                        return StepResult::Failed;
                    thread.inst = utf8::advance(thread.inst, prog_end, CharCount{count - 1});
                    break;
                }
                case CompiledRegex::Match:
                    return StepResult::Matched;
            }
        }
        return StepResult::Failed;
    }

    bool exec(Iterator begin, Iterator end, bool match = true, bool longest = false)
    {
        bool found_match = false;
        m_threads.clear();
        const auto start_offset = (match ? CompiledRegex::search_prefix_size : 0);
        add_thread(0, m_program.bytecode.data() + start_offset,
                   Vector<Iterator>(m_program.save_count, Iterator{}));

        m_begin = begin;
        m_end = end;

        for (m_pos = Utf8It{m_begin, m_begin, m_end}; m_pos != m_end; ++m_pos)
        {
            for (int i = 0; i < m_threads.size(); )
            {
                const auto res = step(i);
                if (res == StepResult::Matched)
                {
                    if (match)
                    {
                        m_threads.erase(m_threads.begin() + i);
                        continue; // We are not at end, this is not a full match
                    }

                    m_captures = std::move(m_threads[i].saves);
                    found_match = true;
                    m_threads.resize(i); // remove this and lower priority threads
                    if (not longest)
                        return true;
                }
                else if (res == StepResult::Failed)
                    m_threads.erase(m_threads.begin() + i);
                else
                {
                    auto it = m_threads.begin() + i;
                    if (std::find_if(m_threads.begin(), it, [inst = it->inst](auto& t)
                                     { return t.inst == inst; }) != it)
                        m_threads.erase(it);
                    else
                        ++i;
                }
            }
            // we should never have more than one thread on the same instruction
            kak_assert(m_threads.size() <= m_program.bytecode.size());
            if (m_threads.empty())
                return found_match;
        }

        // Step remaining threads to see if they match without consuming anything else
        for (int i = 0; i < m_threads.size(); ++i)
        {
            if (step(i) == StepResult::Matched)
            {
                m_captures = std::move(m_threads[i].saves);
                found_match = true;
                m_threads.resize(i); // remove this and lower priority threads
                if (not longest)
                    return true;
            }
        }
        return found_match;
    }

    void add_thread(int index, const char* inst, Vector<Iterator> saves)
    {
        if (std::find_if(m_threads.begin(), m_threads.end(),
                         [inst](const Thread& t) { return t.inst == inst; }) == m_threads.end())
            m_threads.insert(m_threads.begin() + index, {inst, std::move(saves)});
        kak_assert(m_threads.size() < m_program.bytecode.size());
    }

    bool is_line_start() const
    {
        return m_pos == m_begin or *(m_pos-1) == '\n';
    }

    bool is_line_end() const
    {
        return m_pos == m_end or *m_pos == '\n';
    }

    bool is_word_boundary() const
    {
        return m_pos == m_begin or m_pos == m_end or
               is_word(*(m_pos-1)) != is_word(*m_pos);
    }

    const CompiledRegex& m_program;
    Vector<Thread> m_threads;

    using Utf8It = utf8::iterator<Iterator>;

    Iterator m_begin;
    Iterator m_end;
    Utf8It m_pos;

    Vector<Iterator> m_captures;
};

template<typename It>
bool regex_match(It begin, It end, const CompiledRegex& re)
{
    ThreadedRegexVM<It> vm{re};
    return vm.exec(begin, end, true, false);
}

template<typename It>
bool regex_match(It begin, It end, Vector<It>& captures, const CompiledRegex& re)
{
    ThreadedRegexVM<It> vm{re};
    if (vm.exec(begin, end, true, true))
    {
        captures = std::move(vm.m_captures);
        return true;
    }
    return false;
}

template<typename It>
bool regex_search(It begin, It end, const CompiledRegex& re)
{
    ThreadedRegexVM<It> vm{re};
    return vm.exec(begin, end, false, false);
}

template<typename It>
bool regex_search(It begin, It end, Vector<It>& captures, const CompiledRegex& re)
{
    ThreadedRegexVM<It> vm{re};
    if (vm.exec(begin, end, false, true))
    {
        captures = std::move(vm.m_captures);
        return true;
    }
    return false;
}

}

#endif // regex_impl_hh_INCLUDED
<|endoftext|>


<commit_before>/*
 *       Filename:  to_string.hpp
 *
 *    Description:  definition of a string convertion
 *
 *        Version:  1.0
 *        Created:  02/14/2008 06:55:12 PM
 *
 *         Author:  Jan Funke, TU Dresden
 */

#include <cassert>
#include <vector>
#include <set>
#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>

namespace util {

template<class T>
inline std::string to_string(const T& value) {

  std::ostringstream streamOut;
  streamOut << value;

  return streamOut.str();
}

template<class T>
inline std::string to_string_with_leading_zeros(const T& value, unsigned num_zeros) {

  std::ostringstream streamOut;
  streamOut << std::setw(num_zeros) << std::setfill('0') << value;

  return streamOut.str();
}

template<class T>
inline T from_string(const std::string& string) {

  std::istringstream streamIn(string);
  T value;

  streamIn >> value;

  return value;
}

} // namespace util

template<class T, class U>
std::vector<T> operator+(std::vector<T> lhs, std::vector<U> rhs) {

  assert(lhs.size() == rhs.size());
  std::vector<T> result;
  for (int i = 0; i < lhs.size(); i++)
    result.push_back(lhs[i] + (T)rhs[i]);
  return result;
}
//
//template<class T, class U>
//std::vector<T> operator+(std::vector<T>& lhs, std::vector<U>& rhs) {
//
//  assert(lhs.size() == rhs.size());
//  std::vector<T> result;
//  for (int i = 0; i < lhs.size(); i++)
//    result.push_back(lhs[i] + rhs[i]);
//  return result;
//}

template<class T, class U>
std::vector<T>& operator+=(std::vector<T>& lhs, std::vector<U>& rhs) {

  assert(lhs.size() == rhs.size());
  for (int i = 0; i < lhs.size(); i++)
    lhs[i] += rhs[i];
  return lhs;
}

template<class T, class U>
std::vector<T> operator-(std::vector<T> lhs, std::vector<U> rhs) {

  assert(lhs.size() == rhs.size());
  std::vector<T> result;
  for (int i = 0; i < lhs.size(); i++)
    result.push_back(lhs[i] - rhs[i]);
  return result;
}

template<class T, class U>
std::vector<T>& operator-=(std::vector<T>& lhs, std::vector<U>& rhs) {

  assert(lhs.size() == rhs.size());
  for (int i = 0; i < lhs.size(); i++)
    lhs[i] -= rhs[i];
  return lhs;
}

template<class T>
T operator*(std::vector<T>& lhs, std::vector<T>& rhs) {

  assert(lhs.size() == rhs.size());
  T result = 0.0;
  for (int i = 0; i < lhs.size(); i++)
    result += lhs[i] * rhs[i];
  return result;
}

template<class T, class U>
std::vector<T> operator*(std::vector<T> lhs, U rhs) {

  for (int i = 0; i < lhs.size(); i++)
    lhs[i] *= rhs;
  return lhs;
}

template<class T, class U>
std::vector<T> operator*(std::vector<T>& lhs, U rhs) {

  for (int i = 0; i < lhs.size(); i++)
    lhs[i] *= rhs;
  return lhs;
}

template<class T, class U>
std::vector<T>& operator*=(std::vector<T>& lhs, U rhs) {

  for (int i = 0; i < lhs.size(); i++)
    lhs[i] *= rhs;
  return lhs;
}

template<class T, class U>
std::vector<T> operator/(std::vector<T>& lhs, U& rhs) {

  std::vector<T> result;
  for (int i = 0; i < lhs.size(); i++)
    result.push_back(lhs[i] / rhs);
  return result;
}

template<class T, class U>
std::vector<T> operator/(std::vector<T>& lhs, U rhs) {

  std::vector<T> result;
  for (int i = 0; i < lhs.size(); i++)
    result.push_back(lhs[i] / rhs);
  return result;
}

template<class T, class U>
std::vector<T>& operator/=(std::vector<T>& lhs, U& rhs) {

  for (int i = 0; i < lhs.size(); i++)
    lhs[i] /= rhs;
  return lhs;
}

template<class T, class U>
std::vector<T>& operator/=(std::vector<T>& lhs, U rhs) {

  for (int i = 0; i < lhs.size(); i++)
    lhs[i] /= rhs;
  return lhs;
}

namespace std {

template<class T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& vector) {

  os << "[";

  for (unsigned int i = 0; i < vector.size(); i++) {
      os << vector[i];
      if (i != vector.size() - 1)
        os << ", ";
  }

  os << "]";

  return os;
}

template<class T>
std::ostream& operator<<(std::ostream& os, const std::set<T>& set) {

  os << "{";

  typename std::set<T>::const_iterator i;
  for (i = set.begin(); i != set.end(); i++) {
      if (i != set.begin())
        os << ", ";
      os << *i;
  }

  os << "}";

  return os;
}

} // namespace std

<commit_msg>added include guard to helpers.hpp<commit_after>#ifndef UTIL_HELPERS_H__
#define UTIL_HELPERS_H__

/*
 *       Filename:  to_string.hpp
 *
 *    Description:  definition of a string convertion
 *
 *        Version:  1.0
 *        Created:  02/14/2008 06:55:12 PM
 *
 *         Author:  Jan Funke, TU Dresden
 */

#include <cassert>
#include <vector>
#include <set>
#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>

namespace util {

template<class T>
inline std::string to_string(const T& value) {

  std::ostringstream streamOut;
  streamOut << value;

  return streamOut.str();
}

template<class T>
inline std::string to_string_with_leading_zeros(const T& value, unsigned num_zeros) {

  std::ostringstream streamOut;
  streamOut << std::setw(num_zeros) << std::setfill('0') << value;

  return streamOut.str();
}

template<class T>
inline T from_string(const std::string& string) {

  std::istringstream streamIn(string);
  T value;

  streamIn >> value;

  return value;
}

} // namespace util

template<class T, class U>
std::vector<T> operator+(std::vector<T> lhs, std::vector<U> rhs) {

  assert(lhs.size() == rhs.size());
  std::vector<T> result;
  for (int i = 0; i < lhs.size(); i++)
    result.push_back(lhs[i] + (T)rhs[i]);
  return result;
}
//
//template<class T, class U>
//std::vector<T> operator+(std::vector<T>& lhs, std::vector<U>& rhs) {
//
//  assert(lhs.size() == rhs.size());
//  std::vector<T> result;
//  for (int i = 0; i < lhs.size(); i++)
//    result.push_back(lhs[i] + rhs[i]);
//  return result;
//}

template<class T, class U>
std::vector<T>& operator+=(std::vector<T>& lhs, std::vector<U>& rhs) {

  assert(lhs.size() == rhs.size());
  for (int i = 0; i < lhs.size(); i++)
    lhs[i] += rhs[i];
  return lhs;
}

template<class T, class U>
std::vector<T> operator-(std::vector<T> lhs, std::vector<U> rhs) {

  assert(lhs.size() == rhs.size());
  std::vector<T> result;
  for (int i = 0; i < lhs.size(); i++)
    result.push_back(lhs[i] - rhs[i]);
  return result;
}

template<class T, class U>
std::vector<T>& operator-=(std::vector<T>& lhs, std::vector<U>& rhs) {

  assert(lhs.size() == rhs.size());
  for (int i = 0; i < lhs.size(); i++)
    lhs[i] -= rhs[i];
  return lhs;
}

template<class T>
T operator*(std::vector<T>& lhs, std::vector<T>& rhs) {

  assert(lhs.size() == rhs.size());
  T result = 0.0;
  for (int i = 0; i < lhs.size(); i++)
    result += lhs[i] * rhs[i];
  return result;
}

template<class T, class U>
std::vector<T> operator*(std::vector<T> lhs, U rhs) {

  for (int i = 0; i < lhs.size(); i++)
    lhs[i] *= rhs;
  return lhs;
}

template<class T, class U>
std::vector<T> operator*(std::vector<T>& lhs, U rhs) {

  for (int i = 0; i < lhs.size(); i++)
    lhs[i] *= rhs;
  return lhs;
}

template<class T, class U>
std::vector<T>& operator*=(std::vector<T>& lhs, U rhs) {

  for (int i = 0; i < lhs.size(); i++)
    lhs[i] *= rhs;
  return lhs;
}

template<class T, class U>
std::vector<T> operator/(std::vector<T>& lhs, U& rhs) {

  std::vector<T> result;
  for (int i = 0; i < lhs.size(); i++)
    result.push_back(lhs[i] / rhs);
  return result;
}

template<class T, class U>
std::vector<T> operator/(std::vector<T>& lhs, U rhs) {

  std::vector<T> result;
  for (int i = 0; i < lhs.size(); i++)
    result.push_back(lhs[i] / rhs);
  return result;
}

template<class T, class U>
std::vector<T>& operator/=(std::vector<T>& lhs, U& rhs) {

  for (int i = 0; i < lhs.size(); i++)
    lhs[i] /= rhs;
  return lhs;
}

template<class T, class U>
std::vector<T>& operator/=(std::vector<T>& lhs, U rhs) {

  for (int i = 0; i < lhs.size(); i++)
    lhs[i] /= rhs;
  return lhs;
}

namespace std {

template<class T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& vector) {

  os << "[";

  for (unsigned int i = 0; i < vector.size(); i++) {
      os << vector[i];
      if (i != vector.size() - 1)
        os << ", ";
  }

  os << "]";

  return os;
}

template<class T>
std::ostream& operator<<(std::ostream& os, const std::set<T>& set) {

  os << "{";

  typename std::set<T>::const_iterator i;
  for (i = set.begin(); i != set.end(); i++) {
      if (i != set.begin())
        os << ", ";
      os << *i;
  }

  os << "}";

  return os;
}

} // namespace std

#endif // UTIL_HELPERS_H__

<|endoftext|>


<commit_before>#include "ros/ros.h"
#include "sensor_msgs/Joy.h"
#include "jaws_msgs/Thrusters.h"

const float MAX_THRUST = 500.0;

class Controls
{
  private:
    ros::NodeHandle nh;
    ros::NodeHandle parameters;
    ros::Publisher pub;
    ros::Subscriber sub;
    jaws_msgs::Thrusters thrusters;
    int refresh_rate;
    double aft_thrust_mult;
    double stbd_thrust_mult;
    double port_thrust_mult;

  public:
    Controls() : nh()
    {
      nh.param<int>("/controls_node/controls_refresh_rate", refresh_rate, 10);
      ROS_INFO("Refresh rate: %i", refresh_rate);

      nh.param<double>("/controls_node/aft_thrust_multiplier", aft_thrust_mult, 1.0);
      ROS_INFO("Aft multiplier: %f", aft_thrust_mult);
      nh.param<double>("/controls_node/port_thrust_multiplier", port_thrust_mult, 1.0);
      ROS_INFO("Port multiplier: %f", port_thrust_mult);
      nh.param<double>("/controls_node/stbd_thrust_multiplier", stbd_thrust_mult, 1.0);
      ROS_INFO("Starboard multiplier: %f", stbd_thrust_mult);

      sub = nh.subscribe<sensor_msgs::Joy>("joy", 1, &Controls::callback, this);
      pub = nh.advertise<jaws_msgs::Thrusters>("thrusters", 1);
    }

    void callback(const sensor_msgs::Joy::ConstPtr& joy)
    {
      float raw_thrust = joy->axes[1] * MAX_THRUST;
      float stbd_power = stbd_thrust_mult * (raw_thrust * raw_thrust * raw_thrust) / (MAX_THRUST * MAX_THRUST);
      float port_power = port_thrust_mult * (raw_thrust * raw_thrust * raw_thrust) / (MAX_THRUST * MAX_THRUST);
      float stbd_yaw = 1 + joy->axes[13];
      float port_yaw = 1 + joy->axes[12];

      float aft_thrust = joy->axes[3] * MAX_THRUST;
      float aft_power = (aft_thrust * aft_thrust * aft_thrust) / (MAX_THRUST * MAX_THRUST);

      thrusters.stbd_angle = (int)(90 + joy->axes[2] * 90);
      thrusters.port_angle = (int)(90 + joy->axes[2] * 90);
      thrusters.aft_power = (int)(1500 + aft_power);
      thrusters.stbd_power = (int)(1500 + stbd_power * stbd_yaw);
      thrusters.port_power = (int)(1500 + port_power * port_yaw);

      if(joy->buttons[4]){
	stbd_thrust_mult+=0.001;
      }
      else if(joy->buttons[5]){
 	stbd_thrust_mult-=0.001;
      }
      if(joy->buttons[6]){
	port_thrust_mult+=0.001;
      }
      else if(joy->buttons[7]){
	port_thrust_mult-=0.001;
      }

      ROS_INFO("Current PTM: %4f - Current STM: %4f",port_thrust_mult,stbd_thrust_mult);
      pub.publish(thrusters);

    }

    void loop()
    {
      ros::Rate rate(refresh_rate);
      while(ros::ok())
      {
        ros::spinOnce();
        rate.sleep();
      }
    }
};

int main(int argc, char **argv)
{
  ros::init(argc, argv, "controls_node");
  Controls controls;
  controls.loop();
}
<commit_msg>Refactored code.<commit_after>#include "ros/ros.h"
#include "sensor_msgs/Joy.h"
#include "jaws_msgs/Thrusters.h"

#define THRUSTER_CALIBRATION

class Controls
{
  private:
    ros::NodeHandle nh;
    ros::NodeHandle parameters;
    ros::Publisher pub;
    ros::Subscriber sub;
    jaws_msgs::Thrusters thrusters;
    int refresh_rate;
    double aft_cal;
    double port_cal;
    double stbd_cal;
    float neut_power;
    float neut_angle;
    float max_power;
    float max_angle;

    float curved_power(float raw, float max)
    {
      return (raw * raw * raw) / (max * max);
    }

  public:
    Controls() : nh()
    {
      max_power = 500.0;
      max_angle = 90.0;
      neut_power = 1500.0;
      neut_angle = 90.0;

      nh.param<int>("/controls_node/controls_refresh_rate", refresh_rate, 10);
      ROS_INFO("Refresh rate: %i", refresh_rate);

      nh.param<double>("/controls_node/aft_thrust_multiplier", aft_cal, 1.0);
      ROS_INFO("Aft cal. factor: %f", aft_cal);
      nh.param<double>("/controls_node/port_thrust_multiplier", port_cal, 1.0);
      ROS_INFO("Port cal. factor: %f", port_cal);
      nh.param<double>("/controls_node/stbd_thrust_multiplier", stbd_cal, 1.0);
      ROS_INFO("Stbd cal. factor: %f", stbd_cal);

      sub = nh.subscribe<sensor_msgs::Joy>("joy", 1, &Controls::callback, this);
      pub = nh.advertise<jaws_msgs::Thrusters>("thrusters", 1);
    }

    void callback(const sensor_msgs::Joy::ConstPtr& joy)
    {
      float port_angle = joy->axes[2] * max_angle;
      float stbd_angle = joy->axes[2] * max_angle;
      float aft_power = joy->axes[3] * max_power * (float)aft_cal;
      float port_power = joy->axes[1] * max_power * (float)port_cal;
      float stbd_power = joy->axes[1] * max_power * (float)stbd_cal;

      float port_yaw = 1.0 + joy->axes[12];
      float stbd_yaw = 1.0 + joy->axes[13];

      aft_power = curved_power(aft_power, max_power);
      port_power = curved_power(port_power, max_power) * port_yaw;
      stbd_power = curved_power(stbd_power, max_power) * stbd_yaw;

      thrusters.port_angle = (int)(neut_angle + port_angle);
      thrusters.stbd_angle = (int)(neut_angle + stbd_angle);
      thrusters.aft_power = (int)(neut_power + aft_power);
      thrusters.port_power = (int)(neut_power + port_power);
      thrusters.stbd_power = (int)(neut_power + stbd_power);

#ifdef THRUSTER_CALIBRATION
      if(joy->buttons[6])
      {
 	aft_cal -= 0.001;
      }
      if(joy->buttons[8])
      {
	port_cal -= 0.001;
      }
      else if(joy->buttons[7])
      {
	stbd_cal -= 0.001;
      }
      ROS_INFO("Aft cal. factor: %4f", aft_cal);
      ROS_INFO("Port cal. factor: %4f", port_cal);
      ROS_INFO("Stbd cal. factor: %4f", stbd_cal);
#endif

      pub.publish(thrusters);
    }

    void loop()
    {
      ros::Rate rate(refresh_rate);
      while(ros::ok())
      {
        ros::spinOnce();
        rate.sleep();
      }
    }
};

int main(int argc, char **argv)
{
  ros::init(argc, argv, "controls_node");
  Controls controls;
  controls.loop();
}
<|endoftext|>