<commit_before>/*
  ESP8266WebServer.cpp - Dead simple web-server.
  Supports only one simultaneous client, knows how to handle GET and POST.

  Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
*/

#include <Arduino.h>
#include "WiFiServer.h"
#include "WiFiClient.h"
#include "ESP8266WebServer.h"

// #define DEBUG
#define DEBUG_OUTPUT Serial

struct ESP8266WebServer::RequestHandler {
  RequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method)
  : fn(fn)
  , uri(uri)
  , method(method)
  , next(NULL)
  {
  }

  ESP8266WebServer::THandlerFunction fn;
  String uri;
  HTTPMethod method;
  RequestHandler* next;

};

ESP8266WebServer::ESP8266WebServer(int port)
: _server(port)
, _firstHandler(0)
, _lastHandler(0)
, _currentArgCount(0)
, _currentArgs(0)
{
}

ESP8266WebServer::~ESP8266WebServer()
{
  if (!_firstHandler)
    return;
  RequestHandler* handler = _firstHandler;
  while (handler) {
    RequestHandler* next = handler->next;
    delete handler;
    handler = next;
  }
}

void ESP8266WebServer::begin() {
  _server.begin();
}

void ESP8266WebServer::on(const char* uri, ESP8266WebServer::THandlerFunction handler)
{
  on(uri, HTTP_ANY, handler);
}

void ESP8266WebServer::on(const char* uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn)
{
  RequestHandler* handler = new RequestHandler(fn, uri, method);
  if (!_lastHandler) {
    _firstHandler = handler;
    _lastHandler = handler;
  }
  else {
    _lastHandler->next = handler;
    _lastHandler = handler;
  }
}

void ESP8266WebServer::handleClient()
{
  WiFiClient client = _server.available();
  if (!client) {
    return;
  }

#ifdef DEBUG
  DEBUG_OUTPUT.println("New client");
#endif

  // Wait for data from client to become available
  uint16_t maxWait = HTTP_MAX_DATA_WAIT;
  while(client.connected() && !client.available() && maxWait--){
    delay(1);
  }

  if (!_parseRequest(client)) {
    return;
  }

  _currentClient = client;
  _contentLength = CONTENT_LENGTH_NOT_SET;
  _handleRequest();
}

void ESP8266WebServer::sendHeader(String name, String value, bool first) {
  String headerLine = name;
  headerLine += ": ";
  headerLine += value;
  headerLine += "\r\n";

  if (first) {
    _responseHeaders = headerLine + _responseHeaders;
  }
  else {
    _responseHeaders += headerLine;
  }
}

void ESP8266WebServer::send(int code, const char* content_type, String content) {
  String response = "HTTP/1.1 ";
  response += String(code);
  response += " ";
  response += _responseCodeToString(code);
  response += "\r\n";

  if (!content_type)
    content_type = "text/html";

  sendHeader("Content-Type", content_type, true);
  if (_contentLength != CONTENT_LENGTH_UNKNOWN) {
    size_t length = (_contentLength == CONTENT_LENGTH_NOT_SET) ?
                    content.length() : _contentLength;
    String lengthStr(length);
    sendHeader("Content-Length", lengthStr.c_str());
  }
  sendHeader("Connection", "close");
  sendHeader("Access-Control-Allow-Origin", "*");

  response += _responseHeaders;
  response += "\r\n";
  response += content;
  _responseHeaders = String();
  sendContent(response);
}

void ESP8266WebServer::send(int code, char* content_type, String content) {
  send(code, (const char*)content_type, content);
}

void ESP8266WebServer::send(int code, String content_type, String content) {
  send(code, (const char*)content_type.c_str(), content);
}

void ESP8266WebServer::sendContent(String content) {
  size_t size_to_send = content.length();
  size_t size_sent = 0;
  while(size_to_send) {
    const size_t unit_size = HTTP_DOWNLOAD_UNIT_SIZE;
    size_t will_send = (size_to_send < unit_size) ? size_to_send : unit_size;
    size_t sent = _currentClient.write(content.c_str() + size_sent, will_send);
    size_to_send -= sent;
    size_sent += sent;
    if (sent == 0) {
      break;
    }
  }
}

String ESP8266WebServer::arg(const char* name) {
  for (int i = 0; i < _currentArgCount; ++i) {
    if (_currentArgs[i].key == name)
      return _currentArgs[i].value;
  }
  return String();
}

String ESP8266WebServer::arg(int i) {
  if (i < _currentArgCount)
    return _currentArgs[i].value;
  return String();
}

String ESP8266WebServer::argName(int i) {
  if (i < _currentArgCount)
    return _currentArgs[i].key;
  return String();
}

int ESP8266WebServer::args() {
  return _currentArgCount;
}

bool ESP8266WebServer::hasArg(const char* name) {
  for (int i = 0; i < _currentArgCount; ++i) {
    if (_currentArgs[i].key == name)
      return true;
  }
  return false;
}

void ESP8266WebServer::onFileUpload(THandlerFunction fn) {
  _fileUploadHandler = fn;
}

void ESP8266WebServer::onNotFound(THandlerFunction fn) {
  _notFoundHandler = fn;
}

void ESP8266WebServer::_handleRequest() {
  RequestHandler* handler;
  for (handler = _firstHandler; handler; handler = handler->next)
  {
    if (handler->method != HTTP_ANY && handler->method != _currentMethod)
      continue;

    if (handler->uri != _currentUri)
      continue;

    handler->fn();
    break;
  }

  if (!handler){
#ifdef DEBUG
    DEBUG_OUTPUT.println("request handler not found");
#endif

    if(_notFoundHandler) {
      _notFoundHandler();
    }
    else {
      send(404, "text/plain", String("Not found: ") + _currentUri);
    }
  }

  uint16_t maxWait = HTTP_MAX_CLOSE_WAIT;
  while(_currentClient.connected() && maxWait--) {
    delay(1);
  }
  _currentClient   = WiFiClient();
  _currentUri      = String();
}

const char* ESP8266WebServer::_responseCodeToString(int code) {
  switch (code) {
    case 101: return "Switching Protocols";
    case 200: return "OK";
    case 403: return "Forbidden";
    case 404: return "Not found";
    case 500: return "Fail";
    default:  return "";
  }
}
<commit_msg>allow proper content length handling<commit_after>/*
  ESP8266WebServer.cpp - Dead simple web-server.
  Supports only one simultaneous client, knows how to handle GET and POST.

  Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
*/

#include <Arduino.h>
#include "WiFiServer.h"
#include "WiFiClient.h"
#include "ESP8266WebServer.h"

// #define DEBUG
#define DEBUG_OUTPUT Serial

struct ESP8266WebServer::RequestHandler {
  RequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method)
  : fn(fn)
  , uri(uri)
  , method(method)
  , next(NULL)
  {
  }

  ESP8266WebServer::THandlerFunction fn;
  String uri;
  HTTPMethod method;
  RequestHandler* next;

};

ESP8266WebServer::ESP8266WebServer(int port)
: _server(port)
, _firstHandler(0)
, _lastHandler(0)
, _currentArgCount(0)
, _currentArgs(0)
{
}

ESP8266WebServer::~ESP8266WebServer()
{
  if (!_firstHandler)
    return;
  RequestHandler* handler = _firstHandler;
  while (handler) {
    RequestHandler* next = handler->next;
    delete handler;
    handler = next;
  }
}

void ESP8266WebServer::begin() {
  _server.begin();
}

void ESP8266WebServer::on(const char* uri, ESP8266WebServer::THandlerFunction handler)
{
  on(uri, HTTP_ANY, handler);
}

void ESP8266WebServer::on(const char* uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn)
{
  RequestHandler* handler = new RequestHandler(fn, uri, method);
  if (!_lastHandler) {
    _firstHandler = handler;
    _lastHandler = handler;
  }
  else {
    _lastHandler->next = handler;
    _lastHandler = handler;
  }
}

void ESP8266WebServer::handleClient()
{
  WiFiClient client = _server.available();
  if (!client) {
    return;
  }

#ifdef DEBUG
  DEBUG_OUTPUT.println("New client");
#endif

  // Wait for data from client to become available
  uint16_t maxWait = HTTP_MAX_DATA_WAIT;
  while(client.connected() && !client.available() && maxWait--){
    delay(1);
  }

  if (!_parseRequest(client)) {
    return;
  }

  _currentClient = client;
  _contentLength = CONTENT_LENGTH_NOT_SET;
  _handleRequest();
}

void ESP8266WebServer::sendHeader(String name, String value, bool first) {
  String headerLine = name;
  headerLine += ": ";
  headerLine += value;
  headerLine += "\r\n";

  if (first) {
    _responseHeaders = headerLine + _responseHeaders;
  }
  else {
    _responseHeaders += headerLine;
  }
}

void ESP8266WebServer::send(int code, const char* content_type, String content) {
  String response = "HTTP/1.1 ";
  response += String(code);
  response += " ";
  response += _responseCodeToString(code);
  response += "\r\n";

  if (!content_type)
    content_type = "text/html";

  sendHeader("Content-Type", content_type, true);
  if (_contentLength != CONTENT_LENGTH_UNKNOWN && _contentLength != CONTENT_LENGTH_NOT_SET) {
    sendHeader("Content-Length", String(_contentLength).c_str());
  } else if(content.length() > 0){
    sendHeader("Content-Length", String(content.length()).c_str());
  }
  sendHeader("Connection", "close");
  sendHeader("Access-Control-Allow-Origin", "*");

  response += _responseHeaders;
  response += "\r\n";
  response += content;
  _responseHeaders = String();
  sendContent(response);
}

void ESP8266WebServer::send(int code, char* content_type, String content) {
  send(code, (const char*)content_type, content);
}

void ESP8266WebServer::send(int code, String content_type, String content) {
  send(code, (const char*)content_type.c_str(), content);
}

void ESP8266WebServer::sendContent(String content) {
  size_t size_to_send = content.length();
  size_t size_sent = 0;
  while(size_to_send) {
    const size_t unit_size = HTTP_DOWNLOAD_UNIT_SIZE;
    size_t will_send = (size_to_send < unit_size) ? size_to_send : unit_size;
    size_t sent = _currentClient.write(content.c_str() + size_sent, will_send);
    size_to_send -= sent;
    size_sent += sent;
    if (sent == 0) {
      break;
    }
  }
}

String ESP8266WebServer::arg(const char* name) {
  for (int i = 0; i < _currentArgCount; ++i) {
    if (_currentArgs[i].key == name)
      return _currentArgs[i].value;
  }
  return String();
}

String ESP8266WebServer::arg(int i) {
  if (i < _currentArgCount)
    return _currentArgs[i].value;
  return String();
}

String ESP8266WebServer::argName(int i) {
  if (i < _currentArgCount)
    return _currentArgs[i].key;
  return String();
}

int ESP8266WebServer::args() {
  return _currentArgCount;
}

bool ESP8266WebServer::hasArg(const char* name) {
  for (int i = 0; i < _currentArgCount; ++i) {
    if (_currentArgs[i].key == name)
      return true;
  }
  return false;
}

void ESP8266WebServer::onFileUpload(THandlerFunction fn) {
  _fileUploadHandler = fn;
}

void ESP8266WebServer::onNotFound(THandlerFunction fn) {
  _notFoundHandler = fn;
}

void ESP8266WebServer::_handleRequest() {
  RequestHandler* handler;
  for (handler = _firstHandler; handler; handler = handler->next)
  {
    if (handler->method != HTTP_ANY && handler->method != _currentMethod)
      continue;

    if (handler->uri != _currentUri)
      continue;

    handler->fn();
    break;
  }

  if (!handler){
#ifdef DEBUG
    DEBUG_OUTPUT.println("request handler not found");
#endif

    if(_notFoundHandler) {
      _notFoundHandler();
    }
    else {
      send(404, "text/plain", String("Not found: ") + _currentUri);
    }
  }

  uint16_t maxWait = HTTP_MAX_CLOSE_WAIT;
  while(_currentClient.connected() && maxWait--) {
    delay(1);
  }
  _currentClient   = WiFiClient();
  _currentUri      = String();
}

const char* ESP8266WebServer::_responseCodeToString(int code) {
  switch (code) {
    case 101: return "Switching Protocols";
    case 200: return "OK";
    case 403: return "Forbidden";
    case 404: return "Not found";
    case 500: return "Fail";
    default:  return "";
  }
}
<|endoftext|>


<commit_before>#include "chewing.h"

Chewing::Chewing(QQuickItem *parent):
    QQuickItem(parent)
{
    this->codec = QTextCodec::codecForName("UTF-8");
    this->ct =chewing_new();
    chewing_set_maxChiSymbolLen(this->ct, 10);
    chewing_set_candPerPage(this->ct, 9);
}

Chewing::~Chewing()
{
    chewing_delete(this->ct);
}

