<commit_before>// Plugins.cpp
// Copyright (c) 2009, Dan Heeks
// This program is released under the BSD license. See the file COPYING for details.

#include "stdafx.h"
#include "Plugins.h"
#include "HeeksConfig.h"

enum
{
    ID_BUTTON_ADD,
	ID_BUTTON_EDIT,
	ID_BUTTON_REMOVE,
	ID_BUTTON_PLUGIN_BROWSE,
	ID_LISTBOX_CONTROL
};

BEGIN_EVENT_TABLE( CPluginItemDialog, wxDialog )
EVT_BUTTON( ID_BUTTON_PLUGIN_BROWSE, CPluginItemDialog::OnButtonBrowse )
EVT_BUTTON( wxID_OK, CPluginItemDialog::OnButtonOK )
EVT_BUTTON( wxID_CANCEL, CPluginItemDialog::OnButtonCancel )
END_EVENT_TABLE()

CPluginItemDialog::CPluginItemDialog(wxWindow *parent, const wxString& title, PluginData& pd):wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
	m_pd = &pd;

    m_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);

    // create buttons
    wxButton *button1 = new wxButton(m_panel, wxID_OK);
    wxButton *button2 = new wxButton(m_panel, wxID_CANCEL);

    wxBoxSizer *mainsizer = new wxBoxSizer( wxVERTICAL );

    wxFlexGridSizer *gridsizer = new wxFlexGridSizer(3, 5, 5);
	gridsizer->Add(new wxStaticText(m_panel, wxID_ANY, _("Name")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
	m_name_text_ctrl = new wxTextCtrl(m_panel, wxID_ANY, pd.name);
	gridsizer->Add(m_name_text_ctrl, wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL).Expand());
	gridsizer->Add(new wxStaticText(m_panel, wxID_ANY, _T("")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
	gridsizer->Add(new wxStaticText(m_panel, wxID_ANY, _("File Path")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
	m_path_text_ctrl = new wxTextCtrl(m_panel, wxID_ANY, pd.path, wxDefaultPosition, wxSize(400, 0));
	gridsizer->Add(m_path_text_ctrl, wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL).Expand());
	wxButton* browse_button = new wxButton(m_panel, ID_BUTTON_PLUGIN_BROWSE, _T("..."));
    gridsizer->Add(browse_button, wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));

	if(pd.hard_coded)
	{
		// don't allow editing of hard coded plugins
		m_name_text_ctrl->Enable(false);
		m_path_text_ctrl->Enable(false);
		browse_button->Enable(false);
	}

	gridsizer->AddGrowableCol(1, 1);

    wxBoxSizer *bottomsizer = new wxBoxSizer( wxHORIZONTAL );

    bottomsizer->Add( button1, 0, wxALL, 10 );
    bottomsizer->Add( button2, 0, wxALL, 10 );

    mainsizer->Add( gridsizer, wxSizerFlags().Align(wxALIGN_CENTER).Border(wxALL, 10).Expand() );
    mainsizer->Add( bottomsizer, wxSizerFlags().Align(wxALIGN_CENTER) );

    // tell frame to make use of sizer (or constraints, if any)
    m_panel->SetAutoLayout( true );
    m_panel->SetSizer( mainsizer );

#ifndef __WXWINCE__
    // don't allow frame to get smaller than what the sizers tell ye
    mainsizer->SetSizeHints( this );
#endif

    Show(true);
}

void CPluginItemDialog::OnButtonOK(wxCommandEvent& event)
{
	// it must have a name
	if(m_name_text_ctrl->GetValue().Len() == 0)
	{
		wxMessageBox(_("Plugin must have a name!"));
		m_name_text_ctrl->SetFocus();
		return;
	}

	// it must have a file path
	if(m_path_text_ctrl->GetValue().Len() == 0)
	{
		wxMessageBox(_("No file specified!"));
		m_path_text_ctrl->SetFocus();
		return;
	}

	m_pd->name = m_name_text_ctrl->GetValue();
	m_pd->path = m_path_text_ctrl->GetValue();

	EndModal(wxID_OK);
}

void CPluginItemDialog::OnButtonCancel(wxCommandEvent& event)
{
	EndModal(wxID_CANCEL);
}

void CPluginItemDialog::OnButtonBrowse(wxCommandEvent& event)
{
#ifdef WIN32
	wxString ext_str(_T("*.dll"));
#else
	wxString ext_str(_T("*.so*"));
#endif
	wxString wildcard_string = wxString(_("shared library files")) + _T(" |") + ext_str;

    wxFileDialog dialog(this, _("Choose shared library file"), wxEmptyString, wxEmptyString, wildcard_string);
    dialog.CentreOnParent();

    if (dialog.ShowModal() == wxID_OK)
    {
		m_path_text_ctrl->SetValue(dialog.GetPath());
    }
}

BEGIN_EVENT_TABLE( CPluginsDialog, wxDialog )
EVT_BUTTON( ID_BUTTON_ADD, CPluginsDialog::OnButtonAdd )
EVT_BUTTON( ID_BUTTON_EDIT, CPluginsDialog::OnButtonEdit )
EVT_UPDATE_UI(ID_BUTTON_EDIT, CPluginsDialog::OnUpdateEdit)
EVT_BUTTON( ID_BUTTON_REMOVE, CPluginsDialog::OnButtonRemove )
EVT_UPDATE_UI(ID_BUTTON_REMOVE, CPluginsDialog::OnUpdateRemove)
EVT_BUTTON( wxID_OK, CPluginsDialog::OnButtonOK )
EVT_BUTTON( wxID_CANCEL, CPluginsDialog::OnButtonCancel )
EVT_LISTBOX_DCLICK(ID_LISTBOX_CONTROL, CPluginsDialog::OnListboxDblClick)
END_EVENT_TABLE()

CPluginsDialog::CPluginsDialog(wxWindow *parent):wxDialog(parent, wxID_ANY, _("HeeksCAD plugin libraries"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
   // make a panel with some controls
    m_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);

	ReadPluginsList(m_plugins);

    CreateCheckListbox();

    // create buttons
    wxButton *button1 = new wxButton(m_panel, ID_BUTTON_ADD, _("New"));
    wxButton *button2 = new wxButton(m_panel, ID_BUTTON_EDIT, _("Edit"));
    wxButton *button3 = new wxButton(m_panel, ID_BUTTON_REMOVE, _("Delete"));
    wxButton *button4 = new wxButton(m_panel, wxID_OK);
    wxButton *button5 = new wxButton(m_panel, wxID_CANCEL);

    wxBoxSizer *mainsizer = new wxBoxSizer( wxVERTICAL );

    mainsizer->Add( m_pListBox, 1, wxGROW|wxALL, 10 );

    wxBoxSizer *bottomsizer = new wxBoxSizer( wxHORIZONTAL );

    bottomsizer->Add( button1, 0, wxALL, 10 );
    bottomsizer->Add( button2, 0, wxALL, 10 );
    bottomsizer->Add( button3, 0, wxALL, 10 );
    bottomsizer->Add( button4, 0, wxALL, 10 );
    bottomsizer->Add( button5, 0, wxALL, 10 );

    mainsizer->Add( bottomsizer, 0, wxCENTER );

    // tell frame to make use of sizer (or constraints, if any)
    m_panel->SetAutoLayout( true );
    m_panel->SetSizer( mainsizer );

#ifndef __WXWINCE__
    // don't allow frame to get smaller than what the sizers tell ye
    mainsizer->SetSizeHints( this );
#endif

    Show(true);
}

void ReadPluginsList(std::list<PluginData> &plugins)
{
	plugins.clear();

	::wxSetWorkingDirectory(wxGetApp().GetExeFolder());
	HeeksConfig plugins_config;
	plugins_config.SetPath(_T("/plugins"));

	wxString key;
	long Index;
	wxString str;

	bool entry_found = false;

	entry_found = plugins_config.GetFirstEntry(key, Index);

	while(entry_found)
	{
		plugins_config.Read(key, &str);

		PluginData pd;
		if(str[0] == '#')
		{
			str = str.Mid(1);
			pd.enabled = false;
		}
		else
		{
			pd.enabled = true;
		}

		pd.name = key;
		pd.path = str;

		plugins.push_back(pd);

		entry_found = plugins_config.GetNextEntry(key, Index);
	}
#ifdef ADDWIREPLUGIN_DEBUG
	{
		PluginData pd;
		pd.enabled = true;
		pd.hard_coded = true;
		pd.name = _T("WireHeeksCAD");
		pd.path = _T("$(SLDWORKSPATH)/p4c/bin/WireHeeksCADd.dll");
		plugins.push_back(pd);
	}
#endif
#ifdef ADDWIREPLUGIN
	{
		PluginData pd;
		pd.enabled = true;
		pd.hard_coded = true;
		pd.name = _T("WireHeeksCAD");
		pd.path = _T("..\Release\p2c\bin\WireHeeksCAD.dll");
		plugins.push_back(pd);
	}
#endif
}

void CPluginsDialog::CreateCheckListbox(long flags)
{
    wxString *astrChoices = new wxString[m_plugins.size()];
    unsigned int ui = 0;
	for ( std::list<PluginData>::iterator It = m_plugins.begin(); It != m_plugins.end(); It++, ui++ )
	{
		PluginData &pd = *It;
		astrChoices[ui] = pd.name;
	}

    m_pListBox = new wxCheckListBox
        (
         m_panel,               // parent
         ID_LISTBOX_CONTROL,       // control id
         wxPoint(10, 10),       // listbox poistion
         wxSize(400, 100),      // listbox size
         m_plugins.size(),		// number of strings
         astrChoices,           // array of strings
         flags
        );

    delete [] astrChoices;

    ui = 0;
	for ( std::list<PluginData>::iterator It = m_plugins.begin(); It != m_plugins.end(); It++, ui++ )
	{
		PluginData &pd = *It;
		if(pd.enabled)m_pListBox->Check(ui);
	}
}

void CPluginsDialog::EditSelected(unsigned int selection)
{
	unsigned int ui = 0;
	for ( std::list<PluginData>::iterator It = m_plugins.begin(); It != m_plugins.end(); It++, ui++ )
	{
		if(ui == selection)
		{
			PluginData &pd = *It;
			CPluginItemDialog dlg(this, _("Add New Plugin"), pd);
			if(dlg.ShowModal() == wxID_OK)
			{
				// updata the check list
				delete m_pListBox;
				CreateCheckListbox();
			}
		}
	}
}

void CPluginsDialog::OnButtonAdd(wxCommandEvent& event)
{
	PluginData pd;
	CPluginItemDialog dlg(this, _("Add New Plugin"), pd);
	if(dlg.ShowModal() == wxID_OK)
	{
		// add the new item
		m_plugins.push_back(pd);

		// updata the check list
		delete m_pListBox;
		CreateCheckListbox();
	}
}

void CPluginsDialog::OnButtonEdit(wxCommandEvent& event)
{
	unsigned int selection = m_pListBox->GetSelection();

	EditSelected(selection);
}

void CPluginsDialog::OnUpdateEdit( wxUpdateUIEvent& event )
{
	event.Enable(m_pListBox->GetSelection() >= 0);
}

void CPluginsDialog::OnUpdateRemove( wxUpdateUIEvent& event )
{
	event.Enable(m_pListBox->GetSelection() >= 0);
}

void CPluginsDialog::OnButtonRemove(wxCommandEvent& event)
{
	unsigned int selection = m_pListBox->GetSelection();
	unsigned int ui = 0;
	for ( std::list<PluginData>::iterator It = m_plugins.begin(); It != m_plugins.end(); It++, ui++ )
	{
		if(ui == selection)
		{
			m_plugins.erase(It);
			// updata the check list
			delete m_pListBox;
			CreateCheckListbox();
			break;
		}
	}
}

void CPluginsDialog::OnButtonOK(wxCommandEvent& event)
{
	::wxSetWorkingDirectory(wxGetApp().GetExeFolder());

	HeeksConfig plugins_config;

	plugins_config.DeleteGroup(_T("plugins"));
	plugins_config.SetPath(_T("/plugins"));

	unsigned int ui = 0;
	for ( std::list<PluginData>::iterator It = m_plugins.begin(); It != m_plugins.end(); It++, ui++ )
	{
		PluginData &pd = *It;
		if(pd.hard_coded)continue;
		pd.enabled = m_pListBox->IsChecked(ui);
		wxString value = pd.path;
		if(!(pd.enabled))value.Prepend(_T("#"));
		plugins_config.Write(pd.name, value);
	}

	EndModal(wxID_OK);
}

void CPluginsDialog::OnButtonCancel(wxCommandEvent& event)
{
	EndModal(wxID_CANCEL);
}

void CPluginsDialog::OnListboxDblClick(wxCommandEvent& WXUNUSED(event))
{
    int selection = m_pListBox->GetSelection();

	EditSelected(selection);
}
<commit_msg>I've changed my customers dll path to a sample one; you don't need my actual code here.<commit_after>// Plugins.cpp
// Copyright (c) 2009, Dan Heeks
// This program is released under the BSD license. See the file COPYING for details.

#include "stdafx.h"
#include "Plugins.h"
#include "HeeksConfig.h"

enum
{
    ID_BUTTON_ADD,
	ID_BUTTON_EDIT,
	ID_BUTTON_REMOVE,
	ID_BUTTON_PLUGIN_BROWSE,
	ID_LISTBOX_CONTROL
};

BEGIN_EVENT_TABLE( CPluginItemDialog, wxDialog )
EVT_BUTTON( ID_BUTTON_PLUGIN_BROWSE, CPluginItemDialog::OnButtonBrowse )
EVT_BUTTON( wxID_OK, CPluginItemDialog::OnButtonOK )
EVT_BUTTON( wxID_CANCEL, CPluginItemDialog::OnButtonCancel )
END_EVENT_TABLE()

CPluginItemDialog::CPluginItemDialog(wxWindow *parent, const wxString& title, PluginData& pd):wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
	m_pd = &pd;

    m_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);

    // create buttons
    wxButton *button1 = new wxButton(m_panel, wxID_OK);
    wxButton *button2 = new wxButton(m_panel, wxID_CANCEL);

    wxBoxSizer *mainsizer = new wxBoxSizer( wxVERTICAL );

    wxFlexGridSizer *gridsizer = new wxFlexGridSizer(3, 5, 5);
	gridsizer->Add(new wxStaticText(m_panel, wxID_ANY, _("Name")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
	m_name_text_ctrl = new wxTextCtrl(m_panel, wxID_ANY, pd.name);
	gridsizer->Add(m_name_text_ctrl, wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL).Expand());
	gridsizer->Add(new wxStaticText(m_panel, wxID_ANY, _T("")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
	gridsizer->Add(new wxStaticText(m_panel, wxID_ANY, _("File Path")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
	m_path_text_ctrl = new wxTextCtrl(m_panel, wxID_ANY, pd.path, wxDefaultPosition, wxSize(400, 0));
	gridsizer->Add(m_path_text_ctrl, wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL).Expand());
	wxButton* browse_button = new wxButton(m_panel, ID_BUTTON_PLUGIN_BROWSE, _T("..."));
    gridsizer->Add(browse_button, wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));

	if(pd.hard_coded)
	{
		// don't allow editing of hard coded plugins
		m_name_text_ctrl->Enable(false);
		m_path_text_ctrl->Enable(false);
		browse_button->Enable(false);
	}

	gridsizer->AddGrowableCol(1, 1);

    wxBoxSizer *bottomsizer = new wxBoxSizer( wxHORIZONTAL );

    bottomsizer->Add( button1, 0, wxALL, 10 );
    bottomsizer->Add( button2, 0, wxALL, 10 );

    mainsizer->Add( gridsizer, wxSizerFlags().Align(wxALIGN_CENTER).Border(wxALL, 10).Expand() );
    mainsizer->Add( bottomsizer, wxSizerFlags().Align(wxALIGN_CENTER) );

    // tell frame to make use of sizer (or constraints, if any)
    m_panel->SetAutoLayout( true );
    m_panel->SetSizer( mainsizer );

#ifndef __WXWINCE__
    // don't allow frame to get smaller than what the sizers tell ye
    mainsizer->SetSizeHints( this );
#endif

    Show(true);
}

void CPluginItemDialog::OnButtonOK(wxCommandEvent& event)
{
	// it must have a name
	if(m_name_text_ctrl->GetValue().Len() == 0)
	{
		wxMessageBox(_("Plugin must have a name!"));
		m_name_text_ctrl->SetFocus();
		return;
	}

	// it must have a file path
	if(m_path_text_ctrl->GetValue().Len() == 0)
	{
		wxMessageBox(_("No file specified!"));
		m_path_text_ctrl->SetFocus();
		return;
	}

	m_pd->name = m_name_text_ctrl->GetValue();
	m_pd->path = m_path_text_ctrl->GetValue();

	EndModal(wxID_OK);
}

void CPluginItemDialog::OnButtonCancel(wxCommandEvent& event)
{
	EndModal(wxID_CANCEL);
}

void CPluginItemDialog::OnButtonBrowse(wxCommandEvent& event)
{
#ifdef WIN32
	wxString ext_str(_T("*.dll"));
#else
	wxString ext_str(_T("*.so*"));
#endif
	wxString wildcard_string = wxString(_("shared library files")) + _T(" |") + ext_str;

    wxFileDialog dialog(this, _("Choose shared library file"), wxEmptyString, wxEmptyString, wildcard_string);
    dialog.CentreOnParent();

    if (dialog.ShowModal() == wxID_OK)
    {
		m_path_text_ctrl->SetValue(dialog.GetPath());
    }
}

BEGIN_EVENT_TABLE( CPluginsDialog, wxDialog )
EVT_BUTTON( ID_BUTTON_ADD, CPluginsDialog::OnButtonAdd )
EVT_BUTTON( ID_BUTTON_EDIT, CPluginsDialog::OnButtonEdit )
EVT_UPDATE_UI(ID_BUTTON_EDIT, CPluginsDialog::OnUpdateEdit)
EVT_BUTTON( ID_BUTTON_REMOVE, CPluginsDialog::OnButtonRemove )
EVT_UPDATE_UI(ID_BUTTON_REMOVE, CPluginsDialog::OnUpdateRemove)
EVT_BUTTON( wxID_OK, CPluginsDialog::OnButtonOK )
EVT_BUTTON( wxID_CANCEL, CPluginsDialog::OnButtonCancel )
EVT_LISTBOX_DCLICK(ID_LISTBOX_CONTROL, CPluginsDialog::OnListboxDblClick)
END_EVENT_TABLE()

CPluginsDialog::CPluginsDialog(wxWindow *parent):wxDialog(parent, wxID_ANY, _("HeeksCAD plugin libraries"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
   // make a panel with some controls
    m_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);

	ReadPluginsList(m_plugins);

    CreateCheckListbox();

    // create buttons
    wxButton *button1 = new wxButton(m_panel, ID_BUTTON_ADD, _("New"));
    wxButton *button2 = new wxButton(m_panel, ID_BUTTON_EDIT, _("Edit"));
    wxButton *button3 = new wxButton(m_panel, ID_BUTTON_REMOVE, _("Delete"));
    wxButton *button4 = new wxButton(m_panel, wxID_OK);
    wxButton *button5 = new wxButton(m_panel, wxID_CANCEL);

    wxBoxSizer *mainsizer = new wxBoxSizer( wxVERTICAL );

    mainsizer->Add( m_pListBox, 1, wxGROW|wxALL, 10 );

    wxBoxSizer *bottomsizer = new wxBoxSizer( wxHORIZONTAL );

    bottomsizer->Add( button1, 0, wxALL, 10 );
    bottomsizer->Add( button2, 0, wxALL, 10 );
    bottomsizer->Add( button3, 0, wxALL, 10 );
    bottomsizer->Add( button4, 0, wxALL, 10 );
    bottomsizer->Add( button5, 0, wxALL, 10 );

    mainsizer->Add( bottomsizer, 0, wxCENTER );

    // tell frame to make use of sizer (or constraints, if any)
    m_panel->SetAutoLayout( true );
    m_panel->SetSizer( mainsizer );

#ifndef __WXWINCE__
    // don't allow frame to get smaller than what the sizers tell ye
    mainsizer->SetSizeHints( this );
#endif

    Show(true);
}

void ReadPluginsList(std::list<PluginData> &plugins)
{
	plugins.clear();

	::wxSetWorkingDirectory(wxGetApp().GetExeFolder());
	HeeksConfig plugins_config;
	plugins_config.SetPath(_T("/plugins"));

	wxString key;
	long Index;
	wxString str;

	bool entry_found = false;

	entry_found = plugins_config.GetFirstEntry(key, Index);

	while(entry_found)
	{
		plugins_config.Read(key, &str);

		PluginData pd;
		if(str[0] == '#')
		{
			str = str.Mid(1);
			pd.enabled = false;
		}
		else
		{
			pd.enabled = true;
		}

		pd.name = key;
		pd.path = str;

		plugins.push_back(pd);

		entry_found = plugins_config.GetNextEntry(key, Index);
	}

	// add code to always start your dll here.
#ifdef MY_OWN_HARDCODED_PLUGIN
	{
		PluginData pd;
		pd.enabled = true;
		pd.hard_coded = true;
		pd.name = _T("MyPlugin");
		pd.path = _T("$(MYEXEPATH)/mypluing/PluginForHeeksCAD.dll");
		plugins.push_back(pd);
	}
#endif
}

void CPluginsDialog::CreateCheckListbox(long flags)
{
    wxString *astrChoices = new wxString[m_plugins.size()];
    unsigned int ui = 0;
	for ( std::list<PluginData>::iterator It = m_plugins.begin(); It != m_plugins.end(); It++, ui++ )
	{
		PluginData &pd = *It;
		astrChoices[ui] = pd.name;
	}

    m_pListBox = new wxCheckListBox
        (
         m_panel,               // parent
         ID_LISTBOX_CONTROL,       // control id
         wxPoint(10, 10),       // listbox poistion
         wxSize(400, 100),      // listbox size
         m_plugins.size(),		// number of strings
         astrChoices,           // array of strings
         flags
        );

    delete [] astrChoices;

    ui = 0;
	for ( std::list<PluginData>::iterator It = m_plugins.begin(); It != m_plugins.end(); It++, ui++ )
	{
		PluginData &pd = *It;
		if(pd.enabled)m_pListBox->Check(ui);
	}
}

void CPluginsDialog::EditSelected(unsigned int selection)
{
	unsigned int ui = 0;
	for ( std::list<PluginData>::iterator It = m_plugins.begin(); It != m_plugins.end(); It++, ui++ )
	{
		if(ui == selection)
		{
			PluginData &pd = *It;
			CPluginItemDialog dlg(this, _("Add New Plugin"), pd);
			if(dlg.ShowModal() == wxID_OK)
			{
				// updata the check list
				delete m_pListBox;
				CreateCheckListbox();
			}
		}
	}
}

void CPluginsDialog::OnButtonAdd(wxCommandEvent& event)
{
	PluginData pd;
	CPluginItemDialog dlg(this, _("Add New Plugin"), pd);
	if(dlg.ShowModal() == wxID_OK)
	{
		// add the new item
		m_plugins.push_back(pd);

		// updata the check list
		delete m_pListBox;
		CreateCheckListbox();
	}
}

void CPluginsDialog::OnButtonEdit(wxCommandEvent& event)
{
	unsigned int selection = m_pListBox->GetSelection();

	EditSelected(selection);
}

void CPluginsDialog::OnUpdateEdit( wxUpdateUIEvent& event )
{
	event.Enable(m_pListBox->GetSelection() >= 0);
}

void CPluginsDialog::OnUpdateRemove( wxUpdateUIEvent& event )
{
	event.Enable(m_pListBox->GetSelection() >= 0);
}

void CPluginsDialog::OnButtonRemove(wxCommandEvent& event)
{
	unsigned int selection = m_pListBox->GetSelection();
	unsigned int ui = 0;
	for ( std::list<PluginData>::iterator It = m_plugins.begin(); It != m_plugins.end(); It++, ui++ )
	{
		if(ui == selection)
		{
			m_plugins.erase(It);
			// updata the check list
			delete m_pListBox;
			CreateCheckListbox();
			break;
		}
	}
}

void CPluginsDialog::OnButtonOK(wxCommandEvent& event)
{
	::wxSetWorkingDirectory(wxGetApp().GetExeFolder());

	HeeksConfig plugins_config;

	plugins_config.DeleteGroup(_T("plugins"));
	plugins_config.SetPath(_T("/plugins"));

	unsigned int ui = 0;
	for ( std::list<PluginData>::iterator It = m_plugins.begin(); It != m_plugins.end(); It++, ui++ )
	{
		PluginData &pd = *It;
		if(pd.hard_coded)continue;
		pd.enabled = m_pListBox->IsChecked(ui);
		wxString value = pd.path;
		if(!(pd.enabled))value.Prepend(_T("#"));
		plugins_config.Write(pd.name, value);
	}

	EndModal(wxID_OK);
}

void CPluginsDialog::OnButtonCancel(wxCommandEvent& event)
{
	EndModal(wxID_CANCEL);
}

void CPluginsDialog::OnListboxDblClick(wxCommandEvent& WXUNUSED(event))
{
    int selection = m_pListBox->GetSelection();

	EditSelected(selection);
}
<|endoftext|>


<commit_before>#include <iostream>
#include <cmath>
#include "bond_angle.h"

using namespace std;

double ex_ji(double x_j, double x_i, double R_ji) {
    return ((-(x_j-x_i))/R_ji);
}

double ey_ji(double y_j, double y_i, double R_ji) {
    return ((-(y_j-y_i))/R_ji);
}

double ez_ji(double z_j, double z_i, double R_ji) {
    return ((-(z_j-z_i))/R_ji);
}

double ex_jk(double x_j, double x_k, double R_jk) {
    return ((-(x_j-x_k))/R_jk);
}

double ey_jk(double y_j, double y_k, double R_jk) {
    return ((-(y_j-y_k))/R_jk);
}

double ez_jk(double z_j, double z_k, double R_jk) {
    return ((-(z_j-z_k))/R_jk);
}

double ex_kl(double x_k, double x_l, double R_kl) {
    return ((-(x_k-x_l))/R_kl);
}

double ey_kl(double y_k, double y_l, double R_kl) {
    return ((-(y_k-y_l))/R_kl);
}

double ez_kl(double z_k, double z_l, double R_kl) {
    return ((-(z_k-z_l))/R_kl);
}

double phi_ijk(double ex_ji, double ey_ji, double ez_ji, double ex_jk, double ey_jk, double ez_jk) {
    return acos((ex_ji)*(ex_jk)+(ey_ji)*(ey_jk)+(ez_ji)*(ez_jk));
}

double phi_jkl(double ex_kj, double ey_kj, double ez_kj, double ex_kl, double ey_kl, double ez_kl) {
    return acos((ex_kj)*(ex_kl)+(ey_kj)*(ey_kl)+(ez_kj)*(ez_kl));
}
<commit_msg>added fstream<commit_after>#include <iostream>
#include <cmath>
#include <fstream>
#include "bond_angle.h"

using namespace std;

double ex_ji(double x_j, double x_i, double R_ji) {
    return ((-(x_j-x_i))/R_ji);
}

double ey_ji(double y_j, double y_i, double R_ji) {
    return ((-(y_j-y_i))/R_ji);
}

double ez_ji(double z_j, double z_i, double R_ji) {
    return ((-(z_j-z_i))/R_ji);
}

double ex_jk(double x_j, double x_k, double R_jk) {
    return ((-(x_j-x_k))/R_jk);
}

double ey_jk(double y_j, double y_k, double R_jk) {
    return ((-(y_j-y_k))/R_jk);
}

double ez_jk(double z_j, double z_k, double R_jk) {
    return ((-(z_j-z_k))/R_jk);
}

double ex_kl(double x_k, double x_l, double R_kl) {
    return ((-(x_k-x_l))/R_kl);
}

double ey_kl(double y_k, double y_l, double R_kl) {
    return ((-(y_k-y_l))/R_kl);
}

double ez_kl(double z_k, double z_l, double R_kl) {
    return ((-(z_k-z_l))/R_kl);
}

double phi_ijk(double ex_ji, double ey_ji, double ez_ji, double ex_jk, double ey_jk, double ez_jk) {
    return acos((ex_ji)*(ex_jk)+(ey_ji)*(ey_jk)+(ez_ji)*(ez_jk));
}

double phi_jkl(double ex_kj, double ey_kj, double ez_kj, double ex_kl, double ey_kl, double ez_kl) {
    return acos((ex_kj)*(ex_kl)+(ey_kj)*(ey_kl)+(ez_kj)*(ez_kl));
}
<|endoftext|>


<commit_before>#include "../group_flux_checker_sequential.h"

#include <memory>

#include <deal.II/lac/petsc_parallel_vector.h>

#include "../../test_helpers/gmock_wrapper.h"
#include "flux_checker_mock.h"

class GroupFluxCheckerSequentialTest : public ::testing::Test {
 protected:
  std::unique_ptr<bart::convergence::FluxCheckerI> tester_ptr;
  std::unique_ptr<bart::convergence::FluxCheckerMock> tester_mock;

  void SetUp() override;
  void MocksToPointers() {
    tester_ptr = std::move(tester_mock);
  };
};

void GroupFluxCheckerSequentialTest::SetUp() {
  tester_mock = std::make_unique<bart::convergence::FluxCheckerMock>();
}

// Tests where there are no mock calls
class GroupFluxCheckerSeqTestEmptyMock : public GroupFluxCheckerSequentialTest {
 protected:
  bart::convergence::GroupFluxCheckerSequential sequential_tester;
  void SetUp() override {
    MocksToPointers();
    sequential_tester.ProvideChecker(tester_ptr);
  }
};

TEST_F(GroupFluxCheckerSeqTestEmptyMock, Constructor) {
  EXPECT_EQ(tester_ptr, nullptr);
}

TEST_F(GroupFluxCheckerSeqTestEmptyMock, DifferentGroupSizes) {
  EXPECT_TRUE(true);
}

<commit_msg>added tests for different group sizes throwing error<commit_after>#include "../group_flux_checker_sequential.h"

#include <memory>

#include <deal.II/lac/petsc_parallel_vector.h>

#include "../../test_helpers/test_helper_functions.h"
#include "../../test_helpers/gmock_wrapper.h"
#include "flux_checker_mock.h"

class GroupFluxCheckerSequentialTest : public ::testing::Test {
 protected:
  std::unique_ptr<bart::convergence::FluxCheckerI> tester_ptr;
  std::unique_ptr<bart::convergence::FluxCheckerMock> tester_mock;

  bart::data::GroupFluxes current;
  bart::data::GroupFluxes previous;

  void SetUp() override;
  void FillGroupFluxes(bart::data::GroupFluxes &to_fill, int n_groups);
  void MocksToPointers() {
    tester_ptr = std::move(tester_mock);
  };
};

void GroupFluxCheckerSequentialTest::FillGroupFluxes(
    bart::data::GroupFluxes &to_fill, int n_groups) {
  for (int i = 0; i < n_groups; ++i) {
    bart::data::Flux flux;
    flux.reinit(MPI_COMM_WORLD, 5, 5);
    auto random_vector = btest::RandomVector(5, 0, 2);
    for (unsigned int j = 0; j < flux.size(); ++j)
      flux(j) = random_vector[j];
    flux.compress(dealii::VectorOperation::values::insert);
    to_fill[i] = flux;
  }
}

void GroupFluxCheckerSequentialTest::SetUp() {
  tester_mock = std::make_unique<bart::convergence::FluxCheckerMock>();
}

// Tests where there are no mock calls
class GroupFluxCheckerSeqTestEmptyMock : public GroupFluxCheckerSequentialTest {
 protected:
  bart::convergence::GroupFluxCheckerSequential sequential_tester;
  void SetUp() override {
    MocksToPointers();
    sequential_tester.ProvideChecker(tester_ptr);
  }
};

TEST_F(GroupFluxCheckerSeqTestEmptyMock, Constructor) {
  EXPECT_EQ(tester_ptr, nullptr);
}

TEST_F(GroupFluxCheckerSeqTestEmptyMock, EmptyGroups) {
  EXPECT_ANY_THROW(sequential_tester.isConverged(current, previous));
}

TEST_F(GroupFluxCheckerSeqTestEmptyMock, DifferentGroupSizes) {
  FillGroupFluxes(current, 5);
  FillGroupFluxes(previous, 4);
  EXPECT_ANY_THROW(sequential_tester.isConverged(current, previous));
}

<|endoftext|>


<commit_before>#include "output.h"
#include "astprinter.h"
#include "options.h"
#include "sourcebuffer.h"

static void error(const QString& err)
{
    QTextStream out(stderr);
    out << err;
    out.flush();
    exit(EXIT_FAILURE);
}

Output::Output(SourceBuffer* source)
    : m_source(source)
{
}

void Output::write(const QString& llvmIR)
{
    QString file = Options::instance()->outputFile();
    QString type = Options::instance()->outputType();
    if (type == "ast") {
        QTextStream out(stdout);
        QFile f(file);
        if (!file.isEmpty()) {
            if (f.open(QIODevice::WriteOnly))
                out.setDevice(&f);
        }
        f.close();
        ASTPrinter printer(m_source, &out);
        printer.walk();
    } else if (type == "llvm") {
        if (file.isEmpty()) {
            QTextStream out(stdout);
            out << llvmIR;
            out.flush();
        } else {
            QFile f(file);
            if (f.open(QIODevice::WriteOnly)) {
                QTextStream out(&f);
                out << llvmIR;
                out.flush();
                f.close();
            } else {
                error(QString("can not write to file $0").arg(file));
            }
        }
    } else if (type == "obj") {
        if (file.isEmpty()) {
            QFileInfo info(m_source->name());
            file = info.dir().path() + QDir::separator() + info.baseName() + ".o";
        }

        QProcess config;
        config.setProgram("llvm-config");
        config.setArguments(QStringList() << "--bindir");
        config.start(QIODevice::ReadOnly);
        if (!config.waitForFinished())
            error("could not find llvm-config tool");
        QString bindir = config.readAllStandardOutput();
        bindir.remove('\n');

        QProcess llc;
        llc.setProgram(bindir + QDir::separator() + "llc");
        llc.setArguments(QStringList() << "-filetype=obj");
        llc.start();
        if (!llc.waitForStarted())
            error("could not start llc tool");

        llc.write(llvmIR.toLatin1());

        llc.waitForBytesWritten();
        llc.closeWriteChannel();

        if (!llc.waitForFinished())
            error("llc tool crashed");

        QByteArray err = llc.readAllStandardError();
        if (!err.isEmpty())
            error("llc tool exited with error");

        QByteArray output = llc.readAllStandardOutput();

        QFile f(file);
        if (f.open(QIODevice::WriteOnly)) {
            f.write(output);
            f.flush();
            f.close();
        } else {
            error(QString("can not write to file $0").arg(file));
        }
    }
}
<commit_msg>Append a newline to the error message for llc output<commit_after>#include "output.h"
#include "astprinter.h"
#include "options.h"
#include "sourcebuffer.h"

static void error(const QString& err)
{
    QTextStream out(stderr);
    out << err << '\n';
    out.flush();
    exit(EXIT_FAILURE);
}

Output::Output(SourceBuffer* source)
    : m_source(source)
{
}

void Output::write(const QString& llvmIR)
{
    QString file = Options::instance()->outputFile();
    QString type = Options::instance()->outputType();
    if (type == "ast") {
        QTextStream out(stdout);
        QFile f(file);
        if (!file.isEmpty()) {
            if (f.open(QIODevice::WriteOnly))
                out.setDevice(&f);
        }
        f.close();
        ASTPrinter printer(m_source, &out);
        printer.walk();
    } else if (type == "llvm") {
        if (file.isEmpty()) {
            QTextStream out(stdout);
            out << llvmIR;
            out.flush();
        } else {
            QFile f(file);
            if (f.open(QIODevice::WriteOnly)) {
                QTextStream out(&f);
                out << llvmIR;
                out.flush();
                f.close();
            } else {
                error(QString("can not write to file $0").arg(file));
            }
        }
    } else if (type == "obj") {
        if (file.isEmpty()) {
            QFileInfo info(m_source->name());
            file = info.dir().path() + QDir::separator() + info.baseName() + ".o";
        }

        QProcess config;
        config.setProgram("llvm-config");
        config.setArguments(QStringList() << "--bindir");
        config.start(QIODevice::ReadOnly);
        if (!config.waitForFinished())
            error("could not find llvm-config tool");
        QString bindir = config.readAllStandardOutput();
        bindir.remove('\n');

        QProcess llc;
        llc.setProgram(bindir + QDir::separator() + "llc");
        llc.setArguments(QStringList() << "-filetype=obj");
        llc.start();
        if (!llc.waitForStarted())
            error("could not start llc tool");

        llc.write(llvmIR.toLatin1());

        llc.waitForBytesWritten();
        llc.closeWriteChannel();

        if (!llc.waitForFinished())
            error("llc tool crashed");

        QByteArray err = llc.readAllStandardError();
        if (!err.isEmpty())
            error("llc tool exited with error");

        QByteArray output = llc.readAllStandardOutput();

        QFile f(file);
        if (f.open(QIODevice::WriteOnly)) {
            f.write(output);
            f.flush();
            f.close();
        } else {
            error(QString("can not write to file $0").arg(file));
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>just testing<commit_after>#include <iostream>
#include <string>
#include <stdio.h>
#include "game.h"
#include "game_title.h"

void start_game(){
    cout << " hello you haave reached the right place high five5 " << endl;
}<|endoftext|>


<commit_before>#include "z2h/token.hpp"
#include "z2h/parser.hpp"

#include "ast.hpp"
#include "parser.h"
#include "bindpower.h"
#include "exceptions.h"

#include <map>
#include <vector>
#include <boost/regex.hpp>
#include <iostream>

namespace sota {

    std::vector<z2h::Ast *> SotaParser::Expressions(z2h::Symbol *end) {
        std::vector<z2h::Ast *> expressions;
        auto *eoe = symbolmap[SymbolType::EndOfExpression];
        auto *la1 = this->LookAhead1();
        while (end != la1->symbol) {
            auto *expression = this->Expression();
            expressions.push_back(expression);
            this->Consume(eoe);
            la1 = this->LookAhead1();
        }
        return expressions;
    }

    std::vector<z2h::Ast *> SotaParser::Statements(z2h::Symbol *end) {
        std::vector<z2h::Ast *> statements;
        return statements;
    }

    // scanners
    z2h::Token * SotaParser::SkippingScanner(z2h::Symbol *symbol, const std::string &source, size_t position) {
        auto *token = RegexScanner(symbol, source, position);
        if (token)
            token->skip = true;
        return token;
    }

    z2h::Token * SotaParser::RegexScanner(z2h::Symbol *symbol, const std::string &source, size_t position) {
        z2h::Token *token = nullptr;
        auto pattern = symbol->pattern;
        boost::smatch matches;
        boost::regex re("^(" + pattern + ")");
        if (boost::regex_search(source, matches, re)) {
            auto match = matches[0];
            token = new z2h::Token(symbol, match, position, match.length(), false);
        }
        return token;
    }

    z2h::Token * SotaParser::LiteralScanner(z2h::Symbol *symbol, const std::string &source, size_t position) {
        auto pattern = symbol->pattern;
        auto patternSize = pattern.size();
        if (source.size() >= patternSize && source.compare(0, patternSize, pattern) == 0) {
            return new z2h::Token(symbol, pattern, position, patternSize, false);
        }
        return nullptr;
    }

    z2h::Token * SotaParser::EosScanner(z2h::Symbol *symbol, const std::string &source, size_t position) {
        if (!nesting.size()) {
            return RegexScanner(symbol, source, position);
        }
        return nullptr;
    }

    z2h::Token * SotaParser::EoeScanner(z2h::Symbol *symbol, const std::string &source, size_t position) {
        if (nesting.size()) {
        }
        return RegexScanner(symbol, source, position);
    }

    z2h::Token * SotaParser::DentingScanner(z2h::Symbol *symbol, const std::string &source, size_t position) {
        return nullptr;
    }

    // std parsing functions
    z2h::Ast * SotaParser::NullptrStd() {
        return nullptr;
    }

    // nud parsing functions
    z2h::Ast * SotaParser::NullptrNud(z2h::Token *token) {
        return nullptr;
    }
    z2h::Ast * SotaParser::EndOfFileNud(z2h::Token *token) {
        return nullptr;
    }
    z2h::Ast * SotaParser::NewlineNud(z2h::Token *token) {
        std::cout << "newline" << std::endl;
        return new NewlineAst(token);
    }
    z2h::Ast * SotaParser::NumberNud(z2h::Token *token) {
        return new NumberAst(token);
    }
    z2h::Ast * SotaParser::IdentifierNud(z2h::Token *token) {
        return new IdentifierAst(token);
    }
    z2h::Ast * SotaParser::PrefixNud(z2h::Token *token) {
        z2h::Ast *right = Expression(BindPower::Unary);
        return new PrefixAst(token, right);
    }
    z2h::Ast * SotaParser::ParensNud(z2h::Token *token) {
        auto rp = symbolmap[SymbolType::RightParen];
        auto expressions = this->Expressions(rp);
        this->Consume(rp);
        z2h::Ast *ast = new ExpressionsAst(expressions);
        return ast;
    }
    z2h::Ast * SotaParser::IfThenElifElseNud(z2h::Token *token) {
        return nullptr;
    }

    // led parsing functions
    z2h::Ast * SotaParser::NullptrLed(z2h::Ast *left, z2h::Token *token) {
        return nullptr;
    }
    z2h::Ast * SotaParser::EndOfFileLed(z2h::Ast *left, z2h::Token *token) {
        return left;
    }
    z2h::Ast * SotaParser::ComparisonLed(z2h::Ast *left, z2h::Token *token) {
        return nullptr;
    }
    z2h::Ast * SotaParser::InfixLed(z2h::Ast *left, z2h::Token *token) {
        z2h::Ast *right = Expression(token->symbol->lbp);
        return new InfixAst(token, left, right);
    }
    z2h::Ast * SotaParser::PostfixLed(z2h::Ast *left, z2h::Token *token) {
        return nullptr;
    }
    z2h::Ast * SotaParser::AssignLed(z2h::Ast *left, z2h::Token *token) {
        //size_t distance = 1;
        //auto *la1 = this->LookAhead(distance);

        z2h::Ast *right = this->Expression(token->symbol->lbp);
        return new AssignAst(token, left, right);
    }
    z2h::Ast * SotaParser::FuncLed(z2h::Ast *left, z2h::Token *token) {
        std::cout << "FuncLed: token=" << *token << std::endl;
        return nullptr;
    }
    z2h::Ast * SotaParser::RegexLed(z2h::Ast *left, z2h::Token *token) {
        return nullptr;
    }
    z2h::Ast * SotaParser::CallLed(z2h::Ast *left, z2h::Token *token) {
        return nullptr;
    }
    z2h::Ast * SotaParser::TernaryLed(z2h::Ast *left, z2h::Token *token) {
        auto action = Expression();
        auto pair = ConditionalAst::Pair(left, action);
        auto expected = symbolmap[SotaParser::SymbolType::Colon];
        if (nullptr == Consume(expected))
            throw SotaException(__FILE__, __LINE__, "colon : expected");
        auto defaultAction = Expression();
        return new ConditionalAst(token, {pair}, defaultAction);
    }
    z2h::Ast * SotaParser::IfThenElseLed(z2h::Ast *left, z2h::Token *token) {
        auto predicate = Expression();
        auto pair = ConditionalAst::Pair(predicate, left);
        auto expected = symbolmap[SotaParser::SymbolType::Else];
        if (nullptr == Consume(expected))
            throw SotaException(__FILE__, __LINE__, "else expected");
        auto defaultAction = Expression();
        return new ConditionalAst(token, {pair}, defaultAction);
    }

    SotaParser::SotaParser() {

        #define T(k,p,b,s,t,n,l) { SymbolType::k, new z2h::Symbol(SymbolType::k, b, p, SCAN(s), STD(t), NUD(n), LED(l) ) },
        symbolmap = {
            SYMBOLS
        };
        #undef T
    }

    std::exception SotaParser::Exception(const char *file, size_t line, const std::string &message) {
        return SotaException(file, line, message);
    }

    std::vector<z2h::Symbol *> SotaParser::Symbols() {
        std::vector<z2h::Symbol *> symbols;
        for (auto kvp : symbolmap) {
            symbols.push_back(kvp.second);
        }
        return symbols;
    }
}
<commit_msg>added logic after Consuming symbols... -sai<commit_after>#include "z2h/token.hpp"
#include "z2h/parser.hpp"

#include "ast.hpp"
#include "parser.h"
#include "bindpower.h"
#include "exceptions.h"

#include <map>
#include <vector>
#include <boost/regex.hpp>
#include <iostream>

namespace sota {

    std::vector<z2h::Ast *> SotaParser::Expressions(z2h::Symbol *end) {
        std::vector<z2h::Ast *> expressions;
        auto *eoe = symbolmap[SymbolType::EndOfExpression];
        auto *la1 = this->LookAhead1();
        while (end != la1->symbol) {
            auto *expression = this->Expression();
            expressions.push_back(expression);
            if (!this->Consume(eoe)) {
                break;
            }
            la1 = this->LookAhead1();
        }
        return expressions;
    }

    std::vector<z2h::Ast *> SotaParser::Statements(z2h::Symbol *end) {
        std::vector<z2h::Ast *> statements;
        return statements;
    }

    // scanners
    z2h::Token * SotaParser::SkippingScanner(z2h::Symbol *symbol, const std::string &source, size_t position) {
        auto *token = RegexScanner(symbol, source, position);
        if (token)
            token->skip = true;
        return token;
    }

    z2h::Token * SotaParser::RegexScanner(z2h::Symbol *symbol, const std::string &source, size_t position) {
        z2h::Token *token = nullptr;
        auto pattern = symbol->pattern;
        boost::smatch matches;
        boost::regex re("^(" + pattern + ")");
        if (boost::regex_search(source, matches, re)) {
            auto match = matches[0];
            token = new z2h::Token(symbol, match, position, match.length(), false);
        }
        return token;
    }

    z2h::Token * SotaParser::LiteralScanner(z2h::Symbol *symbol, const std::string &source, size_t position) {
        auto pattern = symbol->pattern;
        auto patternSize = pattern.size();
        if (source.size() >= patternSize && source.compare(0, patternSize, pattern) == 0) {
            return new z2h::Token(symbol, pattern, position, patternSize, false);
        }
        return nullptr;
    }

    z2h::Token * SotaParser::EosScanner(z2h::Symbol *symbol, const std::string &source, size_t position) {
        if (!nesting.size()) {
            return RegexScanner(symbol, source, position);
        }
        return nullptr;
    }

    z2h::Token * SotaParser::EoeScanner(z2h::Symbol *symbol, const std::string &source, size_t position) {
        if (nesting.size()) {
        }
        return RegexScanner(symbol, source, position);
    }

    z2h::Token * SotaParser::DentingScanner(z2h::Symbol *symbol, const std::string &source, size_t position) {
        return nullptr;
    }

    // std parsing functions
    z2h::Ast * SotaParser::NullptrStd() {
        return nullptr;
    }

    // nud parsing functions
    z2h::Ast * SotaParser::NullptrNud(z2h::Token *token) {
        return nullptr;
    }
    z2h::Ast * SotaParser::EndOfFileNud(z2h::Token *token) {
        return nullptr;
    }
    z2h::Ast * SotaParser::NewlineNud(z2h::Token *token) {
        std::cout << "newline" << std::endl;
        return new NewlineAst(token);
    }
    z2h::Ast * SotaParser::NumberNud(z2h::Token *token) {
        return new NumberAst(token);
    }
    z2h::Ast * SotaParser::IdentifierNud(z2h::Token *token) {
        return new IdentifierAst(token);
    }
    z2h::Ast * SotaParser::PrefixNud(z2h::Token *token) {
        z2h::Ast *right = Expression(BindPower::Unary);
        return new PrefixAst(token, right);
    }
    z2h::Ast * SotaParser::ParensNud(z2h::Token *token) {
        auto rp = symbolmap[SymbolType::RightParen];
        auto expressions = this->Expressions(rp);
        if (!this->Consume(rp)) {
            std::cout << "RightParen not consumed" << std::endl;
        }
        z2h::Ast *ast = new ExpressionsAst(expressions);
        return ast;
    }
    z2h::Ast * SotaParser::IfThenElifElseNud(z2h::Token *token) {
        return nullptr;
    }

    // led parsing functions
    z2h::Ast * SotaParser::NullptrLed(z2h::Ast *left, z2h::Token *token) {
        return nullptr;
    }
    z2h::Ast * SotaParser::EndOfFileLed(z2h::Ast *left, z2h::Token *token) {
        return left;
    }
    z2h::Ast * SotaParser::ComparisonLed(z2h::Ast *left, z2h::Token *token) {
        return nullptr;
    }
    z2h::Ast * SotaParser::InfixLed(z2h::Ast *left, z2h::Token *token) {
        z2h::Ast *right = Expression(token->symbol->lbp);
        return new InfixAst(token, left, right);
    }
    z2h::Ast * SotaParser::PostfixLed(z2h::Ast *left, z2h::Token *token) {
        return nullptr;
    }
    z2h::Ast * SotaParser::AssignLed(z2h::Ast *left, z2h::Token *token) {
        //size_t distance = 1;
        //auto *la1 = this->LookAhead(distance);

        z2h::Ast *right = this->Expression(token->symbol->lbp);
        return new AssignAst(token, left, right);
    }
    z2h::Ast * SotaParser::FuncLed(z2h::Ast *left, z2h::Token *token) {
        std::cout << "FuncLed: token=" << *token << std::endl;
        return nullptr;
    }
    z2h::Ast * SotaParser::RegexLed(z2h::Ast *left, z2h::Token *token) {
        return nullptr;
    }
    z2h::Ast * SotaParser::CallLed(z2h::Ast *left, z2h::Token *token) {
        return nullptr;
    }
    z2h::Ast * SotaParser::TernaryLed(z2h::Ast *left, z2h::Token *token) {
        auto action = Expression();
        auto pair = ConditionalAst::Pair(left, action);
        auto expected = symbolmap[SotaParser::SymbolType::Colon];
        if (nullptr == Consume(expected))
            throw SotaException(__FILE__, __LINE__, "colon : expected");
        auto defaultAction = Expression();
        return new ConditionalAst(token, {pair}, defaultAction);
    }
    z2h::Ast * SotaParser::IfThenElseLed(z2h::Ast *left, z2h::Token *token) {
        auto predicate = Expression();
        auto pair = ConditionalAst::Pair(predicate, left);
        auto expected = symbolmap[SotaParser::SymbolType::Else];
        if (nullptr == Consume(expected))
            throw SotaException(__FILE__, __LINE__, "else expected");
        auto defaultAction = Expression();
        return new ConditionalAst(token, {pair}, defaultAction);
    }

    SotaParser::SotaParser() {

        #define T(k,p,b,s,t,n,l) { SymbolType::k, new z2h::Symbol(SymbolType::k, b, p, SCAN(s), STD(t), NUD(n), LED(l) ) },
        symbolmap = {
            SYMBOLS
        };
        #undef T
    }

    std::exception SotaParser::Exception(const char *file, size_t line, const std::string &message) {
        return SotaException(file, line, message);
    }

    std::vector<z2h::Symbol *> SotaParser::Symbols() {
        std::vector<z2h::Symbol *> symbols;
        for (auto kvp : symbolmap) {
            symbols.push_back(kvp.second);
        }
        return symbols;
    }
}
<|endoftext|>


<commit_before>#include "mainwindow.h"
#include <QPen>
#include <QGraphicsRectItem>
#include <QGraphicsEllipseItem>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    // specify app window geometry. 1920x1080 screen rez yields a 0.56 pixel ratio
    setGeometry(56,100,600,600);

    // set up the scene
    // traditionally, member objects start with the letter 'm'
    mGraphicsScene = new QGraphicsScene(0, 0, 400, 400);

    // set up the view
    mGraphicsView = new QGraphicsView(mGraphicsScene, this);
    mGraphicsView->setGeometry(0, 0, 600, 600);
    mGraphicsView->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
    mGraphicsView->setDragMode(QGraphicsView::ScrollHandDrag);

    // add a blue box that is the size of the scene
    QPen pen;
    pen.setColor(QColor(Qt::blue));
    pen.setWidth(2);
    mGraphicsScene->addRect(0, 0, mGraphicsScene->width(), mGraphicsScene->height(), pen);

    // these ints will go out of scope after constructor is called because they are not
    // defined in the mainwindow class declaration of the mainwindow.h file:
    int x = 100, y = 100, w = 10, h = 10;

    // add a black circle known as mEllipseItem to the scene
    mEllipseItem = new QGraphicsEllipseItem(x,y,w,h);
    pen.setColor(QColor(Qt::black));
    pen.setWidth(1);
    mEllipseItem->setPen(pen);
    mGraphicsScene->addItem(mEllipseItem);

    // how often we update the scene, in milliseconds:
    mUpdateIntervalMS = 60;

    // start the signal/slot timer, which invokes gameTick() every mUpdateIntervalMS milliseconds
    startGameLoopTimer();
}

MainWindow::~MainWindow()
{
    // this is the destructor code for the MainWindow object
    if (mGraphicsView != NULL)
    {
        delete mGraphicsView;
        mGraphicsView = NULL;
    }
    if (mGraphicsScene != NULL)
    {
        delete mGraphicsScene;
        mGraphicsScene = NULL;
    }
}

void MainWindow::startGameLoopTimer()
{
    // associate the signal timeout() to the slot gameTick(), and start our update timer
    QObject::connect(&mTimer, SIGNAL(timeout()), this, SLOT(gameTick()));
    mTimer.start(mUpdateIntervalMS);
}

void MainWindow::gameTick()
{
    // this code makes whatever changes to the scene you want to make, every gametick.
    // if you want to change what the animation does, you can do that here, or add new
    // functions (methods) and call them from here.

    // Change this code to make the circle stay within the blue box! :-D You could have
    // the circle just stop moving when it reaches the edge of the scene at x,y == 400,400
    // or you could make the circle change direction somehow when x or y get outside of
    // the range of 0 to 400.  Or think of some other way the circle should move!

    // you can uncomment the following line to produce console output every gametick.
    //qDebug() << "MainWindow::gameTick(): invoked\n";
    // ...this will generate around 16 messages per second at 60ms gametick.

    mEllipseItem->setX(mEllipseItem->x()+1);
    mEllipseItem->setY(mEllipseItem->y()+1);
    mGraphicsScene->advance();
}
<commit_msg>most of the important code is in this file<commit_after>#include "mainwindow.h"
#include <QPen>
#include <QGraphicsRectItem>
#include <QGraphicsEllipseItem>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    // specify app window geometry. 1920x1080 screen rez yields a 0.56 pixel ratio
    setGeometry(56,100,600,600);

    // set up the scene
    // traditionally, member objects start with the letter 'm'
    mGraphicsScene = new QGraphicsScene(0, 0, 400, 400);

    // set up the view
    mGraphicsView = new QGraphicsView(mGraphicsScene, this);
    mGraphicsView->setGeometry(0, 0, 600, 600);
    mGraphicsView->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
    mGraphicsView->setDragMode(QGraphicsView::ScrollHandDrag);

    // add a blue box that is the size of the scene
    QPen pen;
    pen.setColor(QColor(Qt::blue));
    pen.setWidth(2);
    mGraphicsScene->addRect(0, 0, mGraphicsScene->width(), mGraphicsScene->height(), pen);

    // these ints will go out of scope after constructor is called because they are not
    // defined in the mainwindow class declaration of the mainwindow.h file:
    int x = 100, y = 100, w = 10, h = 10;

    // add a black circle known as mEllipseItem to the scene
    mEllipseItem = new QGraphicsEllipseItem(x,y,w,h);
    pen.setColor(QColor(Qt::black));
    pen.setWidth(1);
    mEllipseItem->setPen(pen);
    mGraphicsScene->addItem(mEllipseItem);

    // how often we update the scene, in milliseconds:
    mUpdateIntervalMS = 60;

    // start the signal/slot timer, which invokes gameTick() every mUpdateIntervalMS milliseconds
    startGameLoopTimer();
}

MainWindow::~MainWindow()
{
    // this is the destructor code for the MainWindow object
    if (mGraphicsView != NULL)
    {
        delete mGraphicsView;
        mGraphicsView = NULL;
    }
    if (mGraphicsScene != NULL)
    {
        delete mGraphicsScene;
        mGraphicsScene = NULL;
    }
}

void MainWindow::startGameLoopTimer()
{
    // associate the signal timeout() to the slot gameTick(), and start our update timer
    QObject::connect(&mTimer, SIGNAL(timeout()), this, SLOT(gameTick()));
    mTimer.start(mUpdateIntervalMS);
}

void MainWindow::gameTick()
{
    // this code makes whatever changes to the scene you want to make, every gametick.
    // if you want to change what the animation does, you can do that here, or add new
    // functions (methods) and call them from here.

    // Change this code to make the circle stay within the blue box! :-D You could have
    // the circle just stop moving when it reaches the edge of the scene at x,y == 400,400
    // or you could make the circle change direction somehow when x or y get outside of
    // the range of 0 to 400.  Or think of some other way the circle should move!

    // you can uncomment the following line to produce console output every gametick.
    //qDebug() << "MainWindow::gameTick(): invoked\n";
    //  ...this will generate around 16 messages per second at 60ms gametick.

    mEllipseItem->setX(mEllipseItem->x()+1);
    mEllipseItem->setY(mEllipseItem->y()+1);
    mGraphicsScene->advance();
}
<|endoftext|>


<commit_before>/*
 * Call the translation server.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "translate_client.hxx"
#include "translate_quark.hxx"
#include "translate_parser.hxx"
#include "translate_request.hxx"
#include "TranslateHandler.hxx"
#include "buffered_socket.hxx"
#include "please.hxx"
#include "growing_buffer.hxx"
#include "stopwatch.hxx"
#include "GException.hxx"
#include "gerrno.h"
#include "pool.hxx"
#include "util/Cancellable.hxx"
#include "util/RuntimeError.hxx"

#include <socket/address.h>

#include <glib.h>

#include <stdexcept>

#include <assert.h>
#include <string.h>
#include <errno.h>

static const uint8_t PROTOCOL_VERSION = 3;

struct TranslateClient final : Cancellable {
    struct pool &pool;

    Stopwatch *const stopwatch;

    BufferedSocket socket;
    struct lease_ref lease_ref;

    /** the marshalled translate request */
    GrowingBufferReader request_buffer;
    GrowingBufferReader request;

    const TranslateHandler &handler;
    void *handler_ctx;

    TranslateParser parser;

    TranslateClient(struct pool &p, EventLoop &event_loop,
                    int fd, Lease &lease,
                    const TranslateRequest &request2,
                    GrowingBuffer &&_request,
                    const TranslateHandler &_handler, void *_ctx,
                    CancellablePointer &cancel_ptr);

    void ReleaseSocket(bool reuse);
    void Release(bool reuse);

    void Fail(GError *error);

    BufferedResult Feed(const uint8_t *data, size_t length);

    /* virtual methods from class Cancellable */
    void Cancel() override {
        stopwatch_event(stopwatch, "cancel");
        Release(false);
    }
};

static constexpr struct timeval translate_read_timeout = {
    .tv_sec = 60,
    .tv_usec = 0,
};

static constexpr struct timeval translate_write_timeout = {
    .tv_sec = 10,
    .tv_usec = 0,
};

void
TranslateClient::ReleaseSocket(bool reuse)
{
    assert(socket.IsConnected());

    stopwatch_dump(stopwatch);

    socket.Abandon();
    socket.Destroy();

    p_lease_release(lease_ref, reuse, pool);
}

/**
 * Release resources held by this object: the event object, the socket
 * lease, and the pool reference.
 */
void
TranslateClient::Release(bool reuse)
{
    ReleaseSocket(reuse);
    pool_unref(&pool);
}

void
TranslateClient::Fail(GError *error)
{
    stopwatch_event(stopwatch, "error");

    ReleaseSocket(false);

    handler.error(error, handler_ctx);
    pool_unref(&pool);
}

/*
 * request marshalling
 *
 */

static void
write_packet_n(GrowingBuffer *gb, uint16_t command,
               const void *payload, size_t length)
{
    static struct beng_translation_header header;

    if (length >= 0xffff)
        throw FormatRuntimeError("payload for translate command %u too large",
                                 command);

    header.length = (uint16_t)length;
    header.command = command;

    gb->Write(&header, sizeof(header));
    if (length > 0)
        gb->Write(payload, length);
}

static void
write_packet(GrowingBuffer *gb, uint16_t command, const char *payload)
{
    write_packet_n(gb, command, payload,
                   payload != nullptr ? strlen(payload) : 0);
}

template<typename T>
static void
write_buffer(GrowingBuffer *gb, uint16_t command,
             ConstBuffer<T> buffer)
{
    auto b = buffer.ToVoid();
    write_packet_n(gb, command, b.data, b.size);
}

/**
 * Forward the command to write_packet() only if #payload is not nullptr.
 */
static void
write_optional_packet(GrowingBuffer *gb, uint16_t command, const char *payload)
{
    if (payload != nullptr)
        write_packet(gb, command, payload);
}

template<typename T>
static void
write_optional_buffer(GrowingBuffer *gb, uint16_t command,
                      ConstBuffer<T> buffer)
{
    if (!buffer.IsNull())
        write_buffer(gb, command, buffer);
}

static void
write_short(GrowingBuffer *gb, uint16_t command, uint16_t payload)
{
    write_packet_n(gb, command, &payload, sizeof(payload));
}

static void
write_sockaddr(GrowingBuffer *gb,
               uint16_t command, uint16_t command_string,
               SocketAddress address)
{
    assert(!address.IsNull());

    char address_string[1024];
    write_packet_n(gb, command,
                   address.GetAddress(), address.GetSize());

    if (socket_address_to_string(address_string, sizeof(address_string),
                                 address.GetAddress(), address.GetSize()))
        write_packet(gb, command_string, address_string);
}

static void
write_optional_sockaddr(GrowingBuffer *gb,
                        uint16_t command, uint16_t command_string,
                        SocketAddress address)
{
    if (!address.IsNull())
        write_sockaddr(gb, command, command_string, address);
}

static GrowingBuffer
marshal_request(struct pool &pool, const TranslateRequest &request)
{
    GrowingBuffer gb(pool, 512);

    write_packet_n(&gb, TRANSLATE_BEGIN,
                   &PROTOCOL_VERSION, sizeof(PROTOCOL_VERSION));
    write_optional_buffer(&gb, TRANSLATE_ERROR_DOCUMENT,
                          request.error_document);

    if (request.error_document_status != 0)
        write_short(&gb, TRANSLATE_STATUS,
                    request.error_document_status);

    write_optional_packet(&gb, TRANSLATE_LISTENER_TAG,
                          request.listener_tag);
    write_optional_sockaddr(&gb, TRANSLATE_LOCAL_ADDRESS,
                            TRANSLATE_LOCAL_ADDRESS_STRING,
                            request.local_address);
    write_optional_packet(&gb, TRANSLATE_REMOTE_HOST,
                          request.remote_host);
    write_optional_packet(&gb, TRANSLATE_HOST, request.host);
    write_optional_packet(&gb, TRANSLATE_USER_AGENT, request.user_agent);
    write_optional_packet(&gb, TRANSLATE_UA_CLASS, request.ua_class);
    write_optional_packet(&gb, TRANSLATE_LANGUAGE, request.accept_language);
    write_optional_packet(&gb, TRANSLATE_AUTHORIZATION, request.authorization);
    write_optional_packet(&gb, TRANSLATE_URI, request.uri);
    write_optional_packet(&gb, TRANSLATE_ARGS, request.args);
    write_optional_packet(&gb, TRANSLATE_QUERY_STRING, request.query_string);
    write_optional_packet(&gb, TRANSLATE_WIDGET_TYPE, request.widget_type);
    write_optional_buffer(&gb, TRANSLATE_SESSION, request.session);
    write_optional_buffer(&gb, TRANSLATE_INTERNAL_REDIRECT,
                          request.internal_redirect);
    write_optional_buffer(&gb, TRANSLATE_CHECK, request.check);
    write_optional_buffer(&gb, TRANSLATE_AUTH, request.auth);
    write_optional_buffer(&gb, TRANSLATE_WANT_FULL_URI, request.want_full_uri);
    write_optional_buffer(&gb, TRANSLATE_WANT, request.want);
    write_optional_buffer(&gb, TRANSLATE_FILE_NOT_FOUND,
                          request.file_not_found);
    write_optional_buffer(&gb, TRANSLATE_CONTENT_TYPE_LOOKUP,
                          request.content_type_lookup);
    write_optional_packet(&gb, TRANSLATE_SUFFIX, request.suffix);
    write_optional_buffer(&gb, TRANSLATE_ENOTDIR, request.enotdir);
    write_optional_buffer(&gb, TRANSLATE_DIRECTORY_INDEX,
                          request.directory_index);
    write_optional_packet(&gb, TRANSLATE_PARAM, request.param);
    write_optional_buffer(&gb, TRANSLATE_PROBE_PATH_SUFFIXES,
                          request.probe_path_suffixes);
    write_optional_packet(&gb, TRANSLATE_PROBE_SUFFIX,
                          request.probe_suffix);
    write_optional_buffer(&gb, TRANSLATE_READ_FILE,
                          request.read_file);
    write_optional_packet(&gb, TRANSLATE_USER, request.user);
    write_packet(&gb, TRANSLATE_END, nullptr);

    return gb;
}

/*
 * receive response
 *
 */

inline BufferedResult
TranslateClient::Feed(const uint8_t *data, size_t length)
try {
    size_t consumed = 0;
    while (consumed < length) {
        size_t nbytes = parser.Feed(data + consumed, length - consumed);
        if (nbytes == 0)
            /* need more data */
            break;

        consumed += nbytes;
        socket.Consumed(nbytes);

        auto result = parser.Process();
        switch (result) {
        case TranslateParser::Result::MORE:
            break;

        case TranslateParser::Result::DONE:
            ReleaseSocket(true);
            handler.response(parser.GetResponse(), handler_ctx);
            pool_unref(&pool);
            return BufferedResult::CLOSED;
        }
    }

    return BufferedResult::MORE;
} catch (const std::runtime_error &e) {
    Fail(g_error_new_literal(translate_quark(), 0, e.what()));
    return BufferedResult::CLOSED;
}

/*
 * send requests
 *
 */

static bool
translate_try_write(TranslateClient *client)
{
    auto src = client->request.Read();
    assert(!src.IsNull());

    ssize_t nbytes = client->socket.Write(src.data, src.size);
    if (gcc_unlikely(nbytes < 0)) {
        if (gcc_likely(nbytes == WRITE_BLOCKING))
            return true;

        GError *error =
            new_error_errno_msg("write error to translation server");
        client->Fail(error);
        return false;
    }

    client->request.Consume(nbytes);
    if (client->request.IsEOF()) {
        /* the buffer is empty, i.e. the request has been sent */

        stopwatch_event(client->stopwatch, "request");

        client->socket.UnscheduleWrite();
        return client->socket.Read(true);
    }

    client->socket.ScheduleWrite();
    return true;
}

/*
 * buffered_socket handler
 *
 */

static BufferedResult
translate_client_socket_data(const void *buffer, size_t size, void *ctx)
{
    TranslateClient *client = (TranslateClient *)ctx;

    return client->Feed((const uint8_t *)buffer, size);
}

static bool
translate_client_socket_closed(void *ctx)
{
    TranslateClient *client = (TranslateClient *)ctx;

    client->ReleaseSocket(false);
    return true;
}

static bool
translate_client_socket_write(void *ctx)
{
    TranslateClient *client = (TranslateClient *)ctx;

    return translate_try_write(client);
}

static void
translate_client_socket_error(std::exception_ptr ep, void *ctx)
{
    TranslateClient *client = (TranslateClient *)ctx;

    auto *error = ToGError(ep);
    g_prefix_error(&error, "Translation server connection failed: ");
    client->Fail(error);
}

static constexpr BufferedSocketHandler translate_client_socket_handler = {
    .data = translate_client_socket_data,
    .direct = nullptr,
    .closed = translate_client_socket_closed,
    .remaining = nullptr,
    .end = nullptr,
    .write = translate_client_socket_write,
    .drained = nullptr,
    .timeout = nullptr,
    .broken = nullptr,
    .error = translate_client_socket_error,
};

/*
 * constructor
 *
 */

inline
TranslateClient::TranslateClient(struct pool &p, EventLoop &event_loop,
                                 int fd, Lease &lease,
                                 const TranslateRequest &request2,
                                 GrowingBuffer &&_request,
                                 const TranslateHandler &_handler, void *_ctx,
                                 CancellablePointer &cancel_ptr)
    :pool(p),
     stopwatch(stopwatch_fd_new(&p, fd, request2.GetDiagnosticName())),
     socket(event_loop),
     request_buffer(std::move(_request)),
     request(request_buffer),
     handler(_handler), handler_ctx(_ctx),
     parser(p, request2)
{
    socket.Init(fd, FdType::FD_SOCKET,
                &translate_read_timeout,
                &translate_write_timeout,
                translate_client_socket_handler, this);
    p_lease_ref_set(lease_ref, lease, p, "translate_lease");

    cancel_ptr = *this;
}

void
translate(struct pool &pool, EventLoop &event_loop,
          int fd, Lease &lease,
          const TranslateRequest &request,
          const TranslateHandler &handler, void *ctx,
          CancellablePointer &cancel_ptr)
try {
    assert(fd >= 0);
    assert(request.uri != nullptr || request.widget_type != nullptr ||
           (!request.content_type_lookup.IsNull() &&
            request.suffix != nullptr));
    assert(handler.response != nullptr);
    assert(handler.error != nullptr);

    GrowingBuffer gb = marshal_request(pool, request);

    auto *client = NewFromPool<TranslateClient>(pool, pool, event_loop,
                                                fd, lease,
                                                request, std::move(gb),
                                                handler, ctx, cancel_ptr);

    pool_ref(&client->pool);
    translate_try_write(client);
} catch (const std::runtime_error &e) {
    lease.ReleaseLease(true);

    handler.error(ToGError(e), ctx);
}
<commit_msg>translate_client: add Fail() overload with std::exception<commit_after>/*
 * Call the translation server.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "translate_client.hxx"
#include "translate_quark.hxx"
#include "translate_parser.hxx"
#include "translate_request.hxx"
#include "TranslateHandler.hxx"
#include "buffered_socket.hxx"
#include "please.hxx"
#include "growing_buffer.hxx"
#include "stopwatch.hxx"
#include "GException.hxx"
#include "gerrno.h"
#include "pool.hxx"
#include "util/Cancellable.hxx"
#include "util/RuntimeError.hxx"

#include <socket/address.h>

#include <glib.h>

#include <stdexcept>

#include <assert.h>
#include <string.h>
#include <errno.h>

static const uint8_t PROTOCOL_VERSION = 3;

struct TranslateClient final : Cancellable {
    struct pool &pool;

    Stopwatch *const stopwatch;

    BufferedSocket socket;
    struct lease_ref lease_ref;

    /** the marshalled translate request */
    GrowingBufferReader request_buffer;
    GrowingBufferReader request;

    const TranslateHandler &handler;
    void *handler_ctx;

    TranslateParser parser;

    TranslateClient(struct pool &p, EventLoop &event_loop,
                    int fd, Lease &lease,
                    const TranslateRequest &request2,
                    GrowingBuffer &&_request,
                    const TranslateHandler &_handler, void *_ctx,
                    CancellablePointer &cancel_ptr);

    void ReleaseSocket(bool reuse);
    void Release(bool reuse);

    void Fail(GError *error);
    void Fail(const std::exception &e);

    BufferedResult Feed(const uint8_t *data, size_t length);

    /* virtual methods from class Cancellable */
    void Cancel() override {
        stopwatch_event(stopwatch, "cancel");
        Release(false);
    }
};

static constexpr struct timeval translate_read_timeout = {
    .tv_sec = 60,
    .tv_usec = 0,
};

static constexpr struct timeval translate_write_timeout = {
    .tv_sec = 10,
    .tv_usec = 0,
};

void
TranslateClient::ReleaseSocket(bool reuse)
{
    assert(socket.IsConnected());

    stopwatch_dump(stopwatch);

    socket.Abandon();
    socket.Destroy();

    p_lease_release(lease_ref, reuse, pool);
}

/**
 * Release resources held by this object: the event object, the socket
 * lease, and the pool reference.
 */
void
TranslateClient::Release(bool reuse)
{
    ReleaseSocket(reuse);
    pool_unref(&pool);
}

void
TranslateClient::Fail(GError *error)
{
    stopwatch_event(stopwatch, "error");

    ReleaseSocket(false);

    handler.error(error, handler_ctx);
    pool_unref(&pool);
}

void
TranslateClient::Fail(const std::exception &e)
{
    Fail(ToGError(e));
}

/*
 * request marshalling
 *
 */

static void
write_packet_n(GrowingBuffer *gb, uint16_t command,
               const void *payload, size_t length)
{
    static struct beng_translation_header header;

    if (length >= 0xffff)
        throw FormatRuntimeError("payload for translate command %u too large",
                                 command);

    header.length = (uint16_t)length;
    header.command = command;

    gb->Write(&header, sizeof(header));
    if (length > 0)
        gb->Write(payload, length);
}

static void
write_packet(GrowingBuffer *gb, uint16_t command, const char *payload)
{
    write_packet_n(gb, command, payload,
                   payload != nullptr ? strlen(payload) : 0);
}

template<typename T>
static void
write_buffer(GrowingBuffer *gb, uint16_t command,
             ConstBuffer<T> buffer)
{
    auto b = buffer.ToVoid();
    write_packet_n(gb, command, b.data, b.size);
}

/**
 * Forward the command to write_packet() only if #payload is not nullptr.
 */
static void
write_optional_packet(GrowingBuffer *gb, uint16_t command, const char *payload)
{
    if (payload != nullptr)
        write_packet(gb, command, payload);
}

template<typename T>
static void
write_optional_buffer(GrowingBuffer *gb, uint16_t command,
                      ConstBuffer<T> buffer)
{
    if (!buffer.IsNull())
        write_buffer(gb, command, buffer);
}

static void
write_short(GrowingBuffer *gb, uint16_t command, uint16_t payload)
{
    write_packet_n(gb, command, &payload, sizeof(payload));
}

static void
write_sockaddr(GrowingBuffer *gb,
               uint16_t command, uint16_t command_string,
               SocketAddress address)
{
    assert(!address.IsNull());

    char address_string[1024];
    write_packet_n(gb, command,
                   address.GetAddress(), address.GetSize());

    if (socket_address_to_string(address_string, sizeof(address_string),
                                 address.GetAddress(), address.GetSize()))
        write_packet(gb, command_string, address_string);
}

static void
write_optional_sockaddr(GrowingBuffer *gb,
                        uint16_t command, uint16_t command_string,
                        SocketAddress address)
{
    if (!address.IsNull())
        write_sockaddr(gb, command, command_string, address);
}

static GrowingBuffer
marshal_request(struct pool &pool, const TranslateRequest &request)
{
    GrowingBuffer gb(pool, 512);

    write_packet_n(&gb, TRANSLATE_BEGIN,
                   &PROTOCOL_VERSION, sizeof(PROTOCOL_VERSION));
    write_optional_buffer(&gb, TRANSLATE_ERROR_DOCUMENT,
                          request.error_document);

    if (request.error_document_status != 0)
        write_short(&gb, TRANSLATE_STATUS,
                    request.error_document_status);

    write_optional_packet(&gb, TRANSLATE_LISTENER_TAG,
                          request.listener_tag);
    write_optional_sockaddr(&gb, TRANSLATE_LOCAL_ADDRESS,
                            TRANSLATE_LOCAL_ADDRESS_STRING,
                            request.local_address);
    write_optional_packet(&gb, TRANSLATE_REMOTE_HOST,
                          request.remote_host);
    write_optional_packet(&gb, TRANSLATE_HOST, request.host);
    write_optional_packet(&gb, TRANSLATE_USER_AGENT, request.user_agent);
    write_optional_packet(&gb, TRANSLATE_UA_CLASS, request.ua_class);
    write_optional_packet(&gb, TRANSLATE_LANGUAGE, request.accept_language);
    write_optional_packet(&gb, TRANSLATE_AUTHORIZATION, request.authorization);
    write_optional_packet(&gb, TRANSLATE_URI, request.uri);
    write_optional_packet(&gb, TRANSLATE_ARGS, request.args);
    write_optional_packet(&gb, TRANSLATE_QUERY_STRING, request.query_string);
    write_optional_packet(&gb, TRANSLATE_WIDGET_TYPE, request.widget_type);
    write_optional_buffer(&gb, TRANSLATE_SESSION, request.session);
    write_optional_buffer(&gb, TRANSLATE_INTERNAL_REDIRECT,
                          request.internal_redirect);
    write_optional_buffer(&gb, TRANSLATE_CHECK, request.check);
    write_optional_buffer(&gb, TRANSLATE_AUTH, request.auth);
    write_optional_buffer(&gb, TRANSLATE_WANT_FULL_URI, request.want_full_uri);
    write_optional_buffer(&gb, TRANSLATE_WANT, request.want);
    write_optional_buffer(&gb, TRANSLATE_FILE_NOT_FOUND,
                          request.file_not_found);
    write_optional_buffer(&gb, TRANSLATE_CONTENT_TYPE_LOOKUP,
                          request.content_type_lookup);
    write_optional_packet(&gb, TRANSLATE_SUFFIX, request.suffix);
    write_optional_buffer(&gb, TRANSLATE_ENOTDIR, request.enotdir);
    write_optional_buffer(&gb, TRANSLATE_DIRECTORY_INDEX,
                          request.directory_index);
    write_optional_packet(&gb, TRANSLATE_PARAM, request.param);
    write_optional_buffer(&gb, TRANSLATE_PROBE_PATH_SUFFIXES,
                          request.probe_path_suffixes);
    write_optional_packet(&gb, TRANSLATE_PROBE_SUFFIX,
                          request.probe_suffix);
    write_optional_buffer(&gb, TRANSLATE_READ_FILE,
                          request.read_file);
    write_optional_packet(&gb, TRANSLATE_USER, request.user);
    write_packet(&gb, TRANSLATE_END, nullptr);

    return gb;
}

/*
 * receive response
 *
 */

inline BufferedResult
TranslateClient::Feed(const uint8_t *data, size_t length)
try {
    size_t consumed = 0;
    while (consumed < length) {
        size_t nbytes = parser.Feed(data + consumed, length - consumed);
        if (nbytes == 0)
            /* need more data */
            break;

        consumed += nbytes;
        socket.Consumed(nbytes);

        auto result = parser.Process();
        switch (result) {
        case TranslateParser::Result::MORE:
            break;

        case TranslateParser::Result::DONE:
            ReleaseSocket(true);
            handler.response(parser.GetResponse(), handler_ctx);
            pool_unref(&pool);
            return BufferedResult::CLOSED;
        }
    }

    return BufferedResult::MORE;
} catch (const std::runtime_error &e) {
    Fail(e);
    return BufferedResult::CLOSED;
}

/*
 * send requests
 *
 */

static bool
translate_try_write(TranslateClient *client)
{
    auto src = client->request.Read();
    assert(!src.IsNull());

    ssize_t nbytes = client->socket.Write(src.data, src.size);
    if (gcc_unlikely(nbytes < 0)) {
        if (gcc_likely(nbytes == WRITE_BLOCKING))
            return true;

        GError *error =
            new_error_errno_msg("write error to translation server");
        client->Fail(error);
        return false;
    }

    client->request.Consume(nbytes);
    if (client->request.IsEOF()) {
        /* the buffer is empty, i.e. the request has been sent */

        stopwatch_event(client->stopwatch, "request");

        client->socket.UnscheduleWrite();
        return client->socket.Read(true);
    }

    client->socket.ScheduleWrite();
    return true;
}

/*
 * buffered_socket handler
 *
 */

static BufferedResult
translate_client_socket_data(const void *buffer, size_t size, void *ctx)
{
    TranslateClient *client = (TranslateClient *)ctx;

    return client->Feed((const uint8_t *)buffer, size);
}

static bool
translate_client_socket_closed(void *ctx)
{
    TranslateClient *client = (TranslateClient *)ctx;

    client->ReleaseSocket(false);
    return true;
}

static bool
translate_client_socket_write(void *ctx)
{
    TranslateClient *client = (TranslateClient *)ctx;

    return translate_try_write(client);
}

static void
translate_client_socket_error(std::exception_ptr ep, void *ctx)
{
    TranslateClient *client = (TranslateClient *)ctx;

    auto *error = ToGError(ep);
    g_prefix_error(&error, "Translation server connection failed: ");
    client->Fail(error);
}

static constexpr BufferedSocketHandler translate_client_socket_handler = {
    .data = translate_client_socket_data,
    .direct = nullptr,
    .closed = translate_client_socket_closed,
    .remaining = nullptr,
    .end = nullptr,
    .write = translate_client_socket_write,
    .drained = nullptr,
    .timeout = nullptr,
    .broken = nullptr,
    .error = translate_client_socket_error,
};

/*
 * constructor
 *
 */

inline
TranslateClient::TranslateClient(struct pool &p, EventLoop &event_loop,
                                 int fd, Lease &lease,
                                 const TranslateRequest &request2,
                                 GrowingBuffer &&_request,
                                 const TranslateHandler &_handler, void *_ctx,
                                 CancellablePointer &cancel_ptr)
    :pool(p),
     stopwatch(stopwatch_fd_new(&p, fd, request2.GetDiagnosticName())),
     socket(event_loop),
     request_buffer(std::move(_request)),
     request(request_buffer),
     handler(_handler), handler_ctx(_ctx),
     parser(p, request2)
{
    socket.Init(fd, FdType::FD_SOCKET,
                &translate_read_timeout,
                &translate_write_timeout,
                translate_client_socket_handler, this);
    p_lease_ref_set(lease_ref, lease, p, "translate_lease");

    cancel_ptr = *this;
}

void
translate(struct pool &pool, EventLoop &event_loop,
          int fd, Lease &lease,
          const TranslateRequest &request,
          const TranslateHandler &handler, void *ctx,
          CancellablePointer &cancel_ptr)
try {
    assert(fd >= 0);
    assert(request.uri != nullptr || request.widget_type != nullptr ||
           (!request.content_type_lookup.IsNull() &&
            request.suffix != nullptr));
    assert(handler.response != nullptr);
    assert(handler.error != nullptr);

    GrowingBuffer gb = marshal_request(pool, request);

    auto *client = NewFromPool<TranslateClient>(pool, pool, event_loop,
                                                fd, lease,
                                                request, std::move(gb),
                                                handler, ctx, cancel_ptr);

    pool_ref(&client->pool);
    translate_try_write(client);
} catch (const std::runtime_error &e) {
    lease.ReleaseLease(true);

    handler.error(ToGError(e), ctx);
}
<|endoftext|>


<commit_before>//
// ulib - a collection of useful classes
// Copyright (C) 2008,2009,2013,2017 Michael Fink
//
/// \file ProgramOptions.cpp program options implementation
//

#include "stdafx.h"
#include "ProgramOptions.hpp"
#include "CommandLineParser.hpp"
#include "Path.hpp"

void ProgramOptions::RegisterOption(const CString& shortOptionChars, const CString& longOption,
   const CString& helpText, unsigned int numArgs, T_fnOptionHandler fnOptionHandler)
{
   CString longOptionLower(longOption);
   longOptionLower.MakeLower();

   OptionInfo info(shortOptionChars, longOptionLower, helpText, numArgs, fnOptionHandler);
   m_optionsList.push_back(info);
}

void ProgramOptions::RegisterOption(const CString& shortOptionChars, const CString& longOption,
   const CString& helpText, T_fnOptionHandlerSingleArg fnOptionHandler)
{
   T_fnOptionHandler fnOptionHandler2 =
      std::bind(&ProgramOptions::CallSingleArgHandler, std::placeholders::_1, fnOptionHandler);

   RegisterOption(shortOptionChars, longOption, helpText, 1, fnOptionHandler2);
}

void ProgramOptions::RegisterOption(const CString& shortOptionChars, const CString& longOption,
   const CString& helpText, CString& argStorage)
{
   T_fnOptionHandlerSingleArg fnOptionHandler =
      std::bind(&ProgramOptions::SetStringArgStorage, std::placeholders::_1, std::ref(argStorage));

   RegisterOption(shortOptionChars, longOption, helpText, fnOptionHandler);
}

void ProgramOptions::RegisterOption(const CString& shortOptionChars, const CString& longOption,
   const CString& helpText, bool& optionFlag)
{
   T_fnOptionHandlerSingleArg fnOptionHandler =
      std::bind(&ProgramOptions::SetBoolArgStorage, std::ref(optionFlag));

   RegisterOption(shortOptionChars, longOption, helpText, fnOptionHandler);
}

void ProgramOptions::RegisterHelpOption()
{
   T_fnOptionHandler fnOptionHandler = std::bind(&ProgramOptions::OutputHelp, this);

   RegisterOption(_T("h?"), _T("help"), _T("Shows help"), 0, fnOptionHandler);
}

bool ProgramOptions::OutputHelp()
{
   if (!m_fnOptionOutputHandler)
      return true;

   CString appFilename = Path(m_executableFilename).FilenameAndExt();

   CString helpText;
   helpText.Format(
      _T("Syntax: %s <params> <args>\n")
      _T("Options:\n"),
      appFilename.GetString());

   // append all options
   size_t maxOptionListIndex = m_optionsList.size();
   for (size_t optionListIndex = 0; optionListIndex < maxOptionListIndex; optionListIndex++)
   {
      OptionInfo& optInfo = m_optionsList[optionListIndex];

      // add short option chars
      size_t maxShortOptions = optInfo.m_shortOptionChars.GetLength();
      for (size_t shortOptionIndex = 0; shortOptionIndex < maxShortOptions; shortOptionIndex++)
      {
         helpText.AppendFormat(_T(" -%c"), optInfo.m_shortOptionChars[shortOptionIndex]);
      }

      // add long option string
      helpText.AppendFormat(_T(" --%s"), optInfo.m_longOption.GetString());
      helpText += _T("\n   ");

      // help text
      CString temp = optInfo.m_helpText;
      temp.Replace(_T("\n"), _T("\n   ")); // add proper indentation
      helpText += temp + _T("\n");
   }

   m_fnOptionOutputHandler(helpText);

   m_handledHelp = true;

   return true;
}

void ProgramOptions::Parse(int argc, _TCHAR* argv[])
{
   CommandLineParser parser(argc, argv);
   Parse(parser);
}

void ProgramOptions::Parse(LPCTSTR commandLine)
{
   CommandLineParser parser(commandLine);
   Parse(parser);
}

void ProgramOptions::Parse(CommandLineParser& parser)
{
   // must contain at least one param
   ATLVERIFY(true == parser.GetNext(m_executableFilename));

   CString argument;
   while (parser.GetNext(argument))
   {
      ATLASSERT(argument.GetLength() > 0);

      // check for first char
      TCHAR chArg = argument[0];
      if (argument.GetLength() > 1 && (chArg == _T('/') || chArg == _T('-')))
      {
         TCHAR searchOptionChar = 0;
         CString longSearchOption;

         // it's an option, either short or long one
         TCHAR optionChar = argument[1];
         if (chArg == _T('-') && optionChar == _T('-'))
         {
            // we have a unix-style option, long name only
            longSearchOption = argument.Mid(2);
            longSearchOption.MakeLower();
         }
         else
         {
            // we have either /X or -X or option, short name only
            searchOptionChar = optionChar;
         }

         // now search for the proper option
         bool foundOption = false;
         for (size_t optionsIndex = 0, maxOptionsIndex = m_optionsList.size(); optionsIndex < maxOptionsIndex; optionsIndex++)
         {
            OptionInfo& optInfo = m_optionsList[optionsIndex];

            // check long name first, then short name
            if ((!longSearchOption.IsEmpty() && longSearchOption == optInfo.m_longOption) ||
               (searchOptionChar != 0 && CString(searchOptionChar).FindOneOf(optInfo.m_shortOptionChars) != -1))
            {
               // found long or short option
               foundOption = true;

               // get arguments
               std::vector<CString> argsList;
               CString paramArgs;
               for (unsigned int numArgs = 0; numArgs < optInfo.m_numArgs; numArgs++)
               {
                  parser.GetNext(paramArgs);
                  argsList.push_back(paramArgs);
               }

               if (argsList.size() < optInfo.m_numArgs)
               {
                  // too few arguments
                  if (m_fnOptionOutputHandler)
                     m_fnOptionOutputHandler(CString(_T("Too few parameters for option: ") + argument));
                  break;
               }

               ATLASSERT(optInfo.m_fnOptionHandler != NULL);
               bool ret2 = optInfo.m_fnOptionHandler(argsList);

               if (!ret2)
               {
                  if (m_fnOptionOutputHandler)
                     m_fnOptionOutputHandler(CString(_T("Syntax error for option: ") + argument));
               }

               break;
            }
         } // end for

         if (!foundOption)
         {
            // unknown option
            if (m_fnOptionOutputHandler)
               m_fnOptionOutputHandler(CString(_T("Unknown option: ") + argument));
         }
      }
      else
      {
         // no arg; it's a file
         bool handled = false;
         if (m_fnParameterHandler)
            handled = m_fnParameterHandler(argument);

         if (!handled)
         {
            // output: unhandled option
            if (m_fnOptionOutputHandler)
               m_fnOptionOutputHandler(CString(_T("Unknown parameter: ") + argument));
         }
      }
   }
}

void ProgramOptions::OutputConsole(const CString& text)
{
   _tprintf(_T("%s\n"), text.GetString());
}
<commit_msg>fixed warning in x64 configuration<commit_after>//
// ulib - a collection of useful classes
// Copyright (C) 2008,2009,2013,2017 Michael Fink
//
/// \file ProgramOptions.cpp program options implementation
//

#include "stdafx.h"
#include "ProgramOptions.hpp"
#include "CommandLineParser.hpp"
#include "Path.hpp"

void ProgramOptions::RegisterOption(const CString& shortOptionChars, const CString& longOption,
   const CString& helpText, unsigned int numArgs, T_fnOptionHandler fnOptionHandler)
{
   CString longOptionLower(longOption);
   longOptionLower.MakeLower();

   OptionInfo info(shortOptionChars, longOptionLower, helpText, numArgs, fnOptionHandler);
   m_optionsList.push_back(info);
}

void ProgramOptions::RegisterOption(const CString& shortOptionChars, const CString& longOption,
   const CString& helpText, T_fnOptionHandlerSingleArg fnOptionHandler)
{
   T_fnOptionHandler fnOptionHandler2 =
      std::bind(&ProgramOptions::CallSingleArgHandler, std::placeholders::_1, fnOptionHandler);

   RegisterOption(shortOptionChars, longOption, helpText, 1, fnOptionHandler2);
}

void ProgramOptions::RegisterOption(const CString& shortOptionChars, const CString& longOption,
   const CString& helpText, CString& argStorage)
{
   T_fnOptionHandlerSingleArg fnOptionHandler =
      std::bind(&ProgramOptions::SetStringArgStorage, std::placeholders::_1, std::ref(argStorage));

   RegisterOption(shortOptionChars, longOption, helpText, fnOptionHandler);
}

void ProgramOptions::RegisterOption(const CString& shortOptionChars, const CString& longOption,
   const CString& helpText, bool& optionFlag)
{
   T_fnOptionHandlerSingleArg fnOptionHandler =
      std::bind(&ProgramOptions::SetBoolArgStorage, std::ref(optionFlag));

   RegisterOption(shortOptionChars, longOption, helpText, fnOptionHandler);
}

void ProgramOptions::RegisterHelpOption()
{
   T_fnOptionHandler fnOptionHandler = std::bind(&ProgramOptions::OutputHelp, this);

   RegisterOption(_T("h?"), _T("help"), _T("Shows help"), 0, fnOptionHandler);
}

bool ProgramOptions::OutputHelp()
{
   if (!m_fnOptionOutputHandler)
      return true;

   CString appFilename = Path(m_executableFilename).FilenameAndExt();

   CString helpText;
   helpText.Format(
      _T("Syntax: %s <params> <args>\n")
      _T("Options:\n"),
      appFilename.GetString());

   // append all options
   size_t maxOptionListIndex = m_optionsList.size();
   for (size_t optionListIndex = 0; optionListIndex < maxOptionListIndex; optionListIndex++)
   {
      OptionInfo& optInfo = m_optionsList[optionListIndex];

      // add short option chars
      int maxShortOptions = optInfo.m_shortOptionChars.GetLength();
      for (int shortOptionIndex = 0; shortOptionIndex < maxShortOptions; shortOptionIndex++)
      {
         helpText.AppendFormat(_T(" -%c"), optInfo.m_shortOptionChars[shortOptionIndex]);
      }

      // add long option string
      helpText.AppendFormat(_T(" --%s"), optInfo.m_longOption.GetString());
      helpText += _T("\n   ");

      // help text
      CString temp = optInfo.m_helpText;
      temp.Replace(_T("\n"), _T("\n   ")); // add proper indentation
      helpText += temp + _T("\n");
   }

   m_fnOptionOutputHandler(helpText);

   m_handledHelp = true;

   return true;
}

void ProgramOptions::Parse(int argc, _TCHAR* argv[])
{
   CommandLineParser parser(argc, argv);
   Parse(parser);
}

void ProgramOptions::Parse(LPCTSTR commandLine)
{
   CommandLineParser parser(commandLine);
   Parse(parser);
}

void ProgramOptions::Parse(CommandLineParser& parser)
{
   // must contain at least one param
   ATLVERIFY(true == parser.GetNext(m_executableFilename));

   CString argument;
   while (parser.GetNext(argument))
   {
      ATLASSERT(argument.GetLength() > 0);

      // check for first char
      TCHAR chArg = argument[0];
      if (argument.GetLength() > 1 && (chArg == _T('/') || chArg == _T('-')))
      {
         TCHAR searchOptionChar = 0;
         CString longSearchOption;

         // it's an option, either short or long one
         TCHAR optionChar = argument[1];
         if (chArg == _T('-') && optionChar == _T('-'))
         {
            // we have a unix-style option, long name only
            longSearchOption = argument.Mid(2);
            longSearchOption.MakeLower();
         }
         else
         {
            // we have either /X or -X or option, short name only
            searchOptionChar = optionChar;
         }

         // now search for the proper option
         bool foundOption = false;
         for (size_t optionsIndex = 0, maxOptionsIndex = m_optionsList.size(); optionsIndex < maxOptionsIndex; optionsIndex++)
         {
            OptionInfo& optInfo = m_optionsList[optionsIndex];

            // check long name first, then short name
            if ((!longSearchOption.IsEmpty() && longSearchOption == optInfo.m_longOption) ||
               (searchOptionChar != 0 && CString(searchOptionChar).FindOneOf(optInfo.m_shortOptionChars) != -1))
            {
               // found long or short option
               foundOption = true;

               // get arguments
               std::vector<CString> argsList;
               CString paramArgs;
               for (unsigned int numArgs = 0; numArgs < optInfo.m_numArgs; numArgs++)
               {
                  parser.GetNext(paramArgs);
                  argsList.push_back(paramArgs);
               }

               if (argsList.size() < optInfo.m_numArgs)
               {
                  // too few arguments
                  if (m_fnOptionOutputHandler)
                     m_fnOptionOutputHandler(CString(_T("Too few parameters for option: ") + argument));
                  break;
               }

               ATLASSERT(optInfo.m_fnOptionHandler != NULL);
               bool ret2 = optInfo.m_fnOptionHandler(argsList);

               if (!ret2)
               {
                  if (m_fnOptionOutputHandler)
                     m_fnOptionOutputHandler(CString(_T("Syntax error for option: ") + argument));
               }

               break;
            }
         } // end for

         if (!foundOption)
         {
            // unknown option
            if (m_fnOptionOutputHandler)
               m_fnOptionOutputHandler(CString(_T("Unknown option: ") + argument));
         }
      }
      else
      {
         // no arg; it's a file
         bool handled = false;
         if (m_fnParameterHandler)
            handled = m_fnParameterHandler(argument);

         if (!handled)
         {
            // output: unhandled option
            if (m_fnOptionOutputHandler)
               m_fnOptionOutputHandler(CString(_T("Unknown parameter: ") + argument));
         }
      }
   }
}

void ProgramOptions::OutputConsole(const CString& text)
{
   _tprintf(_T("%s\n"), text.GetString());
}
<|endoftext|>