char convert_to_Eng(QString input_bopomofo){
    typedef struct bopomofo_to_Eng{
         QString bopomofo;
         char Eng;
    }BtoE;

    char character;
    int i;

    BtoE table[41] =
    {
        {"ㄅ", '1'},{"ㄉ",'2'},{"ˇ",'3'},{"ˋ",'4'},{"ㄓ",'5'},{"ˊ",'6'},
        {  "˙", '7' },{  "ㄚ", '8' },{  "ㄞ", '9' },{  "ㄢ", '0' },{  "ㄦ", '-' },{  "ㄆ", 'q' },
        {  "ㄊ", 'w' },{  "ㄍ", 'e' },{  "ㄐ", 'r' },{  "ㄔ", 't' },{  "ㄗ", 'y' },{  "ㄧ", 'u' },
        {  "ㄛ", 'i' },{  "ㄟ", 'o' },{  "ㄣ", 'p' },{  "ㄇ", 'a' },{  "ㄋ", 's' },{  "ㄎ", 'd' },
        {  "ㄑ", 'f' },{  "ㄕ", 'g' },{  "ㄘ", 'h' },{  "ㄨ", 'j' },{  "ㄜ", 'k' },{  "ㄠ", 'l' },
        {  "ㄤ", ';' },{  "ㄈ", 'z' },{  "ㄌ", 'x' },{  "ㄏ", 'c' },{  "ㄒ", 'v' },{  "ㄖ", 'b' },
        {  "ㄙ", 'n' },{  "ㄩ", 'm' },{  "ㄝ", ',' },{  "ㄡ", '.' },{  "ㄥ", '/' },
    };

    for(i=0;i<=40;i++){
       if(input_bopomofo==table[i].bopomofo){
           character = table[i].Eng;
           break;
       }
        else{
            character = input_bopomofo.toLocal8Bit ().data()[0];
        }
    }
    return character;
}

/*generate cnadidate string for Chinese words or symbols*/
QString generateCandidate( ChewingContext* ctx){
    QString candidate_string;
    chewing_cand_open(ctx);
    while(chewing_cand_hasNext(ctx)) {
        char *str = chewing_cand_String(ctx);
        if (str) {
            candidate_string +=  QString(QByteArray(str));
            candidate_string +=" ";
            chewing_free(str);
        }
    }
    chewing_cand_close(ctx);
    return candidate_string;
}

/*Reset ChewingContext*/
void Chewing::handleReset(){
    chewing_delete(this->ct);
    this->ct =chewing_new();
}

/*This function will transfer pressed key to chewing engine.*/
void Chewing::handleDefault(QString str){
    chewing_handle_Default(this->ct,convert_to_Eng(str));
}

/*This function will transfer space key to chewing engine
  when you don't have keyboard to input.*/
void Chewing::handleSpace(){
    chewing_handle_Space(this->ct);
}

/*This function will transfer backspace key to chewing engine.*/
void Chewing::handleBackSpace(){
    chewing_handle_Backspace(this->ct);
}

/*This function will transfer enter key to chewing engine
  when you don't have keyboard to input.*/
QString Chewing::handleEnter(){
    chewing_handle_Enter(this->ct);
    QString buf = chewing_commit_String(this->ct);
    return buf;
}

/*This function will return preedit string that include bopomofo.*/
QString Chewing::getPreedit(){
    QString preedit_string;
    if (chewing_buffer_Check(this->ct)) {
        char *buf = chewing_buffer_String(this->ct);

        if (buf) {
            preedit_string = QString(QByteArray(buf));
            chewing_free(buf);
        }
    }

    const char *bopomofo_str =chewing_bopomofo_String_static(this->ct);
    if (bopomofo_str) {
        preedit_string += QString(QByteArray(bopomofo_str));
    }

    return preedit_string;
}

/*This function will return candidate string of Chinese words.*/
QString Chewing::getCandidate(){
    return generateCandidate(this->ct);
}
/*This function will return candidate string of symbols.*/
QString Chewing::getSymbol(){
    this->ct =chewing_new();
    chewing_handle_Default(this->ct,'`');
    chewing_handle_Down(this->ct);
    chewing_cand_Enumerate(this->ct);
    chewing_handle_Default(this->ct, '3');
    return generateCandidate(this->ct);
}

<commit_msg>improving the performance<commit_after>#include "chewing.h"

Chewing::Chewing(QQuickItem *parent):
    QQuickItem(parent)
{
    this->codec = QTextCodec::codecForName("UTF-8");
    this->ct =chewing_new();
    chewing_set_maxChiSymbolLen(this->ct, 10);
    chewing_set_candPerPage(this->ct, 9);
}

Chewing::~Chewing()
{
    chewing_delete(this->ct);
}

char convert_to_Eng(QString input_bopomofo){
    typedef struct bopomofo_to_Eng{
         QString bopomofo;
         char Eng;
    }BtoE;

    BtoE table[41] =
    {
        {"ㄅ", '1'},{"ㄉ",'2'},{"ˇ",'3'},{"ˋ",'4'},{"ㄓ",'5'},{"ˊ",'6'},
        {  "˙", '7' },{  "ㄚ", '8' },{  "ㄞ", '9' },{  "ㄢ", '0' },{  "ㄦ", '-' },{  "ㄆ", 'q' },
        {  "ㄊ", 'w' },{  "ㄍ", 'e' },{  "ㄐ", 'r' },{  "ㄔ", 't' },{  "ㄗ", 'y' },{  "ㄧ", 'u' },
        {  "ㄛ", 'i' },{  "ㄟ", 'o' },{  "ㄣ", 'p' },{  "ㄇ", 'a' },{  "ㄋ", 's' },{  "ㄎ", 'd' },
        {  "ㄑ", 'f' },{  "ㄕ", 'g' },{  "ㄘ", 'h' },{  "ㄨ", 'j' },{  "ㄜ", 'k' },{  "ㄠ", 'l' },
        {  "ㄤ", ';' },{  "ㄈ", 'z' },{  "ㄌ", 'x' },{  "ㄏ", 'c' },{  "ㄒ", 'v' },{  "ㄖ", 'b' },
        {  "ㄙ", 'n' },{  "ㄩ", 'm' },{  "ㄝ", ',' },{  "ㄡ", '.' },{  "ㄥ", '/' },
    };

    for(int i=0;i<=40;i++){
       if(input_bopomofo==table[i].bopomofo){
           return table[i].Eng;
       }
    }
    return input_bopomofo.toLocal8Bit ().data()[0];
}

/*generate cnadidate string for Chinese words or symbols*/
QString generateCandidate( ChewingContext* ctx){
    QString candidate_string;
    chewing_cand_open(ctx);
    while(chewing_cand_hasNext(ctx)) {
        char *str = chewing_cand_String(ctx);
        if (str) {
            candidate_string +=  QString(QByteArray(str));
            candidate_string +=" ";
            chewing_free(str);
        }
    }
    chewing_cand_close(ctx);
    return candidate_string;
}

/*Reset ChewingContext*/
void Chewing::handleReset(){
    chewing_delete(this->ct);
    this->ct =chewing_new();
}

/*This function will transfer pressed key to chewing engine.*/
void Chewing::handleDefault(QString str){
    chewing_handle_Default(this->ct,convert_to_Eng(str));
}

/*This function will transfer space key to chewing engine
  when you don't have keyboard to input.*/
void Chewing::handleSpace(){
    chewing_handle_Space(this->ct);
}

/*This function will transfer backspace key to chewing engine.*/
void Chewing::handleBackSpace(){
    chewing_handle_Backspace(this->ct);
}

/*This function will transfer enter key to chewing engine
  when you don't have keyboard to input.*/
QString Chewing::handleEnter(){
    chewing_handle_Enter(this->ct);
    QString buf = chewing_commit_String(this->ct);
    return buf;
}

/*This function will return preedit string that include bopomofo.*/
QString Chewing::getPreedit(){
    QString preedit_string;
    if (chewing_buffer_Check(this->ct)) {
        char *buf = chewing_buffer_String(this->ct);

        if (buf) {
            preedit_string = QString(QByteArray(buf));
            chewing_free(buf);
        }
    }

    const char *bopomofo_str =chewing_bopomofo_String_static(this->ct);
    if (bopomofo_str) {
        preedit_string += QString(QByteArray(bopomofo_str));
    }

    return preedit_string;
}

/*This function will return candidate string of Chinese words.*/
QString Chewing::getCandidate(){
    return generateCandidate(this->ct);
}
/*This function will return candidate string of symbols.*/
QString Chewing::getSymbol(){
    this->ct =chewing_new();
    chewing_handle_Default(this->ct,'`');
    chewing_handle_Down(this->ct);
    chewing_cand_Enumerate(this->ct);
    chewing_handle_Default(this->ct, '3');
    return generateCandidate(this->ct);
}

<|endoftext|>


<commit_before>#include <iostream>
#include <stdio.h>
#include <unistd.h> //used for fork(), execvp()
#include <sys/types.h> //used for wait
#include <sys/wait.h> //used for wait
#include <sys/stat.h>
#include <string.h>
#include <cstdlib>
#include <vector>
#include <stack>
#include "command.h"
using namespace std;

bool Execute::execute(vector<string>cmds) {
	if (cmds.at(0) == "exit") {
		exit(0);
	}
	if(cmds.size() > 1){
		if (cmds.at(1).at(0) == '\"'||cmds.at(1).at(0) ==  '\'') { //fixes quotation marks when executing echo
			cmds.at(1).erase(cmds.at(1).begin());
			cmds.at(cmds.size()-1).erase(cmds.at(cmds.size()-1).begin() + cmds.at(cmds.size()-1).size()-1);
		}
	}
	char* cmdArr[cmds.size() + 1];
	int i = 0;
	for (i = 0; i < static_cast<int>(cmds.size()); i++) {//convert the vector into a char* array for execvp
		cmdArr[i] = (char*)cmds.at(i).c_str();
	}
	cmdArr[cmds.size()] = NULL; //set last value to NULL for execvp
	int status = 0;
	char fullpath[20] = "/bin/"; //full file path, starts at bin
	strcat(fullpath, cmds[0].c_str()); //add the first command to the file path
	pid_t pid = fork();//split the processes into parent and child
	if(pid==0) { //if the process is a child
		int exec = execvp(fullpath,cmdArr); // to check if execvp passes or fails
		if (exec == -1) { //execvp failed
			return false;
			perror("exec");
			exit(-1);
		}
	}
	else { //Otherwise, the process is a parent
		waitpid(pid, &status, 0); //wait for the child process to finish
	}
	return true;
}
bool Execute::execTest(vector<string>cmds){
	struct stat file;
	bool hasFlag;
	for(unsigned i = 0; i < cmds.size(); i++) {
		cout << "<" << cmds.at(i) << ">";
	}
	cout << endl;
	if( cmds.at(1) != "-e" && cmds.at(1) != "-f" && cmds.at(1) != "-d"){
			hasFlag = false;
	}
	else {
			hasFlag = true;
	}

	if( cmds.at(1) == "-e" || !hasFlag ){ //does test -e or test with no specifications
		// cout << "-e or none" << endl;
		if( hasFlag && stat(cmds.at(2).c_str(), &file) == 0){
			// cout << "-e" << endl;
			cout << "(True)" << endl;
			return true;
		}
		else if (!hasFlag && stat(cmds.at(1).c_str(), &file) == 0){
			// cout << "none" << endl;
			cout << "(True)" << endl;
			return true;
		}
		else {
			cout << "(False)" << endl;
			return false;
		}
	}

	else if(cmds.at(1) == "-f"){	//does test witht -f, so checks if it is a file
		if(stat(cmds.at(2).c_str(), &file) == 0){
			if(S_ISREG(file.st_mode)){
				cout << "(True)" << endl;
				return true;
			}
			else{
				cout << "(False)" << endl;
				return false;
			}
		}
		else{
			cout << "(False)" << endl;
			return false;
		}
	}

	else if(cmds.at(1) == "-d"){ // does test with -d, so checks if it a directory
		if(stat(cmds.at(2).c_str(), &file) == 0) {
			if(S_ISDIR(file.st_mode)){
				cout << "(True)" << endl;
				return true;
			}
			else{
				cout << "(False)" << endl;
				return false;
			}
		}
		else{
			cout << "(False)" << endl;
			return false;
		}
	}
	return false;
}

void Execute::execMain(string ss, bool &execStatus) {
	vector<string>argsList; //holds the whole command line (parsed)
	vector<string>command; //holds individual commands to run in execute() function

	if (ss == "exit") {
		exit(0);
	}

	parseString(ss, argsList);

	for (unsigned i = 0; i < argsList.size(); i++) { //start where we left off, only executes when there is two connectors in between a command
		command.push_back(argsList.at(i));

		/*if (isComment(argsList.at(i))) { //if it is a comment, break out of the loop
			break;
		}*/
		if(isComparator(argsList.at(i)) && command.size() > 1) { //if there's more commands and the next command is a connector
			if(command.at(1) == "#") { //handles comments after connector
					break;
			}

			if (command.at(0) == "&&") {
				i--; //decrement i to include next connector
				command.erase(command.begin()); //remove connector from command
				command.pop_back(); //remove connector at the end
				if (execStatus == true) {
					if(isTest(command.at(0))){
						execStatus = execTest(command);
					}
					else{
						execStatus = execute(command);
					}
				}
				command.clear();
			}

			else if(command.at(0)== ";") {
				i--;
				command.erase(command.begin()); //remove connector from command
				command.pop_back(); //pop back twice
				if(isTest(command.at(0))){
					execStatus = execTest(command);
				}
				else{
					execStatus = execute(command);
				}
				command.clear();
			}
			else if(command.at(0) == "||") {
				i--; //decrement i to include next connector
				command.erase(command.begin()); //delete the connector
				command.pop_back(); //remove connector at end of command
				if (execStatus == false) { //if the previous command did not execute, run this command
					if(isTest(command.at(0))){
						execStatus = execTest(command);
					}
					else{
						execStatus = execute(command);
					}
				}
				else {
					execStatus = false;
				}
				command.clear();
			}
			else if(command.at(0) == "#" && command.size() == 1) {

				break; //don't do anything if command has comments
			}
			else if(!isComparator(command.at(0))){
				i--;
				command.pop_back(); //remove the connector
				if(isTest(command.at(0))){
						execStatus = execTest(command);
				}
				else{
					execStatus = execute(command);
				}
				command.clear();
			}
		}
		else if(i == argsList.size()-1) { //if this is the last command
			if(isComparator(command.at(command.size()-1))) { //if there is a dangling connector at the end remove it
				command.pop_back();
				break;
			}
			if (command.size() != 0 && command.at(0) == "&&") {
				command.erase(command.begin()); //delete connector
				if (execStatus == true) {
					if(isTest(command.at(0))){
						execStatus = execTest(command);
					}
					else{
						execStatus = execute(command);
					}
				}
			}
			else if (command.size() != 0 && command.at(0) == "||") {
				command.erase(command.begin());
				if (execStatus == false) {
					if(isTest(command.at(0))){
						execStatus = execTest(command);
					}
					else{
						execStatus = execute(command);
					}
				}
			}
			else if (command.size() != 0 && command.at(0) == ";") {
				command.erase(command.begin());
				if(isTest(command.at(0))){
						execStatus = execTest(command);
				}
				else{
					execStatus = execute(command);
				}
				break;
			}
			else if (command.size() != 0 && command.at(0) == "#") {
				command.erase(command.begin());
				return;
			}
			else {
				if(isTest(command.at(0))){
					execStatus = execTest(command);
				}
				else{
					execStatus = execute(command);
				}
			}
		}
	}

}

void Parse::print() {
	char* user = getlogin();
	char host[256]; //creates a host name with buffer size
	gethostname(host, 128);
	if (user == NULL) { //could not find the username
		cout << "$ ";
	}
	else {
		cout << user << '@' << host << "$ ";
	}
}

bool Parse::hasOpenParenthesis(string s){ //checks for '('

	if(s.at(0) == '(') {
		return true; //return position of semi
	}
	return false; // if not found return -1
}

bool Parse::hasEndingParenthesis(string s){ //checks for ')'

	if(s.at(s.size() - 1) == ')'){
		return true; //return position of semi
	}
	return false; // if not found return -1
}

bool Parse::isComparator(string s) {
	if (s == "||" || s== "&&" || s == ";" || s == "#") {
		return true;
	}
	return false;
}

bool Parse::hasSemiColon(string s){ //checks for semicolon attached to a word;
	for(unsigned i = 0; i < s.size(); ++i){
		if(s.at(i) == ';'){
				return true; //return position of semi
		}
	}
	return false; // if not found return -1
}

bool Parse::isComment(string s){
	for(unsigned i = 0; i < s.size(); ++i){
			if(s.at(i) == '#') {
					return true;
			}
	}
	return false;
}
bool Parse::isTest(string s){ //checks if command is a test case
	if(s == "[" || s == "test"){
			return true;
	}
	return false;
}

bool Parse::checkForParenthesis(string command) {
	stack<char>parenthesis;
	for(unsigned i = 0; i < command.size(); i++) {
		if(command.at(i) == '(') {
			parenthesis.push('(');
		}
		else if(command.at(i) == ')') {
			if(!parenthesis.empty()) {
				parenthesis.pop();
			}
			else {
				cout << "Parenthesis mismatch. Missing \'('." << endl;
				return false;
			}
		}
	}
	if(!parenthesis.empty()) {
		cout << "Parenthesis mismatch. Missing \')'." << endl;
		return false;
	}
	return true;
}

void Parse::remove_parenthesis(string &s) {
	for(unsigned i = 0; i < s.size(); i++) {
		if(s.at(i) == '(' || s.at(i) == ')') {
			s.erase(s.begin() + i);
		}
	}
}

void Parse::parseString(string ss, vector<string>&argsList) {

	char* token; //split command into separate strings
	char* cmd = new char[ss.length() + 1];
	strcpy(cmd, ss.c_str()); //converts string to char*
	token = strtok(cmd, " "); //tokenize first part of string
	int i = 0;
	for (i = 0; token != NULL; i++) {

		if (token == NULL) { //break out of for loop if there's an empty token
			break;
		}
		string tokenString = string(token);
		//cout << "Token is: " << tokenString << endl;
		if(!isComparator(tokenString)) { //if it a just a connector without any words attached  go straight to push_back
			if (hasSemiColon(tokenString)) {//if there is a semicolon attached enter loop
				string semicolon = ";";
				tokenString.erase(tokenString.begin() + tokenString.size() -1 ); //remove the semicolon at the end of word
				argsList.push_back(tokenString); // push back word without semicolon
				argsList.push_back(semicolon);// push back semi colon as its own string
			}
			else if(isComment(tokenString)) { //if there is a hash enter loop
				string hash = "#";
				tokenString.erase(tokenString.begin()); // remove hashtag located at front of string
				argsList.push_back(hash); // push back has as its own string
				argsList.push_back(tokenString); // push back word
			}
			else{
					argsList.push_back(tokenString); // if it does not contain semi or hash push back word
			}
		}
		else if(hasEndingParenthesis(tokenString)){
			if(tokenString.size() == 1){
				argsList.push_back(tokenString);
			}
			else{
				string paran = ")";
				tokenString.erase(tokenString.begin() + tokenString.size() -1 ); //remove the semicolon at the end of word
				argsList.push_back(tokenString); // push back word without semicolon
				argsList.push_back(paran);// push back semi colon as its own string
			}
		}
		else if(hasOpenParenthesis(tokenString)){
			if(tokenString.size() == 1){
				argsList.push_back(tokenString);
			}
			else{
				string paran = "(";
				tokenString.erase(tokenString.begin()); // remove hashtag located at front of string
				argsList.push_back(paran); // push back has as its own string
				argsList.push_back(tokenString); // push back word
			}
		}

		else{
			argsList.push_back(tokenString);
			}
		token = strtok(NULL, " ");
	}

}

void Parse::sepParenthesis(string command) { //parses the parenthesis and calls execMain on those parsed strings
	bool execStatus = true;
	checkForParenthesis(command);
	vector<string>parseCommands;
	vector<string>commandList;
	parseString(command,parseCommands);

	string comm;
	bool inParenthesis = false;
	for(unsigned i = 0; i < parseCommands.size();i++) {
		comm += parseCommands.at(i);
		if(i < parseCommands.size()-1) { //push individual command if we find a connector and we're not in the parenthesis
			if(isComparator(parseCommands.at(i+1)) && !inParenthesis) {
				remove_parenthesis(comm); //remove any parenthesis before pushing individual commands
				commandList.push_back(comm);
				comm.clear();
			}
		}
		if(i < parseCommands.size()-1 && !hasEndingParenthesis(parseCommands.at(i))) { //won't add a space to the end or after the end parenthesis
			comm += " ";
		}

		if(hasOpenParenthesis(parseCommands.at(i))) {
			inParenthesis = true;
		}
		else if(hasEndingParenthesis(parseCommands.at(i))) {
			remove_parenthesis(comm); //remove any parenthesis before pushing individual commands
			commandList.push_back(comm);
			comm.clear();
			inParenthesis = false;
		}
		else if(i == parseCommands.size() - 1) {
			remove_parenthesis(comm); //remove any parenthesis before pushing individual commands
			commandList.push_back(comm);
		}
	}

    Execute e;
	//execute individual commands based on precedence
	for (unsigned i = 0; i < commandList.size(); i++) {
		e.execMain(commandList.at(i),execStatus);
	}

}<commit_msg>Deleted old file<commit_after><|endoftext|>


<commit_before>/*************************************************************************
 *   Copyright (C) 2011-2012 by Paul-Louis Ageneau                       *
 *   paul-louis (at) ageneau (dot) org                                   *
 *                                                                       *
 *   This file is part of TeapotNet.                                     *
 *                                                                       *
 *   TeapotNet is free software: you can redistribute it and/or modify   *
 *   it under the terms of the GNU Affero General Public License as      *
 *   published by the Free Software Foundation, either version 3 of      *
 *   the License, or (at your option) any later version.                 *
 *                                                                       *
 *   TeapotNet is distributed in the hope that it will be useful, but    *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the        *
 *   GNU Affero General Public License for more details.                 *
 *                                                                       *
 *   You should have received a copy of the GNU Affero General Public    *
 *   License along with TeapotNet.                                       *
 *   If not, see <http://www.gnu.org/licenses/>.                         *
 *************************************************************************/

#include "tpn/tracker.h"
#include "tpn/yamlserializer.h"

namespace tpn
{

const double Tracker::EntryLife = 3600.;	// seconds

Tracker::Tracker(int port) :
		Http::Server(port)
{
	mStorage.cleaner   = mStorage.map.begin();
	mAlternate.cleaner = mAlternate.map.begin();
}

Tracker::~Tracker(void)
{

}

void Tracker::process(Http::Request &request)
{
	LogDebug("Tracker", "URL " + request.url);

	try {
		if(request.url != "/tracker" && request.url != "/tracker/")
			throw 404;

		if(!request.get.contains("id"))
			throw Exception("Missing identifier");

		Identifier identifier;
		try {
			request.get["id"].extract(identifier);
		}
		catch(...)
		{
			throw Exception("Invalid identifier");
		}

		if(identifier.getDigest().size() != 64)
			throw Exception("Invalid indentifier size");

		bool alternate = false;
		if(request.get.contains("alternate")) alternate = true;

		if(request.method == "POST")
		{
			String instance;
			if(request.post.get("instance", instance)) identifier.setName(instance);
			if(identifier.getName().empty()) identifier.setName("default");

			int count = 0;

			String port;
			if(request.post.get("port", port))
			{
				String host;
				if(!request.post.get("host", host))
				{
					if(request.headers.contains("X-Forwarded-For"))
						host = request.headers["X-Forwarded-For"];
					else host = request.sock->getRemoteAddress().host();
				}

				Address addr(host, port);
				SynchronizeStatement(this, insert(mStorage, identifier, addr));
				++count;
				LogDebug("Tracker", "POST " + identifier.toString() + " -> " + addr.toString());
			}

			String addresses;
			if(request.post.get("addresses", addresses))
			{
				List<String> list;
				addresses.explode(list, ',');

				for(	List<String>::iterator it = list.begin();
					it != list.end();
					++it)
				try {
				      	Address addr(*it);
				      	if(alternate)
					{
						LogDebug("Tracker", "POST " + identifier.toString() + " -> " + addr.toString() + " (alternate)");
						SynchronizeStatement(this, insert(mAlternate, identifier, addr));
					}
				      	else {
						LogDebug("Tracker", "POST " + identifier.toString() + " -> " + addr.toString());
						SynchronizeStatement(this, insert(mStorage, identifier, addr));
					}

					++count;
				}
				catch(...)
				{

				}
			}

			if(!alternate && request.post.get("alternate", addresses))
			{
				List<String> list;
				addresses.explode(list, ',');

				for(	List<String>::iterator it = list.begin();
					it != list.end();
					++it)
				try {
				      Address addr(*it);
				      SynchronizeStatement(this, insert(mAlternate, identifier, addr));
				      ++count;
				      LogDebug("Tracker", "POST " + identifier.toString() + " -> " + addr.toString() + " (alternate)");
				}
				catch(...)
				{

				}
			}

			SynchronizeStatement(this, clean(mStorage, 2*count+1));
			SynchronizeStatement(this, clean(mAlternate, 2*count+1));

			Http::Response response(request,200);
			response.send();
		}
		else {
			Http::Response response(request, 200);
			response.headers["Content-Type"] = "text/plain";
			response.send();

			if(alternate)
			{
				LogDebug("Tracker", "GET " + identifier.toString() + " (alternate)");
				SynchronizeStatement(this, retrieve(mAlternate, identifier, *response.sock));
			}
			else {
				LogDebug("Tracker", "GET " + identifier.toString());
				SynchronizeStatement(this, retrieve(mStorage, identifier, *response.sock));
			}
		}
	}
	catch(int code)
	{
		Http::Response response(request,code);
                response.send();
	}
	catch(const Exception &e)
	{
		LogWarn("Tracker", e.what());
		Http::Response response(request, 400);
                response.send();
	}
}

void Tracker::clean(Tracker::Storage &s, int nbr)
{
  	if(nbr < 0) nbr = s.map.size();
	else if(nbr > s.map.size()) nbr = s.map.size();
	for(int i=0; i<nbr; ++i)
	{
	  	if(s.cleaner == s.map.end()) s.cleaner = s.map.begin();

		Map<Address,Time> &submap = s.cleaner->second;
		Map<Address,Time>::iterator it = submap.begin();
		while(it != submap.end())
		{
			if(Time::Now() - it->second >= EntryLife) submap.erase(it++);
			else it++;
		}

		if(submap.empty()) s.map.erase(s.cleaner++);
		else s.cleaner++;
	}
}

void Tracker::insert(Tracker::Storage &s, const Identifier &identifier, const Address &addr)
{
	Map<Address,Time> &submap = s.map[identifier];
	submap[addr] = Time::Now();
}

void Tracker::retrieve(Tracker::Storage &s, const Identifier &identifier, Stream &output) const
{
	map_t::const_iterator it = s.map.lower_bound(identifier);
	if(it == s.map.end() || it->first != identifier) return;

	YamlSerializer serializer(&output);
	serializer.outputMapBegin();

	while(it != s.map.end() && it->first == identifier)
	{
		SerializableArray<Address> array;
		it->second.getKeys(array);
		if(!array.empty())
		{
			std::random_shuffle(array.begin(), array.end());
			serializer.outputMapElement(it->first.getName(), array);
		}
		++it;
	}

	serializer.outputMapEnd();
}

bool Tracker::contains(Storage &s, const Identifier &identifier)
{
	return s.map.contains(identifier);
}

}
<commit_msg>Fixed X-Forwarded-For parsing<commit_after>/*************************************************************************
 *   Copyright (C) 2011-2012 by Paul-Louis Ageneau                       *
 *   paul-louis (at) ageneau (dot) org                                   *
 *                                                                       *
 *   This file is part of TeapotNet.                                     *
 *                                                                       *
 *   TeapotNet is free software: you can redistribute it and/or modify   *
 *   it under the terms of the GNU Affero General Public License as      *
 *   published by the Free Software Foundation, either version 3 of      *
 *   the License, or (at your option) any later version.                 *
 *                                                                       *
 *   TeapotNet is distributed in the hope that it will be useful, but    *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the        *
 *   GNU Affero General Public License for more details.                 *
 *                                                                       *
 *   You should have received a copy of the GNU Affero General Public    *
 *   License along with TeapotNet.                                       *
 *   If not, see <http://www.gnu.org/licenses/>.                         *
 *************************************************************************/

#include "tpn/tracker.h"
#include "tpn/yamlserializer.h"

namespace tpn
{

const double Tracker::EntryLife = 3600.;	// seconds

Tracker::Tracker(int port) :
		Http::Server(port)
{
	mStorage.cleaner   = mStorage.map.begin();
	mAlternate.cleaner = mAlternate.map.begin();
}

Tracker::~Tracker(void)
{

}

void Tracker::process(Http::Request &request)
{
	LogDebug("Tracker", "URL " + request.url);

	try {
		if(request.url != "/tracker" && request.url != "/tracker/")
			throw 404;

		if(!request.get.contains("id"))
			throw Exception("Missing identifier");

		Identifier identifier;
		try {
			request.get["id"].extract(identifier);
		}
		catch(...)
		{
			throw Exception("Invalid identifier");
		}

		if(identifier.getDigest().size() != 64)
			throw Exception("Invalid indentifier size");

		bool alternate = false;
		if(request.get.contains("alternate")) alternate = true;

		if(request.method == "POST")
		{
			String instance;
			if(request.post.get("instance", instance)) identifier.setName(instance);
			if(identifier.getName().empty()) identifier.setName("default");

			int count = 0;

			String port;
			if(request.post.get("port", port))
			{
				String host;
				if(!request.post.get("host", host))
				{
					if(request.headers.contains("X-Forwarded-For"))
						host = request.headers["X-Forwarded-For"].beforeLast(',').trimmed();
					else host = request.sock->getRemoteAddress().host();
				}

				Address addr(host, port);
				SynchronizeStatement(this, insert(mStorage, identifier, addr));
				++count;
				LogDebug("Tracker", "POST " + identifier.toString() + " -> " + addr.toString());
			}

			String addresses;
			if(request.post.get("addresses", addresses))
			{
				List<String> list;
				addresses.explode(list, ',');

				for(	List<String>::iterator it = list.begin();
					it != list.end();
					++it)
				try {
				      	Address addr(*it);
				      	if(alternate)
					{
						LogDebug("Tracker", "POST " + identifier.toString() + " -> " + addr.toString() + " (alternate)");
						SynchronizeStatement(this, insert(mAlternate, identifier, addr));
					}
				      	else {
						LogDebug("Tracker", "POST " + identifier.toString() + " -> " + addr.toString());
						SynchronizeStatement(this, insert(mStorage, identifier, addr));
					}

					++count;
				}
				catch(...)
				{

				}
			}

			if(!alternate && request.post.get("alternate", addresses))
			{
				List<String> list;
				addresses.explode(list, ',');

				for(	List<String>::iterator it = list.begin();
					it != list.end();
					++it)
				try {
				      Address addr(*it);
				      SynchronizeStatement(this, insert(mAlternate, identifier, addr));
				      ++count;
				      LogDebug("Tracker", "POST " + identifier.toString() + " -> " + addr.toString() + " (alternate)");
				}
				catch(...)
				{

				}
			}

			SynchronizeStatement(this, clean(mStorage, 2*count+1));
			SynchronizeStatement(this, clean(mAlternate, 2*count+1));

			Http::Response response(request,200);
			response.send();
		}
		else {
			Http::Response response(request, 200);
			response.headers["Content-Type"] = "text/plain";
			response.send();

			if(alternate)
			{
				LogDebug("Tracker", "GET " + identifier.toString() + " (alternate)");
				SynchronizeStatement(this, retrieve(mAlternate, identifier, *response.sock));
			}
			else {
				LogDebug("Tracker", "GET " + identifier.toString());
				SynchronizeStatement(this, retrieve(mStorage, identifier, *response.sock));
			}
		}
	}
	catch(int code)
	{
		Http::Response response(request,code);
                response.send();
	}
	catch(const Exception &e)
	{
		LogWarn("Tracker", e.what());
		Http::Response response(request, 400);
                response.send();
	}
}

void Tracker::clean(Tracker::Storage &s, int nbr)
{
  	if(nbr < 0) nbr = s.map.size();
	else if(nbr > s.map.size()) nbr = s.map.size();
	for(int i=0; i<nbr; ++i)
	{
	  	if(s.cleaner == s.map.end()) s.cleaner = s.map.begin();

		Map<Address,Time> &submap = s.cleaner->second;
		Map<Address,Time>::iterator it = submap.begin();
		while(it != submap.end())
		{
			if(Time::Now() - it->second >= EntryLife) submap.erase(it++);
			else it++;
		}

		if(submap.empty()) s.map.erase(s.cleaner++);
		else s.cleaner++;
	}
}

void Tracker::insert(Tracker::Storage &s, const Identifier &identifier, const Address &addr)
{
	Map<Address,Time> &submap = s.map[identifier];
	submap[addr] = Time::Now();
}

void Tracker::retrieve(Tracker::Storage &s, const Identifier &identifier, Stream &output) const
{
	map_t::const_iterator it = s.map.lower_bound(identifier);
	if(it == s.map.end() || it->first != identifier) return;

	YamlSerializer serializer(&output);
	serializer.outputMapBegin();

	while(it != s.map.end() && it->first == identifier)
	{
		SerializableArray<Address> array;
		it->second.getKeys(array);
		if(!array.empty())
		{
			std::random_shuffle(array.begin(), array.end());
			serializer.outputMapElement(it->first.getName(), array);
		}
		++it;
	}

	serializer.outputMapEnd();
}

bool Tracker::contains(Storage &s, const Identifier &identifier)
{
	return s.map.contains(identifier);
}

}
<|endoftext|>


<commit_before>#include <ncurses.h>
#include "contents.hh"
#include "hooks.hh"

namespace vick {

contents::contents(std::vector<std::string> cont, mode* buffer_mode) : buffer_mode(buffer_mode), cont(cont)
{
    refreshmaxyx();
    hook::proc(hook::contents_created, *this);
}

contents::contents(mode* buffer_mode) : buffer_mode(buffer_mode), cont(std::vector<std::string>())
{
    refreshmaxyx();
    hook::proc(hook::contents_created, *this);
}

contents::contents(move_t y, move_t x, mode* buffer_mode)
    : buffer_mode(buffer_mode), cont(std::vector<std::string>()), y(y), x(x)
{
    refreshmaxyx();
    hook::proc(hook::contents_created, *this);
}

bool contents::operator()(char ch) const
{
    return (*this->buffer_mode)(ch);
}

contents::~contents()
{
    hook::proc(hook::contents_deleted, *this);
    if (delete_mode) delete buffer_mode;
}

contents::contents(const contents& other)
    : buffer_mode(new mode(*other.buffer_mode)), cont(other.cont), changes(other.changes),
      y(other.y), x(other.x), desired_x(other.desired_x),
      y_offset(other.y_offset), max_y(other.max_y), max_x(other.max_x),
      waiting_for_desired(other.waiting_for_desired), refresh(other.refresh),
      delete_mode(other.delete_mode)
{
}
contents::contents(contents&& other)
    : buffer_mode(other.buffer_mode), cont(other.cont), changes(other.changes), y(other.y),
      x(other.x), desired_x(other.desired_x), y_offset(other.y_offset),
      max_y(other.max_y), max_x(other.max_x),
      waiting_for_desired(other.waiting_for_desired), refresh(other.refresh),
      delete_mode(other.delete_mode)
{
}
contents& contents::operator=(const contents& other)
{
    if (this == &other) return *this;
    if (delete_mode && buffer_mode) delete buffer_mode;
    buffer_mode = new mode(*other.buffer_mode);
    cont = other.cont;
    y = other.y;
    x = other.x;
    desired_x = other.desired_x;
    y_offset = other.y_offset;
    max_y = other.max_y;
    max_x = other.max_x;
    waiting_for_desired = other.waiting_for_desired;
    refresh = other.refresh;
    delete_mode = other.delete_mode;
    changes = other.changes;
    return *this;
}

contents& contents::operator=(contents&& other)
{
    if (this == &other) return *this;
    if (delete_mode && buffer_mode) delete buffer_mode;
    buffer_mode = other.buffer_mode;
    cont = std::move(other.cont);
    y = other.y;
    x = other.x;
    desired_x = other.desired_x;
    y_offset = other.y_offset;
    max_y = other.max_y;
    max_x = other.max_x;
    waiting_for_desired = other.waiting_for_desired;
    refresh = other.refresh;
    delete_mode = other.delete_mode;
    changes = std::move(other.changes);
    return *this;
}

void contents::refreshmaxyx()
{
    getmaxyx(stdscr, max_y, max_x);
}

void contents::push_back(const std::string& str)
{
    this->cont.push_back(str);
}

}
<commit_msg>Don't check for null before deleting pointer<commit_after>#include <ncurses.h>
#include "contents.hh"
#include "hooks.hh"

namespace vick {

contents::contents(std::vector<std::string> cont, mode* buffer_mode) : buffer_mode(buffer_mode), cont(cont)
{
    refreshmaxyx();
    hook::proc(hook::contents_created, *this);
}

contents::contents(mode* buffer_mode) : buffer_mode(buffer_mode), cont(std::vector<std::string>())
{
    refreshmaxyx();
    hook::proc(hook::contents_created, *this);
}

contents::contents(move_t y, move_t x, mode* buffer_mode)
    : buffer_mode(buffer_mode), cont(std::vector<std::string>()), y(y), x(x)
{
    refreshmaxyx();
    hook::proc(hook::contents_created, *this);
}

bool contents::operator()(char ch) const
{
    return (*this->buffer_mode)(ch);
}

contents::~contents()
{
    hook::proc(hook::contents_deleted, *this);
    if (delete_mode) delete buffer_mode;
}

contents::contents(const contents& other)
    : buffer_mode(new mode(*other.buffer_mode)), cont(other.cont), changes(other.changes),
      y(other.y), x(other.x), desired_x(other.desired_x),
      y_offset(other.y_offset), max_y(other.max_y), max_x(other.max_x),
      waiting_for_desired(other.waiting_for_desired), refresh(other.refresh),
      delete_mode(other.delete_mode)
{
}
contents::contents(contents&& other)
    : buffer_mode(other.buffer_mode), cont(other.cont), changes(other.changes), y(other.y),
      x(other.x), desired_x(other.desired_x), y_offset(other.y_offset),
      max_y(other.max_y), max_x(other.max_x),
      waiting_for_desired(other.waiting_for_desired), refresh(other.refresh),
      delete_mode(other.delete_mode)
{
}
contents& contents::operator=(const contents& other)
{
    if (this == &other) return *this;
    if (delete_mode) delete buffer_mode;
    buffer_mode = new mode(*other.buffer_mode);
    cont = other.cont;
    y = other.y;
    x = other.x;
    desired_x = other.desired_x;
    y_offset = other.y_offset;
    max_y = other.max_y;
    max_x = other.max_x;
    waiting_for_desired = other.waiting_for_desired;
    refresh = other.refresh;
    delete_mode = other.delete_mode;
    changes = other.changes;
    return *this;
}

contents& contents::operator=(contents&& other)
{
    if (this == &other) return *this;
    if (delete_mode) delete buffer_mode;
    buffer_mode = other.buffer_mode;
    cont = std::move(other.cont);
    y = other.y;
    x = other.x;
    desired_x = other.desired_x;
    y_offset = other.y_offset;
    max_y = other.max_y;
    max_x = other.max_x;
    waiting_for_desired = other.waiting_for_desired;
    refresh = other.refresh;
    delete_mode = other.delete_mode;
    changes = std::move(other.changes);
    return *this;
}

void contents::refreshmaxyx()
{
    getmaxyx(stdscr, max_y, max_x);
}

void contents::push_back(const std::string& str)
{
    this->cont.push_back(str);
}

}
<|endoftext|>


<commit_before>// STL includes
#include <cstring>
#include <cstdio>
#include <iostream>

// Linux includes
#include <fcntl.h>
#include <sys/ioctl.h>

// hyperion local includes
#include "LedDeviceAPA102.h"

LedDeviceAPA102::LedDeviceAPA102(const std::string& outputDevice, const unsigned baudrate) :
	LedSpiDevice(outputDevice, baudrate, 500000),
	_ledBuffer(0)
{
	// empty
}

#define MIN(a,b)	((a)<(b)?(a):(b))
#define MAX(a,b)	((a)>(b)?(a):(b))

#define APA102_START_FRAME_BYTES	4
#define APA102_LED_BYTES		4
#define APA102_END_FRAME_BITS_MIN	32
#define APA102_END_FRAME_BITS(leds)	MAX((((leds-1)/2)+1),APA102_END_FRAME_BITS_MIN)
#define APA102_END_FRAME_BYTES(leds)	(((APA102_END_FRAME_BITS(leds)-1)/8)+1)
#define APA102_LED_HEADER		0xe0
#define APA102_LED_MAX_INTENSITY	0x1f

int LedDeviceAPA102::write(const std::vector<ColorRgb> &ledValues)
{
	const unsigned int startFrameSize = APA102_START_FRAME_BYTES;
	const unsigned int ledsCount = ledValues.size() ;
	const unsigned int ledsSize = ledsCount * APA102_LED_BYTES ;
	const unsigned int endFrameBits = APA102_END_FRAME_BITS(ledsCount) ;
	const unsigned int endFrameSize = APA102_END_FRAME_BYTES(ledsCount) ;
	const unsigned int transferSize = startFrameSize + ledsSize + endFrameSize ;

	if(_ledBuffer.size() != transferSize){
		_ledBuffer.resize(transferSize, 0x00);
	}

	unsigned idx = 0, i;
	for (i=0; i<APA102_START_FRAME_BYTES; i++) {
		_ledBuffer[idx++] = 0x00 ;
	}

	for (i=0; i<ledsCount; i++) {
		const ColorRgb& rgb = ledValues[i];

		_ledBuffer[idx++]   = APA102_LED_HEADER + APA102_LED_MAX_INTENSITY;
		_ledBuffer[idx++] = rgb.red;
		_ledBuffer[idx++] = rgb.green;
		_ledBuffer[idx++] = rgb.blue;
	}
	for(i=0; i<endFrameSize; i++)
		_ledBuffer[idx++] = 0x00 ;

	return writeBytes(_ledBuffer.size(), _ledBuffer.data());
}

int LedDeviceAPA102::switchOff()
{
	return write(std::vector<ColorRgb>(_ledBuffer.size(), ColorRgb{0,0,0}));
}
<commit_msg>Revert "Update LedDeviceAPA102.cpp by pcaffardi"<commit_after>
// STL includes
#include <cstring>
#include <cstdio>
#include <iostream>
#include <algorithm>

// Linux includes
#include <fcntl.h>
#include <sys/ioctl.h>

// hyperion local includes
#include "LedDeviceAPA102.h"

LedDeviceAPA102::LedDeviceAPA102(const std::string& outputDevice, const unsigned baudrate) :
	LedSpiDevice(outputDevice, baudrate, 500000),
	_ledBuffer(0)
{
	// empty
}

int LedDeviceAPA102::write(const std::vector<ColorRgb> &ledValues)
{
	const unsigned int startFrameSize = 4;
	const unsigned int endFrameSize = std::max<unsigned int>(((ledValues.size() + 15) / 16), 4);
	const unsigned int mLedCount = (ledValues.size() * 4) + startFrameSize + endFrameSize;
	if(_ledBuffer.size() != mLedCount){
		_ledBuffer.resize(mLedCount, 0xFF);
		_ledBuffer[0] = 0x00;
		_ledBuffer[1] = 0x00;
		_ledBuffer[2] = 0x00;
		_ledBuffer[3] = 0x00;
	}

	for (unsigned iLed=1; iLed<=ledValues.size(); ++iLed) {
		const ColorRgb& rgb = ledValues[iLed-1];
		_ledBuffer[iLed*4]   = 0xFF;
		_ledBuffer[iLed*4+1] = rgb.red;
		_ledBuffer[iLed*4+2] = rgb.green;
		_ledBuffer[iLed*4+3] = rgb.blue;
	}

	return writeBytes(_ledBuffer.size(), _ledBuffer.data());
}

int LedDeviceAPA102::switchOff()
{
	return write(std::vector<ColorRgb>(_ledBuffer.size(), ColorRgb{0,0,0}));
}
<|endoftext|>


<commit_before>
// STL includes
#include <cstring>
#include <cstdio>
#include <iostream>

// Linux includes
#include <fcntl.h>
#include <sys/ioctl.h>

// hyperion local includes
#include "LedDeviceAPA102.h"

LedDeviceAPA102::LedDeviceAPA102(const std::string& outputDevice, const unsigned baudrate) :
	LedSpiDevice(outputDevice, baudrate, 500000),
	_ledBuffer(0)
{
	// empty
}

int LedDeviceAPA102::write(const std::vector<ColorRgb> &ledValues)
{
	const unsigned int mLedCount = (ledValues.size() * 4) + 8;
	if(_ledBuffer.size() != mLedCount){
		_ledBuffer.resize(mLedCount, 0x00);
	}

	for (unsigned iLed=1; iLed<=ledValues.size(); ++iLed) {
		const ColorRgb& rgb = ledValues[iLed];
		_ledBuffer[iLed*4]   = 0xFF;
		_ledBuffer[iLed*4+1] = rgb.red;
		_ledBuffer[iLed*4+2] = rgb.green;
		_ledBuffer[iLed*4+3] = rgb.blue;
	}

	return writeBytes(_ledBuffer.size(), _ledBuffer.data());
}

int LedDeviceAPA102::switchOff()
{
	return write(std::vector<ColorRgb>(_ledBuffer.size(), ColorRgb{0,0,0}));
}
<commit_msg>Update LedDeviceAPA102.cpp<commit_after>
// STL includes
#include <cstring>
#include <cstdio>
#include <iostream>

// Linux includes
#include <fcntl.h>
#include <sys/ioctl.h>

// hyperion local includes
#include "LedDeviceAPA102.h"

LedDeviceAPA102::LedDeviceAPA102(const std::string& outputDevice, const unsigned baudrate) :
	LedSpiDevice(outputDevice, baudrate, 500000),
	_ledBuffer(0)
{
	// empty
}

int LedDeviceAPA102::write(const std::vector<ColorRgb> &ledValues)
{
	const unsigned int startFrameSize = 4;
	const unsigned int endFrameSize = (ledValues.size() + 63) / 64 * 4;
	const unsigned int mLedCount = (ledValues.size() * 4) + startFrameSize + endFrameSize;
	if(_ledBuffer.size() != mLedCount){
		_ledBuffer.resize(mLedCount, 0x00);
	}

	for (unsigned iLed=1; iLed<=ledValues.size(); ++iLed) {
		const ColorRgb& rgb = ledValues[iLed];
		_ledBuffer[iLed*4]   = 0xFF;
		_ledBuffer[iLed*4+1] = rgb.red;
		_ledBuffer[iLed*4+2] = rgb.green;
		_ledBuffer[iLed*4+3] = rgb.blue;
	}

	return writeBytes(_ledBuffer.size(), _ledBuffer.data());
}

int LedDeviceAPA102::switchOff()
{
	return write(std::vector<ColorRgb>(_ledBuffer.size(), ColorRgb{0,0,0}));
}
<|endoftext|>


<commit_before>#ifndef RETHREAD_CANCELLATION_TOKEN_H
#define RETHREAD_CANCELLATION_TOKEN_H

// Copyright (c) 2016, Boris Sazonov
//
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
// provided that the above copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include <rethread/detail/intrusive_list.h>
#include <rethread/detail/reverse_lock.hpp>
#include <rethread/detail/utility.hpp>

#include <atomic>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <stdexcept>
#include <thread>
#include <algorithm>

namespace rethread
{
	class cancellation_handler
	{
	protected:
		virtual ~cancellation_handler() { }

	public:
		/// @brief Should cancel blocking call
		virtual void cancel() = 0;

		/// @brief Should reset cancellation_handler to it's original state
		///        For each cancel() call, there's _exactly_ one reset() call
		virtual void reset() { }
	};

	class cancellation_token;

	namespace this_thread
	{
		template<typename Rep, typename Period>
		inline void sleep_for(const std::chrono::duration<Rep, Period>& duration, const cancellation_token& token);
	}

	class cancellation_token
	{
	protected:
		mutable std::atomic<cancellation_handler*> _cancel_handler{nullptr};

	public:
		bool is_cancelled() const
		{ return _cancel_handler.load(std::memory_order_relaxed) == HazardPointer(); }

		explicit operator bool() const
		{ return !is_cancelled(); }

	protected:
		virtual void do_sleep_for(const std::chrono::nanoseconds& duration) const = 0;

		/// @pre Handler is registered
		/// @post Handler is not registered
		/// @note Will invoke cancellation_handler::reset() if necessary
		virtual void unregister_cancellation_handler(cancellation_handler& handler) const = 0;

	protected:
		template<typename Rep, typename Period>
		void sleep_for(const std::chrono::duration<Rep, Period>& duration) const
		{ do_sleep_for(std::chrono::duration_cast<std::chrono::nanoseconds>(duration)); }

		/// @pre Handler is not registered
		/// @returns Whether handler was registered. If token was cancelled before registration, this method skips registration and returns false
		bool try_register_cancellation_handler(cancellation_handler& handler) const
		{
			cancellation_handler* h = _cancel_handler.exchange(&handler, std::memory_order_release);
			RETHREAD_ANNOTATE_BEFORE(std::addressof(_cancel_handler));
			if (RETHREAD_UNLIKELY(h != nullptr))
			{
				RETHREAD_ASSERT(h == HazardPointer(), "Cancellation handler already registered!");
				return false;
			}
			return true;
		}

		/// @pre Handler is registered
		/// @returns Whether unregistration succeed
		bool try_unregister_cancellation_handler(cancellation_handler& handler) const
		{
			cancellation_handler* h = _cancel_handler.exchange(nullptr, std::memory_order_acquire);
			if (RETHREAD_LIKELY(h == &handler))
				return true;

			RETHREAD_ASSERT(h == HazardPointer(), "Another token was registered!");
			_cancel_handler.exchange(h); // restore value
			return false;
		}

	protected:
		cancellation_token() = default;
		cancellation_token(const cancellation_token&) = delete;
		cancellation_token& operator =(const cancellation_token&) = delete;
		~cancellation_token() = default;

		// Dirty trick to optimize register/unregister down to one atomic exchange
		static cancellation_handler* HazardPointer()
		{ return reinterpret_cast<cancellation_handler*>(1); }

	private:
		friend class cancellation_guard_base;

		template<typename Rep, typename Period>
		friend void this_thread::sleep_for(const std::chrono::duration<Rep, Period>& duration, const cancellation_token& token);
	};

	class dummy_cancellation_token : public cancellation_token
	{
	public:
		dummy_cancellation_token() = default;
		// Base class can't be copied, but can default construct it, because dummy_cancellation_token may never cancelled state
		dummy_cancellation_token(const dummy_cancellation_token&) : cancellation_token() { }
		dummy_cancellation_token& operator =(const dummy_cancellation_token&) = delete;
		~dummy_cancellation_token() = default;

	protected:
		void do_sleep_for(const std::chrono::nanoseconds& duration) const override     { std::this_thread::sleep_for(duration); }

		// Just in case someone skips try_unregister_cancellation_handler and calls this directly
		void unregister_cancellation_handler(cancellation_handler& h) const override
		{
			bool r = try_unregister_cancellation_handler(h);
			(void)r;
			RETHREAD_ASSERT(r, "Dummy cancellation token can't be cancelled!");
		}
	};

	class standalone_cancellation_token : public cancellation_token
	{
		mutable std::mutex              _mutex;
		mutable std::condition_variable _cv;
		bool                            _cancelled{false};
		bool                            _cancel_done{false};

	public:
		standalone_cancellation_token() = default;
		standalone_cancellation_token(const standalone_cancellation_token&) = delete;
		standalone_cancellation_token& operator =(const standalone_cancellation_token&) = delete;
		~standalone_cancellation_token() = default;

		void cancel()
		{
			std::unique_lock<std::mutex> l(_mutex);
			if (_cancelled)
				return;

			_cancelled = true;
			l.unlock();

			cancellation_handler* cancelHandler = _cancel_handler.exchange(HazardPointer());
			RETHREAD_ASSERT(cancelHandler != HazardPointer(), "_cancelled should protect from double-cancelling");

			if (cancelHandler)
			{
				RETHREAD_ANNOTATE_AFTER(std::addressof(_cancel_handler));
				RETHREAD_ANNOTATE_FORGET(std::addressof(_cancel_handler));
				cancelHandler->cancel();
				RETHREAD_ANNOTATE_BEFORE(cancelHandler);
			}

			l.lock();
			_cancel_done = true;
			_cv.notify_all();
		}

		void reset()
		{
			std::unique_lock<std::mutex> l(_mutex);
			RETHREAD_ASSERT((_cancel_handler.load() || _cancel_handler == HazardPointer()) && (_cancelled == _cancel_done), "Cancellation token is in use!");
			_cancelled = false;
			_cancel_done = false;
			_cancel_handler = nullptr;
		}

	protected:
		void do_sleep_for(const std::chrono::nanoseconds& duration) const override
		{
			std::unique_lock<std::mutex> l(_mutex);
			if (_cancelled)
				return;

			_cv.wait_for(l, duration);
		}

		void unregister_cancellation_handler(cancellation_handler& handler) const override
		{
			if (try_unregister_cancellation_handler(handler))
				return;

			std::unique_lock<std::mutex> l(_mutex);
			RETHREAD_ASSERT(_cancelled, "Wasn't cancelled!");
			RETHREAD_ASSERT(_cancel_handler.load() == HazardPointer(), "Wrong _cancel_handler");

			while (!_cancel_done)
				_cv.wait(l);

			RETHREAD_ANNOTATE_AFTER(std::addressof(handler));
			RETHREAD_ANNOTATE_FORGET(std::addressof(handler));
			l.unlock();
			handler.reset();
		}
	};

	namespace detail
	{
		template <typename TokenType_>
		struct cancellation_source_data
		{
			using tokens_intrusive_list = detail::intrusive_list<TokenType_>;

			std::mutex              _mutex;
			std::condition_variable _cv;
			bool                    _cancelled{false};
			bool                    _cancel_done{false};
			std::mutex              _tokens_mutex;
			tokens_intrusive_list   _tokens;
		};
	}

	class cancellation_token_source;

	class sourced_cancellation_token : public cancellation_token, public detail::intrusive_list_node
	{
		using data_ptr = std::shared_ptr<detail::cancellation_source_data<sourced_cancellation_token>>;

		data_ptr _data;

	public:
		sourced_cancellation_token(const sourced_cancellation_token& other) :
			cancellation_token(), intrusive_list_node(), _data(other._data)
		{ do_register(); }

		sourced_cancellation_token& operator =(const sourced_cancellation_token&) = delete;

		~sourced_cancellation_token()
		{
			RETHREAD_ASSERT(_cancel_handler.load() == nullptr || _cancel_handler == HazardPointer(), "Cancellation token is still in use!");
			do_unregister();
		}

	protected:
		void do_sleep_for(const std::chrono::nanoseconds& duration) const override
		{
			std::unique_lock<std::mutex> l(_data->_mutex);
			if (_data->_cancelled)
				return;

			_data->_cv.wait_for(l, duration);
		}

		void unregister_cancellation_handler(cancellation_handler& handler) const override
		{
			if (try_unregister_cancellation_handler(handler))
				return;

			std::unique_lock<std::mutex> l(_data->_mutex);
			RETHREAD_ASSERT(_data->_cancelled, "Wasn't cancelled!");
			RETHREAD_ASSERT(_cancel_handler == HazardPointer(), "Wrong _cancel_handler");

			while (!_data->_cancel_done)
				_data->_cv.wait(l);

			RETHREAD_ANNOTATE_AFTER(std::addressof(handler));
			RETHREAD_ANNOTATE_FORGET(std::addressof(handler));
			l.unlock();
			handler.reset();
		}

	private:
		sourced_cancellation_token(const data_ptr& data) : _data(data)
		{ do_register(); }

		void do_register()
		{
			std::unique_lock<std::mutex> l(_data->_tokens_mutex);
			_data->_tokens.push_back(*this);
		}

		void do_unregister()
		{
			std::unique_lock<std::mutex> l(_data->_tokens_mutex);
			_data->_tokens.erase(*this);
		}

		void cancel_impl(std::unique_lock<std::mutex>& l)
		{
			cancellation_handler* cancelHandler = _cancel_handler.exchange(HazardPointer());
			RETHREAD_ASSERT(cancelHandler != HazardPointer(), "_cancelled should protect from double-cancelling");

			if (!cancelHandler)
				return;

			RETHREAD_ANNOTATE_AFTER(std::addressof(_cancel_handler));
			RETHREAD_ANNOTATE_FORGET(std::addressof(_cancel_handler));
			{
				// We have to unlock this mutex because cancel by itself may lock some mutexes, thus leading to deadlock
				detail::reverse_lock<std::unique_lock<std::mutex>> ul(l);
				cancelHandler->cancel();
			}
			RETHREAD_ANNOTATE_BEFORE(cancelHandler);
		}

		friend class cancellation_token_source;
	};

	class cancellation_token_source
	{
		using data = detail::cancellation_source_data<sourced_cancellation_token>;
		using data_ptr = std::shared_ptr<data>;

		data_ptr _data;

	public:
		cancellation_token_source() : _data(std::make_shared<data>())
		{ }

		cancellation_token_source(const cancellation_token_source&) = delete;
		cancellation_token_source& operator =(const cancellation_token_source&) = delete;

		~cancellation_token_source()
		{ cancel(); }

		void cancel()
		{
			std::unique_lock<std::mutex> l(_data->_mutex);
			if (_data->_cancelled)
				return;

			_data->_cancelled = true;
			l.unlock();

			{
				std::unique_lock<std::mutex> l(_data->_tokens_mutex);
				for (sourced_cancellation_token& token : _data->_tokens)
					token.cancel_impl(l);
			}

			l.lock();
			_data->_cancel_done = true;
			_data->_cv.notify_all();
		}

		void reset()
		{ _data = std::make_shared<data>(); }

		sourced_cancellation_token create_token()
		{ return sourced_cancellation_token(_data); }
	};

	class cancellation_guard_base
	{
	protected:
		bool try_register(const cancellation_token& token, cancellation_handler& handler)
		{ return token.try_register_cancellation_handler(handler); }

		bool try_unregister(const cancellation_token& token, cancellation_handler& handler)
		{ return token.try_unregister_cancellation_handler(handler); }

		void unregister(const cancellation_token& token, cancellation_handler& handler)
		{ token.unregister_cancellation_handler(handler); }
	};

	class cancellation_guard : public cancellation_guard_base
	{
	private:
		const cancellation_token* _token;
		cancellation_handler*     _handler;

	public:
		cancellation_guard(const cancellation_guard&) = delete;
		cancellation_guard& operator =(const cancellation_guard&) = delete;

		cancellation_guard() :
			_token(nullptr), _handler(nullptr)
		{ }

		cancellation_guard(const cancellation_token& token, cancellation_handler& handler) :
			_token(nullptr), _handler(&handler)
		{
			if (try_register(token, handler))
				_token = &token;
		}

		cancellation_guard(cancellation_guard&& other) :
			_token(other._token), _handler(other._handler)
		{ other._token = nullptr; }

		~cancellation_guard()
		{
			if (!_token || RETHREAD_LIKELY(try_unregister(*_token, *_handler)))
				return;
			unregister(*_token, *_handler);
		}

		bool is_cancelled() const
		{ return !_token; }
	};
}

#endif
<commit_msg>Fixed naming<commit_after>#ifndef RETHREAD_CANCELLATION_TOKEN_H
#define RETHREAD_CANCELLATION_TOKEN_H

// Copyright (c) 2016, Boris Sazonov
//
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
// provided that the above copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include <rethread/detail/intrusive_list.h>
#include <rethread/detail/reverse_lock.hpp>
#include <rethread/detail/utility.hpp>

#include <atomic>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <stdexcept>
#include <thread>
#include <algorithm>

namespace rethread
{
	class cancellation_handler
	{
	protected:
		virtual ~cancellation_handler() { }

	public:
		/// @brief Should cancel blocking call
		virtual void cancel() = 0;

		/// @brief Should reset cancellation_handler to it's original state
		///        For each cancel() call, there's _exactly_ one reset() call
		virtual void reset() { }
	};

	class cancellation_token;

	namespace this_thread
	{
		template<typename Rep, typename Period>
		inline void sleep_for(const std::chrono::duration<Rep, Period>& duration, const cancellation_token& token);
	}

	class cancellation_token
	{
	protected:
		mutable std::atomic<cancellation_handler*> _cancel_handler{nullptr};

	public:
		bool is_cancelled() const
		{ return _cancel_handler.load(std::memory_order_relaxed) == HazardPointer(); }

		explicit operator bool() const
		{ return !is_cancelled(); }

	protected:
		virtual void do_sleep_for(const std::chrono::nanoseconds& duration) const = 0;

		/// @pre Handler is registered
		/// @post Handler is not registered
		/// @note Will invoke cancellation_handler::reset() if necessary
		virtual void unregister_cancellation_handler(cancellation_handler& handler) const = 0;

	protected:
		template<typename Rep, typename Period>
		void sleep_for(const std::chrono::duration<Rep, Period>& duration) const
		{ do_sleep_for(std::chrono::duration_cast<std::chrono::nanoseconds>(duration)); }

		/// @pre Handler is not registered
		/// @returns Whether handler was registered. If token was cancelled before registration, this method skips registration and returns false
		bool try_register_cancellation_handler(cancellation_handler& handler) const
		{
			cancellation_handler* h = _cancel_handler.exchange(&handler, std::memory_order_release);
			RETHREAD_ANNOTATE_BEFORE(std::addressof(_cancel_handler));
			if (RETHREAD_UNLIKELY(h != nullptr))
			{
				RETHREAD_ASSERT(h == HazardPointer(), "Cancellation handler already registered!");
				return false;
			}
			return true;
		}

		/// @pre Handler is registered
		/// @returns Whether unregistration succeed
		bool try_unregister_cancellation_handler(cancellation_handler& handler) const
		{
			cancellation_handler* h = _cancel_handler.exchange(nullptr, std::memory_order_acquire);
			if (RETHREAD_LIKELY(h == &handler))
				return true;

			RETHREAD_ASSERT(h == HazardPointer(), "Another token was registered!");
			_cancel_handler.exchange(h); // restore value
			return false;
		}

	protected:
		cancellation_token() = default;
		cancellation_token(const cancellation_token&) = delete;
		cancellation_token& operator =(const cancellation_token&) = delete;
		~cancellation_token() = default;

		// Dirty trick to optimize register/unregister down to one atomic exchange
		static cancellation_handler* HazardPointer()
		{ return reinterpret_cast<cancellation_handler*>(1); }

	private:
		friend class cancellation_guard_base;

		template<typename Rep, typename Period>
		friend void this_thread::sleep_for(const std::chrono::duration<Rep, Period>& duration, const cancellation_token& token);
	};

	class dummy_cancellation_token : public cancellation_token
	{
	public:
		dummy_cancellation_token() = default;
		// Base class can't be copied, but can default construct it, because dummy_cancellation_token may never cancelled state
		dummy_cancellation_token(const dummy_cancellation_token&) : cancellation_token() { }
		dummy_cancellation_token& operator =(const dummy_cancellation_token&) = delete;
		~dummy_cancellation_token() = default;

	protected:
		void do_sleep_for(const std::chrono::nanoseconds& duration) const override     { std::this_thread::sleep_for(duration); }

		// Just in case someone skips try_unregister_cancellation_handler and calls this directly
		void unregister_cancellation_handler(cancellation_handler& h) const override
		{
			bool r = try_unregister_cancellation_handler(h);
			(void)r;
			RETHREAD_ASSERT(r, "Dummy cancellation token can't be cancelled!");
		}
	};

	class standalone_cancellation_token : public cancellation_token
	{
		mutable std::mutex              _mutex;
		mutable std::condition_variable _cv;
		bool                            _cancelled{false};
		bool                            _cancel_done{false};

	public:
		standalone_cancellation_token() = default;
		standalone_cancellation_token(const standalone_cancellation_token&) = delete;
		standalone_cancellation_token& operator =(const standalone_cancellation_token&) = delete;
		~standalone_cancellation_token() = default;

		void cancel()
		{
			std::unique_lock<std::mutex> l(_mutex);
			if (_cancelled)
				return;

			_cancelled = true;
			l.unlock();

			cancellation_handler* cancelHandler = _cancel_handler.exchange(HazardPointer());
			RETHREAD_ASSERT(cancelHandler != HazardPointer(), "_cancelled should protect from double-cancelling");

			if (cancelHandler)
			{
				RETHREAD_ANNOTATE_AFTER(std::addressof(_cancel_handler));
				RETHREAD_ANNOTATE_FORGET(std::addressof(_cancel_handler));
				cancelHandler->cancel();
				RETHREAD_ANNOTATE_BEFORE(cancelHandler);
			}

			l.lock();
			_cancel_done = true;
			_cv.notify_all();
		}

		void reset()
		{
			std::unique_lock<std::mutex> l(_mutex);
			RETHREAD_ASSERT((_cancel_handler.load() || _cancel_handler == HazardPointer()) && (_cancelled == _cancel_done), "Cancellation token is in use!");
			_cancelled = false;
			_cancel_done = false;
			_cancel_handler = nullptr;
		}

	protected:
		void do_sleep_for(const std::chrono::nanoseconds& duration) const override
		{
			std::unique_lock<std::mutex> l(_mutex);
			if (_cancelled)
				return;

			_cv.wait_for(l, duration);
		}

		void unregister_cancellation_handler(cancellation_handler& handler) const override
		{
			if (try_unregister_cancellation_handler(handler))
				return;

			std::unique_lock<std::mutex> l(_mutex);
			RETHREAD_ASSERT(_cancelled, "Wasn't cancelled!");
			RETHREAD_ASSERT(_cancel_handler.load() == HazardPointer(), "Wrong _cancel_handler");

			while (!_cancel_done)
				_cv.wait(l);

			RETHREAD_ANNOTATE_AFTER(std::addressof(handler));
			RETHREAD_ANNOTATE_FORGET(std::addressof(handler));
			l.unlock();
			handler.reset();
		}
	};

	namespace detail
	{
		template <typename TokenType_>
		struct cancellation_source_data
		{
			using tokens_intrusive_list = detail::intrusive_list<TokenType_>;

			std::mutex              _mutex;
			std::condition_variable _cv;
			bool                    _cancelled{false};
			bool                    _cancel_done{false};
			std::mutex              _tokens_mutex;
			tokens_intrusive_list   _tokens;
		};
	}

	class cancellation_token_source;

	class sourced_cancellation_token : public cancellation_token, public detail::intrusive_list_node
	{
		using data_ptr = std::shared_ptr<detail::cancellation_source_data<sourced_cancellation_token>>;

		data_ptr _data;

	public:
		sourced_cancellation_token(const sourced_cancellation_token& other) :
			cancellation_token(), intrusive_list_node(), _data(other._data)
		{ do_register(); }

		sourced_cancellation_token& operator =(const sourced_cancellation_token&) = delete;

		~sourced_cancellation_token()
		{
			RETHREAD_ASSERT(_cancel_handler.load() == nullptr || _cancel_handler == HazardPointer(), "Cancellation token is still in use!");
			do_unregister();
		}

	protected:
		void do_sleep_for(const std::chrono::nanoseconds& duration) const override
		{
			std::unique_lock<std::mutex> l(_data->_mutex);
			if (_data->_cancelled)
				return;

			_data->_cv.wait_for(l, duration);
		}

		void unregister_cancellation_handler(cancellation_handler& handler) const override
		{
			if (try_unregister_cancellation_handler(handler))
				return;

			std::unique_lock<std::mutex> l(_data->_mutex);
			RETHREAD_ASSERT(_data->_cancelled, "Wasn't cancelled!");
			RETHREAD_ASSERT(_cancel_handler == HazardPointer(), "Wrong _cancel_handler");

			while (!_data->_cancel_done)
				_data->_cv.wait(l);

			RETHREAD_ANNOTATE_AFTER(std::addressof(handler));
			RETHREAD_ANNOTATE_FORGET(std::addressof(handler));
			l.unlock();
			handler.reset();
		}

	private:
		sourced_cancellation_token(const data_ptr& data) : _data(data)
		{ do_register(); }

		void do_register()
		{
			std::unique_lock<std::mutex> l(_data->_tokens_mutex);
			_data->_tokens.push_back(*this);
		}

		void do_unregister()
		{
			std::unique_lock<std::mutex> l(_data->_tokens_mutex);
			_data->_tokens.erase(*this);
		}

		void cancel_impl(std::unique_lock<std::mutex>& l)
		{
			cancellation_handler* cancelHandler = _cancel_handler.exchange(HazardPointer());
			RETHREAD_ASSERT(cancelHandler != HazardPointer(), "_cancelled should protect from double-cancelling");

			if (!cancelHandler)
				return;

			RETHREAD_ANNOTATE_AFTER(std::addressof(_cancel_handler));
			RETHREAD_ANNOTATE_FORGET(std::addressof(_cancel_handler));
			{
				// We have to unlock this mutex because cancel by itself may lock some mutexes, thus leading to deadlock
				detail::reverse_lock<std::unique_lock<std::mutex>> ul(l);
				cancelHandler->cancel();
			}
			RETHREAD_ANNOTATE_BEFORE(cancelHandler);
		}

		friend class cancellation_token_source;
	};

	class cancellation_token_source
	{
		using data = detail::cancellation_source_data<sourced_cancellation_token>;
		using data_ptr = std::shared_ptr<data>;

		data_ptr _data;

	public:
		cancellation_token_source() : _data(std::make_shared<data>())
		{ }

		cancellation_token_source(const cancellation_token_source&) = delete;
		cancellation_token_source& operator =(const cancellation_token_source&) = delete;

		~cancellation_token_source()
		{ cancel(); }

		void cancel()
		{
			std::unique_lock<std::mutex> l(_data->_mutex);
			if (_data->_cancelled)
				return;

			_data->_cancelled = true;
			l.unlock();

			{
				std::unique_lock<std::mutex> l2(_data->_tokens_mutex);
				for (sourced_cancellation_token& token : _data->_tokens)
					token.cancel_impl(l2);
			}

			l.lock();
			_data->_cancel_done = true;
			_data->_cv.notify_all();
		}

		void reset()
		{ _data = std::make_shared<data>(); }

		sourced_cancellation_token create_token()
		{ return sourced_cancellation_token(_data); }
	};

	class cancellation_guard_base
	{
	protected:
		bool try_register(const cancellation_token& token, cancellation_handler& handler)
		{ return token.try_register_cancellation_handler(handler); }

		bool try_unregister(const cancellation_token& token, cancellation_handler& handler)
		{ return token.try_unregister_cancellation_handler(handler); }

		void unregister(const cancellation_token& token, cancellation_handler& handler)
		{ token.unregister_cancellation_handler(handler); }
	};

	class cancellation_guard : public cancellation_guard_base
	{
	private:
		const cancellation_token* _token;
		cancellation_handler*     _handler;

	public:
		cancellation_guard(const cancellation_guard&) = delete;
		cancellation_guard& operator =(const cancellation_guard&) = delete;

		cancellation_guard() :
			_token(nullptr), _handler(nullptr)
		{ }

		cancellation_guard(const cancellation_token& token, cancellation_handler& handler) :
			_token(nullptr), _handler(&handler)
		{
			if (try_register(token, handler))
				_token = &token;
		}

		cancellation_guard(cancellation_guard&& other) :
			_token(other._token), _handler(other._handler)
		{ other._token = nullptr; }

		~cancellation_guard()
		{
			if (!_token || RETHREAD_LIKELY(try_unregister(*_token, *_handler)))
				return;
			unregister(*_token, *_handler);
		}

		bool is_cancelled() const
		{ return !_token; }
	};
}

#endif
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
// Name:      process.cpp
// Purpose:   Implementation of class wxExProcess
// Author:    Anton van Wezenbeek
// Copyright: (c) 2012 Anton van Wezenbeek
////////////////////////////////////////////////////////////////////////////////

#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/config.h>
#include <wx/txtstrm.h> // for wxTextInputStream
#include <wx/extension/process.h>
#include <wx/extension/configdlg.h>
#include <wx/extension/defs.h>
#include <wx/extension/shell.h>
#include <wx/extension/util.h> // for wxExConfigFirstOf

BEGIN_EVENT_TABLE(wxExProcess, wxProcess)
  EVT_MENU(ID_SHELL_COMMAND, wxExProcess::OnCommand)
  EVT_MENU(ID_SHELL_COMMAND_STOP, wxExProcess::OnCommand)
  EVT_TIMER(-1, wxExProcess::OnTimer)
END_EVENT_TABLE()

wxExSTCEntryDialog* wxExProcess::m_Dialog = NULL;
wxString wxExProcess::m_WorkingDirKey = _("Process folder");

wxExProcess::wxExProcess()
  : wxProcess(wxPROCESS_REDIRECT)
  , m_Timer(new wxTimer(this))
  , m_Busy(false)
  , m_Error(false)
{
  m_Command = wxExConfigFirstOf(_("Process"));
}

wxExProcess::~wxExProcess()
{
  delete m_Timer;
}

wxExProcess::wxExProcess(const wxExProcess& process)
{
  *this = process;
}

wxExProcess& wxExProcess::operator=(const wxExProcess& p)
{
  m_Error = p.m_Error;
  m_Output = p.m_Output;
  m_Timer = new wxTimer(this);

  return *this;
}

void wxExProcess::CheckInput()
{
  if (m_Busy)
  {
    return;
  }

  m_Busy = true;

  wxString output;

  if (IsInputAvailable())
  {
    wxTextInputStream tis(*GetInputStream());

    while (IsInputAvailable())
    {
      const wxChar c = tis.GetChar();

      if (c != 0)
      {
        output << c;
      }
    }
  }

  if (IsErrorAvailable())
  {
    wxTextInputStream tis(*GetErrorStream());

    while (IsErrorAvailable())
    {
      const wxChar c = tis.GetChar();

      if (c != 0)
      {
        output << c;
      }
    }
  }

  m_Busy = false;

  if (!output.empty())
  {
    ReportAdd(output);
  }
}

int wxExProcess::ConfigDialog(
  wxWindow* parent,
  const wxString& title,
  bool modal)
{
  std::vector<wxExConfigItem> v;

  v.push_back(wxExConfigItem(
    _("Process"),
    CONFIG_COMBOBOX,
    wxEmptyString,
    true));

  v.push_back(wxExConfigItem(
    m_WorkingDirKey,
    CONFIG_COMBOBOXDIR,
    wxEmptyString,
    true,
    1000));

  if (modal)
  {
    return wxExConfigDialog(parent,
      v,
      title).ShowModal();
  }
  else
  {
    wxExConfigDialog* dlg = new wxExConfigDialog(
      parent,
      v,
      title);

    return dlg->Show();
  }
}

long wxExProcess::Execute(
  const wxString& command_to_execute,
  int flags,
  const wxString& wd)
{
  struct wxExecuteEnv env;

  if (command_to_execute.empty())
  {
    if (wxExConfigFirstOf(_("Process")).empty())
    {
      if (ConfigDialog(wxTheApp->GetTopWindow()) == wxID_CANCEL)
      {
        return -2;
      }
    }

    m_Command = wxExConfigFirstOf(_("Process"));
    env.cwd = wxExConfigFirstOf(m_WorkingDirKey);
  }
  else
  {
    m_Command = command_to_execute;
    env.cwd = wd;
  }

  if (m_Dialog == NULL)
  {
    m_Dialog = new wxExSTCEntryDialog(
      wxTheApp->GetTopWindow(),
      m_Command,
      wxEmptyString,
      wxEmptyString,
      wxOK,
      true);

    m_Dialog->SetEventHandler(this);
    m_Dialog->GetSTCShell()->SetEventHandler(this);
  }

  m_Error = false;

  if (!(flags & wxEXEC_SYNC))
  {
    if (!ReportCreate())
    {
      return -1;
    }

    // For asynchronous execution, however, the return value is the process id and zero
    // value indicates that the command could not be executed
    const long pid = wxExecute(m_Command, flags, this, &env);

    if (pid > 0)
    {
      wxLogVerbose("Execute: " + m_Command);

      CheckInput();

      m_Timer->Start(100); // each 100 milliseconds
    }
    else
    {
      m_Error = true;
      m_Dialog->Hide();

      wxLogStatus(_("Could not execute") + ": " + m_Command);
    }

    return pid;
  }
  else
  {
    wxArrayString output;
    wxArrayString errors;
    long retValue;
    m_Output.clear();

    m_Dialog->GetSTCShell()->EnableShell(false);

    // Call wxExecute to execute the command and
    // collect the output and the errors.
    if ((retValue = wxExecute(
      m_Command,
      output,
      errors,
      flags,
      &env)) != -1)
    {
      wxLogVerbose("Execute: " + m_Command);
    }
    else
    {
      m_Error = true;
      wxLogStatus(_("Could not execute") + ": " + m_Command);
    }

    // Set output by converting array strings into normal strings.
    m_Output = wxJoin(errors, '\n', '\n') + wxJoin(output, '\n', '\n');

    return retValue;
  }
}

void wxExProcess::HideDialog()
{
  if (m_Dialog != NULL)
  {
    m_Dialog->Hide();
  }
}

bool wxExProcess::IsRunning() const
{
  if (GetPid() <= 0)
  {
    return false;
  }

  return Exists(GetPid());
}

wxKillError wxExProcess::Kill(wxSignal sig)
{
  // This seems necessary.
  if (!IsRunning())
  {
    wxLogStatus("no such process");
    return wxKILL_NO_PROCESS;
  }

  const wxKillError result = wxProcess::Kill(GetPid(), sig);

  switch (result)
  {
    case wxKILL_OK:
      wxLogStatus(_("Stopped"));
      DeletePendingEvents();
      m_Timer->Stop();
      m_Dialog->Hide();
      break;

    case wxKILL_BAD_SIGNAL:
      wxLogStatus("no such signal");
      break;

    case wxKILL_ACCESS_DENIED:
    	wxLogStatus("permission denied");
      break;

    case wxKILL_NO_PROCESS:
      wxLogStatus("no such process");
      break;

    case wxKILL_ERROR:
      wxLogStatus("another, unspecified error");
      break;

    default: wxFAIL;
  }

  return result;
}

void  wxExProcess::OnCommand(wxCommandEvent& event)
{
  switch (event.GetId())
  {
  case ID_SHELL_COMMAND:
    m_Dialog->GetSTCShell()->AddText(m_Dialog->GetSTCShell()->GetEOL());
    m_Dialog->GetSTCShell()->Prompt();

    if (IsRunning())
    {
      // send command to process
      wxOutputStream* os = GetOutputStream();

      if (os != NULL)
      {
        wxTextOutputStream os(*GetOutputStream());
        os.WriteString(event.GetString() + "\n");
      }
    }
    break;

  case ID_SHELL_COMMAND_STOP:
    if (IsRunning())
    {
      Kill();
    }
    break;

  default: wxFAIL; break;
  }
}

void wxExProcess::OnTerminate(int pid, int status)
{
  m_Timer->Stop();

  wxLogStatus(_("Ready"));
  wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, ID_TERMINATED_PROCESS);
  wxPostEvent(wxTheApp->GetTopWindow(), event);

  // Collect remaining input.
  CheckInput();
}

void wxExProcess::OnTimer(wxTimerEvent& event)
{
  CheckInput();
}

bool wxExProcess::ReportAdd(const wxString& line) const
{
  m_Dialog->Show();
  m_Dialog->GetSTCShell()->AddText(line);
  m_Dialog->GetSTCShell()->Prompt();
  return true;
}

bool wxExProcess::ReportCreate()
{
  m_Dialog->SetTitle(m_Command);
  m_Dialog->GetSTCShell()->ClearAll();
  m_Dialog->GetSTCShell()->Prompt();
  m_Dialog->GetSTCShell()->EnableShell(true);
  return true;
}

#if wxUSE_GUI
void wxExProcess::ShowOutput(const wxString& caption) const
{
  if (!m_Error)
  {
    if (m_Dialog != NULL)
    {
      m_Dialog->GetSTC()->SetText(m_Output);
      m_Dialog->SetTitle(caption.empty() ? m_Command: caption);
      m_Dialog->Show();
    }
    else if (!m_Output.empty())
    {
      wxLogMessage(m_Output);
    }
  }
  else
  {
    // Executing command failed, so no output,
    // show failing command.
    wxLogError(m_Command);
  }
}
#endif
<commit_msg>moved show dialog<commit_after>////////////////////////////////////////////////////////////////////////////////
// Name:      process.cpp
// Purpose:   Implementation of class wxExProcess
// Author:    Anton van Wezenbeek
// Copyright: (c) 2012 Anton van Wezenbeek
////////////////////////////////////////////////////////////////////////////////

#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/config.h>
#include <wx/txtstrm.h> // for wxTextInputStream
#include <wx/extension/process.h>
#include <wx/extension/configdlg.h>
#include <wx/extension/defs.h>
#include <wx/extension/shell.h>
#include <wx/extension/util.h> // for wxExConfigFirstOf

BEGIN_EVENT_TABLE(wxExProcess, wxProcess)
  EVT_MENU(ID_SHELL_COMMAND, wxExProcess::OnCommand)
  EVT_MENU(ID_SHELL_COMMAND_STOP, wxExProcess::OnCommand)
  EVT_TIMER(-1, wxExProcess::OnTimer)
END_EVENT_TABLE()

wxExSTCEntryDialog* wxExProcess::m_Dialog = NULL;
wxString wxExProcess::m_WorkingDirKey = _("Process folder");

wxExProcess::wxExProcess()
  : wxProcess(wxPROCESS_REDIRECT)
  , m_Timer(new wxTimer(this))
  , m_Busy(false)
  , m_Error(false)
{
  m_Command = wxExConfigFirstOf(_("Process"));
}

wxExProcess::~wxExProcess()
{
  delete m_Timer;
}

wxExProcess::wxExProcess(const wxExProcess& process)
{
  *this = process;
}

wxExProcess& wxExProcess::operator=(const wxExProcess& p)
{
  m_Error = p.m_Error;
  m_Output = p.m_Output;
  m_Timer = new wxTimer(this);

  return *this;
}

void wxExProcess::CheckInput()
{
  if (m_Busy)
  {
    return;
  }

  m_Busy = true;

  wxString output;

  if (IsInputAvailable())
  {
    wxTextInputStream tis(*GetInputStream());

    while (IsInputAvailable())
    {
      const wxChar c = tis.GetChar();

      if (c != 0)
      {
        output << c;
      }
    }
  }

  if (IsErrorAvailable())
  {
    wxTextInputStream tis(*GetErrorStream());

    while (IsErrorAvailable())
    {
      const wxChar c = tis.GetChar();

      if (c != 0)
      {
        output << c;
      }
    }
  }

  m_Busy = false;

  if (!output.empty())
  {
    ReportAdd(output);
  }
}

int wxExProcess::ConfigDialog(
  wxWindow* parent,
  const wxString& title,
  bool modal)
{
  std::vector<wxExConfigItem> v;

  v.push_back(wxExConfigItem(
    _("Process"),
    CONFIG_COMBOBOX,
    wxEmptyString,
    true));

  v.push_back(wxExConfigItem(
    m_WorkingDirKey,
    CONFIG_COMBOBOXDIR,
    wxEmptyString,
    true,
    1000));

  if (modal)
  {
    return wxExConfigDialog(parent,
      v,
      title).ShowModal();
  }
  else
  {
    wxExConfigDialog* dlg = new wxExConfigDialog(
      parent,
      v,
      title);

    return dlg->Show();
  }
}

long wxExProcess::Execute(
  const wxString& command_to_execute,
  int flags,
  const wxString& wd)
{
  struct wxExecuteEnv env;

  if (command_to_execute.empty())
  {
    if (wxExConfigFirstOf(_("Process")).empty())
    {
      if (ConfigDialog(wxTheApp->GetTopWindow()) == wxID_CANCEL)
      {
        return -2;
      }
    }

    m_Command = wxExConfigFirstOf(_("Process"));
    env.cwd = wxExConfigFirstOf(m_WorkingDirKey);
  }
  else
  {
    m_Command = command_to_execute;
    env.cwd = wd;
  }

  if (m_Dialog == NULL)
  {
    m_Dialog = new wxExSTCEntryDialog(
      wxTheApp->GetTopWindow(),
      m_Command,
      wxEmptyString,
      wxEmptyString,
      wxOK,
      true);

    m_Dialog->SetEventHandler(this);
    m_Dialog->GetSTCShell()->SetEventHandler(this);
  }

  m_Error = false;

  if (!(flags & wxEXEC_SYNC))
  {
    if (!ReportCreate())
    {
      return -1;
    }

    // For asynchronous execution, however, the return value is the process id and zero
    // value indicates that the command could not be executed
    const long pid = wxExecute(m_Command, flags, this, &env);

    if (pid > 0)
    {
      m_Dialog->Show();

      wxLogVerbose("Execute: " + m_Command);

      CheckInput();

      m_Timer->Start(100); // each 100 milliseconds
    }
    else
    {
      m_Dialog->Hide();

      wxLogStatus(_("Could not execute") + ": " + m_Command);

      m_Error = true;
    }

    return pid;
  }
  else
  {
    wxArrayString output;
    wxArrayString errors;
    long retValue;
    m_Output.clear();

    m_Dialog->GetSTCShell()->EnableShell(false);

    // Call wxExecute to execute the command and
    // collect the output and the errors.
    if ((retValue = wxExecute(
      m_Command,
      output,
      errors,
      flags,
      &env)) != -1)
    {
      wxLogVerbose("Execute: " + m_Command);
    }
    else
    {
      m_Error = true;
      wxLogStatus(_("Could not execute") + ": " + m_Command);
    }

    // Set output by converting array strings into normal strings.
    m_Output = wxJoin(errors, '\n', '\n') + wxJoin(output, '\n', '\n');

    return retValue;
  }
}

void wxExProcess::HideDialog()
{
  if (m_Dialog != NULL)
  {
    m_Dialog->Hide();
  }
}

bool wxExProcess::IsRunning() const
{
  if (GetPid() <= 0)
  {
    return false;
  }

  return Exists(GetPid());
}

wxKillError wxExProcess::Kill(wxSignal sig)
{
  // This seems necessary.
  if (!IsRunning())
  {
    wxLogStatus("no such process");
    return wxKILL_NO_PROCESS;
  }

  const wxKillError result = wxProcess::Kill(GetPid(), sig);

  switch (result)
  {
    case wxKILL_OK:
      wxLogStatus(_("Stopped"));
      DeletePendingEvents();
      m_Timer->Stop();
      m_Dialog->Hide();
      break;

    case wxKILL_BAD_SIGNAL:
      wxLogStatus("no such signal");
      break;

    case wxKILL_ACCESS_DENIED:
    	wxLogStatus("permission denied");
      break;

    case wxKILL_NO_PROCESS:
      wxLogStatus("no such process");
      break;

    case wxKILL_ERROR:
      wxLogStatus("another, unspecified error");
      break;

    default: wxFAIL;
  }

  return result;
}

void  wxExProcess::OnCommand(wxCommandEvent& event)
{
  switch (event.GetId())
  {
  case ID_SHELL_COMMAND:
    m_Dialog->GetSTCShell()->AddText(m_Dialog->GetSTCShell()->GetEOL());
    m_Dialog->GetSTCShell()->Prompt();

    if (IsRunning())
    {
      // send command to process
      wxOutputStream* os = GetOutputStream();

      if (os != NULL)
      {
        wxTextOutputStream os(*GetOutputStream());
        os.WriteString(event.GetString() + "\n");
      }
    }
    break;

  case ID_SHELL_COMMAND_STOP:
    if (IsRunning())
    {
      Kill();
    }
    break;

  default: wxFAIL; break;
  }
}

void wxExProcess::OnTerminate(int pid, int status)
{
  m_Timer->Stop();

  wxLogStatus(_("Ready"));
  wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, ID_TERMINATED_PROCESS);
  wxPostEvent(wxTheApp->GetTopWindow(), event);

  // Collect remaining input.
  CheckInput();
}

void wxExProcess::OnTimer(wxTimerEvent& event)
{
  CheckInput();
}

bool wxExProcess::ReportAdd(const wxString& line) const
{
  m_Dialog->GetSTCShell()->AddText(line);
  m_Dialog->GetSTCShell()->Prompt();
  return true;
}

bool wxExProcess::ReportCreate()
{
  m_Dialog->SetTitle(m_Command);
  m_Dialog->GetSTCShell()->ClearAll();
  m_Dialog->GetSTCShell()->Prompt();
  m_Dialog->GetSTCShell()->EnableShell(true);
  return true;
}

#if wxUSE_GUI
void wxExProcess::ShowOutput(const wxString& caption) const
{
  if (!m_Error)
  {
    if (m_Dialog != NULL)
    {
      m_Dialog->GetSTC()->SetText(m_Output);
      m_Dialog->SetTitle(caption.empty() ? m_Command: caption);
      m_Dialog->Show();
    }
    else if (!m_Output.empty())
    {
      wxLogMessage(m_Output);
    }
  }
  else
  {
    // Executing command failed, so no output,
    // show failing command.
    wxLogError(m_Command);
  }
}
#endif
<|endoftext|>