<commit_before>/******************************************************************************\
* File:          util.cpp
* Purpose:       Implementation of wxextension report utility functions and classes
* Author:        Anton van Wezenbeek
* RCS-ID:        $Id$
*
* Copyright (c) 1998-2009 Anton van Wezenbeek
* All rights are reserved. Reproduction in whole or part is prohibited
* without the written consent of the copyright owner.
\******************************************************************************/

#include <wx/extension/configdialog.h>
#include <wx/extension/report/util.h>
#include <wx/extension/report/defs.h>
#include <wx/extension/report/frame.h>
#include <wx/extension/report/listitem.h>
#include <wx/extension/report/listview.h>
#include <wx/extension/report/stc.h>
#include <wx/extension/report/textfile.h>

bool wxExCompareFile(const wxFileName& file1, const wxFileName& file2)
{
  const wxString comparator = wxExApp::GetConfig(_("Comparator"));

  if (comparator.empty())
  {
    return false;
  }

  const wxString arguments =
     (file1.GetModificationTime() < file2.GetModificationTime()) ?
       "\"" + file1.GetFullPath() + "\" \"" + file2.GetFullPath() + "\"":
       "\"" + file2.GetFullPath() + "\" \"" + file1.GetFullPath() + "\"";

  if (wxExecute(comparator + " " + arguments) == 0)
  {
    return false;
  }

  const wxString msg = _("Compared") + ": " + arguments;
  wxExApp::Log(msg);
  wxExFrame::StatusText(msg);

  return true;
}

void wxExFindInFiles(wxExFrameWithHistory* frame, bool replace)
{
  // To initialize the combobox.
  wxExApp::GetConfig(_("In files"), wxExApp::GetLexers()->BuildComboBox());

  std::vector<wxExConfigItem> v;
  v.push_back(wxExConfigItem(_("Find what"), CONFIG_COMBOBOX, wxEmptyString, true));
  if (replace) v.push_back(wxExConfigItem(_("Replace with"), CONFIG_COMBOBOX));
  v.push_back(wxExConfigItem(_("In files"), CONFIG_COMBOBOX, wxEmptyString, true));
  v.push_back(wxExConfigItem(_("In folder"), CONFIG_COMBOBOXDIR, wxEmptyString, true));
  v.push_back(wxExConfigItem());
  v.push_back(wxExConfigItem(wxExApp::GetConfig()->GetFindReplaceData()->GetInfo()));

  if (wxExConfigDialog(NULL,
    wxExApp::GetConfig(),
    v,
    (replace ? _("Replace In Files"): _("Find In Files"))).ShowModal() == wxID_CANCEL)
  {
    return;
  }

  const wxExTool tool =
    (replace ?
       ID_TOOL_REPORT_REPLACE:
       ID_TOOL_REPORT_FIND);

  if (!wxExTextFileWithListView::SetupTool(tool))
  {
    return;
  }

  wxExApp::Log(wxExApp::GetConfig()->GetFindReplaceData()->GetText(replace));

  wxExDirWithListView dir(
    tool,
    wxExApp::GetConfig(_("In folder")),
    wxExApp::GetConfig(_("In files")));

  dir.FindFiles();
  dir.GetStatistics().Log(tool);
}

bool wxExFindOtherFileName(
  const wxFileName& filename,
  wxExListViewFile* listview,
  wxFileName* lastfile)
{
  /* Add the base version if present. E.g.
  fullpath: F:\CCIS\v990308\com\atis\atis-ctrl\atis-ctrl.cc
  base:  F:\CCIS\
  append:   \com\atis\atis-ctrl\atis-ctrl.cc
  */
  const wxString fullpath = filename.GetFullPath();

  wxRegEx reg("[/|\\][a-z-]*[0-9]+\\.?[0-9]*\\.?[0-9]*\\.?[0-9]*");

  if (!reg.Matches(fullpath.Lower()))
  {
    wxExFrame::StatusText(_("No version information found"));
    return false;
  }

  size_t start, len;
  if (!reg.GetMatch(&start, &len))
  {
    wxFAIL;
    return false;
  }

  wxString base = fullpath.substr(0, start);
  if (!wxEndsWithPathSeparator(base))
  {
    base += wxFileName::GetPathSeparator();
  }

  wxDir dir(base);

  if (!dir.IsOpened())
  {
    wxFAIL;
    return false;
  }

  wxString filename_string;
  bool cont = dir.GetFirst(&filename_string, wxEmptyString, wxDIR_DIRS); // only get dirs

  wxDateTime lastmodtime((time_t)0);
  const wxString append = fullpath.substr(start + len);

  bool found = false;

  // Readme: Maybe use a thread for this.
  while (cont)
  {
    wxFileName fn(base + filename_string + append);

    if (fn.FileExists() &&
        fn.GetPath().CmpNoCase(filename.GetPath()) != 0 &&
        fn.GetModificationTime() != filename.GetModificationTime())
    {
      found = true;

      if (listview == NULL && lastfile == NULL)
      {
        // We are only interested in return value, so speed it up.
        return true;
      }

      if (listview != NULL)
      {
        wxExListItemWithFileName item(listview, fn.GetFullPath());
        item.Insert();
      }

      if (lastfile != NULL)
      {
        if (fn.GetModificationTime() > lastmodtime)
        {
          lastmodtime = fn.GetModificationTime();
          *lastfile = fn;
        }
      }
    }

    cont = dir.GetNext(&filename_string);

    if (wxTheApp != NULL)
    {
      wxTheApp->Yield();
    }
  }

  if (!found && (listview != NULL || lastfile != NULL))
  {
    wxExFrame::StatusText(_("No files found"));
  }

  return found;
}

bool wxExForEach(wxAuiNotebook* notebook, int id, const wxFont& font)
{
  for (
    size_t page = 0;
    page < notebook->GetPageCount();
    page++)
  {
    wxExListViewFile* lv = (wxExListViewFile*)notebook->GetPage(page);

    if (lv == NULL)
    {
      wxFAIL;
      return false;
    }

    if (id >= wxID_VIEW_DETAILS &&  id <= wxID_VIEW_LIST)
    {
      long view = 0;
      switch (id)
      {
      case wxID_VIEW_DETAILS: view = wxLC_REPORT; break;
      case wxID_VIEW_LIST: view = wxLC_LIST; break;
      case wxID_VIEW_SMALLICONS: view = wxLC_SMALL_ICON; break;
      default: wxFAIL;
      }

      lv->SetSingleStyle(view);
      wxExApp::GetConfig()->Set("List/Style", view);
    }
    else
    {
    switch (id)
    {
    case ID_LIST_ALL_ITEMS:
      {
        if (font.IsOk())
        {
          lv->SetFont(font);
        }

        lv->ItemsUpdate();
      }
      break;

    case ID_LIST_ALL_CLOSE:
      if (!lv->Continue()) return false;
      if (!notebook->DeletePage(page)) return false;
      break;

    default: wxFAIL;
    }
    }
  }

  return true;
}

bool wxExMake(const wxFileName& makefile)
{
  wxSetWorkingDirectory(makefile.GetPath());

  return wxExListViewFile::ProcessRun(
    wxExApp::GetConfig("Make", "make") + " " +
    wxExApp::GetConfig("MakeSwitch", "-f") + " " +
    makefile.GetFullPath());
}

void wxExOpenFiles(
  wxExFrameWithHistory* frame,
  const wxArrayString& files,
  long flags)
{
  for (size_t i = 0; i < files.GetCount(); i++)
  {
    wxString file = files[i]; // cannot be const because of file = later on

    if (file.Contains("*") || file.Contains("?"))
    {
      wxExDirWithListView dir(frame, wxGetCwd(), file, flags);
      dir.FindFiles();
    }
    else
    {
      int line = 0;

      if (file.Contains(":"))
      {
        line = atoi(files[i].AfterFirst(':').c_str());

        if (line != 0) // this indicates an error in the number
        {
          file = file.BeforeFirst(':');
        }
      }

      const wxExFileName filename(file);
      frame->OpenFile(filename, line, wxEmptyString, flags);
    }
  }
}

wxExDirWithListView::wxExDirWithListView(const wxExTool& tool,
  const wxString& fullpath, const wxString& filespec)
  : wxExDir(fullpath, filespec)
  , m_Statistics(fullpath)
  , m_Frame(NULL)
  , m_ListView(NULL)
  , m_Flags(0)
  , m_Tool(tool)
{
}

wxExDirWithListView::wxExDirWithListView(wxExListViewFile* listview,
  const wxString& fullpath, const wxString& filespec)
  : wxExDir(fullpath, filespec)
  , m_Statistics(fullpath)
  , m_Frame(NULL)
  , m_ListView(listview)
  , m_Flags(0)
  , m_Tool(ID_TOOL_LOWEST)
{
}

wxExDirWithListView::wxExDirWithListView(wxExFrameWithHistory* frame,
  const wxString& fullpath, const wxString& filespec, long flags)
  : wxExDir(fullpath, filespec)
  , m_Statistics(fullpath)
  , m_Frame(frame)
  , m_ListView(NULL)
  , m_Flags(flags)
  , m_Tool(ID_TOOL_LOWEST)
{
}

void wxExDirWithListView::OnFile(const wxString& file)
{
  if (m_Frame == NULL && m_ListView == NULL)
  {
    const wxExFileName filename(file);

    if (filename.GetStat().IsOk())
    {
      if (wxFileName::DirExists(file)) return;
      wxExTextFileWithListView report(filename, m_Tool);
      report.RunTool();
      m_Statistics += report.GetStatistics();
    }
  }
  else
  {
    if (m_Frame != NULL)
    {
      if (wxFileName::DirExists(file)) return;
      m_Frame->OpenFile(file, 0, wxEmptyString, m_Flags);
    }
    else if (m_ListView != NULL)
    {
      wxExListItemWithFileName item(m_ListView, file, GetFileSpec());
      item.Insert();

      // Don't move next code into insert, as it itself inserts!
      if (m_ListView->GetType() == wxExListViewFile::LIST_VERSION)
      {
        wxExListItemWithFileName item(m_ListView, m_ListView->GetItemCount() - 1);

        wxExTextFileWithListView report(item.m_Statistics, ID_TOOL_REVISION_RECENT);
        if (report.SetupTool(ID_TOOL_REVISION_RECENT))
        {
          report.RunTool();
          item.UpdateRevisionList(report.GetRCS());
        }
      }
    }
  }
}

/// Offers a find combobox that allows you to find text
/// on a current STC on an wxExFrameWithHistory.
class ComboBox : public wxComboBox
{
public:
  /// Constructor. Fills the combobox box with values from FindReplace from config.
  ComboBox(
    wxWindow* parent,
    wxExFrameWithHistory* frame,
    wxWindowID id = wxID_ANY,
    const wxPoint& pos = wxDefaultPosition,
    const wxSize& size = wxDefaultSize);
private:
  void OnCommand(wxCommandEvent& event);
  void OnKey(wxKeyEvent& event);
  wxExFrameWithHistory* m_Frame;

  DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(ComboBox, wxComboBox)
  EVT_CHAR(ComboBox::OnKey)
  EVT_MENU(wxID_DELETE, ComboBox::OnCommand)
END_EVENT_TABLE()

ComboBox::ComboBox(
  wxWindow* parent,
  wxExFrameWithHistory* frame,
  wxWindowID id,
  const wxPoint& pos,
  const wxSize& size)
  : wxComboBox(parent, id, wxEmptyString, pos, size)
  , m_Frame(frame)
{
  const int accels = 1;
  wxAcceleratorEntry entries[accels];
  entries[0].Set(wxACCEL_NORMAL, WXK_DELETE, wxID_DELETE);
  wxAcceleratorTable accel(accels, entries);
  SetAcceleratorTable(accel);

  const wxFont font(
    wxExApp::GetConfig("FindFont", 8),
    wxFONTFAMILY_DEFAULT,
    wxFONTSTYLE_NORMAL,
    wxFONTWEIGHT_NORMAL);

  SetFont(font);

  wxExComboBoxFromString(this, wxExApp::GetConfig("FindReplace/FindStrings"));

  // And override the value set by previous, as we want text to be same as in Find.
  SetValue(wxExApp::GetConfig()->GetFindReplaceData()->GetFindString());
}

void ComboBox::OnCommand(wxCommandEvent& event)
{
  // README: The delete key default behaviour does not delete the char right from insertion point.
  // Instead, the event is sent to the editor and a char is deleted from the editor.
  // Therefore implement the delete here.
  if (event.GetId() == wxID_DELETE)
  {
    Remove(GetInsertionPoint(), GetInsertionPoint() + 1);
  }
  else
  {
    event.Skip();
  }
}

void ComboBox::OnKey(wxKeyEvent& event)
{
  const int key = event.GetKeyCode();

  if (key == WXK_RETURN)
  {
    wxExSTCWithFrame* stc = m_Frame->GetCurrentSTC();

    if (stc != NULL)
    {
      stc->FindNext(GetValue());
      wxExApp::GetConfig()->GetFindReplaceData()->SetFindString(GetValue());

      // And keep the changed text in the combo box.
      wxString text;

      if (wxExComboBoxToString(this, text))
      {
        wxExApp::GetConfig()->Set("FindReplace/FindStrings", text);
        Clear(); // so wxExComboBoxFromString can append again
        wxExComboBoxFromString(this, text);
        SetValue(wxExApp::GetConfig()->GetFindReplaceData()->GetFindString());
      }
    }
  }
  else
  {
    event.Skip();
  }
}

BEGIN_EVENT_TABLE(wxExFindToolBar, wxAuiToolBar)
  EVT_CHECKBOX(ID_MATCH_WHOLE_WORD, wxExFindToolBar::OnCommand)
  EVT_CHECKBOX(ID_MATCH_CASE, wxExFindToolBar::OnCommand)
  EVT_CHECKBOX(ID_REGULAR_EXPRESSION, wxExFindToolBar::OnCommand)
END_EVENT_TABLE()

wxExFindToolBar::wxExFindToolBar(
  wxWindow* parent,
  wxExFrameWithHistory* frame,
  wxWindowID id)
  : wxAuiToolBar(parent, id)
{
  m_MatchCase = new wxCheckBox(this, ID_MATCH_CASE, _("Match case"));
  m_MatchWholeWord = new wxCheckBox(this, ID_MATCH_WHOLE_WORD, _("Match whole word"));
  m_RegularExpression = new wxCheckBox(this, ID_REGULAR_EXPRESSION, _("Regular expression"));

  m_MatchCase->SetValue(wxExApp::GetConfig()->GetFindReplaceData()->MatchCase());
  m_MatchWholeWord->SetValue(wxExApp::GetConfig()->GetFindReplaceData()->MatchWord());
  m_RegularExpression->SetValue(wxExApp::GetConfig()->GetFindReplaceData()->IsRegularExpression());

#ifdef __WXMSW__
  const wxSize size(150, 20);
#else
  const wxSize size(150, -1);
#endif
  AddControl(new ComboBox(this, frame, ID_FIND_TEXT, wxDefaultPosition, size));
  AddSeparator();
  AddControl(m_MatchWholeWord);
  AddControl(m_MatchCase);
  AddControl(m_RegularExpression);

  Realize();
}

void wxExFindToolBar::OnCommand(wxCommandEvent& event)
{
  switch (event.GetId())
  {
  case ID_MATCH_WHOLE_WORD:
  case ID_MATCH_CASE:
  case ID_REGULAR_EXPRESSION:
    wxExApp::GetConfig()->SetFindReplaceData(
      m_MatchWholeWord->GetValue(),
      m_MatchCase->GetValue(),
      m_RegularExpression->GetValue());
    break;
  }
}
<commit_msg>wxExMake preserves working directory<commit_after>/******************************************************************************\
* File:          util.cpp
* Purpose:       Implementation of wxextension report utility functions and classes
* Author:        Anton van Wezenbeek
* RCS-ID:        $Id$
*
* Copyright (c) 1998-2009 Anton van Wezenbeek
* All rights are reserved. Reproduction in whole or part is prohibited
* without the written consent of the copyright owner.
\******************************************************************************/

#include <wx/extension/configdialog.h>
#include <wx/extension/report/util.h>
#include <wx/extension/report/defs.h>
#include <wx/extension/report/frame.h>
#include <wx/extension/report/listitem.h>
#include <wx/extension/report/listview.h>
#include <wx/extension/report/stc.h>
#include <wx/extension/report/textfile.h>

bool wxExCompareFile(const wxFileName& file1, const wxFileName& file2)
{
  const wxString comparator = wxExApp::GetConfig(_("Comparator"));

  if (comparator.empty())
  {
    return false;
  }

  const wxString arguments =
     (file1.GetModificationTime() < file2.GetModificationTime()) ?
       "\"" + file1.GetFullPath() + "\" \"" + file2.GetFullPath() + "\"":
       "\"" + file2.GetFullPath() + "\" \"" + file1.GetFullPath() + "\"";

  if (wxExecute(comparator + " " + arguments) == 0)
  {
    return false;
  }

  const wxString msg = _("Compared") + ": " + arguments;
  wxExApp::Log(msg);
  wxExFrame::StatusText(msg);

  return true;
}

void wxExFindInFiles(wxExFrameWithHistory* frame, bool replace)
{
  // To initialize the combobox.
  wxExApp::GetConfig(_("In files"), wxExApp::GetLexers()->BuildComboBox());

  std::vector<wxExConfigItem> v;
  v.push_back(wxExConfigItem(_("Find what"), CONFIG_COMBOBOX, wxEmptyString, true));
  if (replace) v.push_back(wxExConfigItem(_("Replace with"), CONFIG_COMBOBOX));
  v.push_back(wxExConfigItem(_("In files"), CONFIG_COMBOBOX, wxEmptyString, true));
  v.push_back(wxExConfigItem(_("In folder"), CONFIG_COMBOBOXDIR, wxEmptyString, true));
  v.push_back(wxExConfigItem());
  v.push_back(wxExConfigItem(wxExApp::GetConfig()->GetFindReplaceData()->GetInfo()));

  if (wxExConfigDialog(NULL,
    wxExApp::GetConfig(),
    v,
    (replace ? _("Replace In Files"): _("Find In Files"))).ShowModal() == wxID_CANCEL)
  {
    return;
  }

  const wxExTool tool =
    (replace ?
       ID_TOOL_REPORT_REPLACE:
       ID_TOOL_REPORT_FIND);

  if (!wxExTextFileWithListView::SetupTool(tool))
  {
    return;
  }

  wxExApp::Log(wxExApp::GetConfig()->GetFindReplaceData()->GetText(replace));

  wxExDirWithListView dir(
    tool,
    wxExApp::GetConfig(_("In folder")),
    wxExApp::GetConfig(_("In files")));

  dir.FindFiles();
  dir.GetStatistics().Log(tool);
}

bool wxExFindOtherFileName(
  const wxFileName& filename,
  wxExListViewFile* listview,
  wxFileName* lastfile)
{
  /* Add the base version if present. E.g.
  fullpath: F:\CCIS\v990308\com\atis\atis-ctrl\atis-ctrl.cc
  base:  F:\CCIS\
  append:   \com\atis\atis-ctrl\atis-ctrl.cc
  */
  const wxString fullpath = filename.GetFullPath();

  wxRegEx reg("[/|\\][a-z-]*[0-9]+\\.?[0-9]*\\.?[0-9]*\\.?[0-9]*");

  if (!reg.Matches(fullpath.Lower()))
  {
    wxExFrame::StatusText(_("No version information found"));
    return false;
  }

  size_t start, len;
  if (!reg.GetMatch(&start, &len))
  {
    wxFAIL;
    return false;
  }

  wxString base = fullpath.substr(0, start);
  if (!wxEndsWithPathSeparator(base))
  {
    base += wxFileName::GetPathSeparator();
  }

  wxDir dir(base);

  if (!dir.IsOpened())
  {
    wxFAIL;
    return false;
  }

  wxString filename_string;
  bool cont = dir.GetFirst(&filename_string, wxEmptyString, wxDIR_DIRS); // only get dirs

  wxDateTime lastmodtime((time_t)0);
  const wxString append = fullpath.substr(start + len);

  bool found = false;

  // Readme: Maybe use a thread for this.
  while (cont)
  {
    wxFileName fn(base + filename_string + append);

    if (fn.FileExists() &&
        fn.GetPath().CmpNoCase(filename.GetPath()) != 0 &&
        fn.GetModificationTime() != filename.GetModificationTime())
    {
      found = true;

      if (listview == NULL && lastfile == NULL)
      {
        // We are only interested in return value, so speed it up.
        return true;
      }

      if (listview != NULL)
      {
        wxExListItemWithFileName item(listview, fn.GetFullPath());
        item.Insert();
      }

      if (lastfile != NULL)
      {
        if (fn.GetModificationTime() > lastmodtime)
        {
          lastmodtime = fn.GetModificationTime();
          *lastfile = fn;
        }
      }
    }

    cont = dir.GetNext(&filename_string);

    if (wxTheApp != NULL)
    {
      wxTheApp->Yield();
    }
  }

  if (!found && (listview != NULL || lastfile != NULL))
  {
    wxExFrame::StatusText(_("No files found"));
  }

  return found;
}

bool wxExForEach(wxAuiNotebook* notebook, int id, const wxFont& font)
{
  for (
    size_t page = 0;
    page < notebook->GetPageCount();
    page++)
  {
    wxExListViewFile* lv = (wxExListViewFile*)notebook->GetPage(page);

    if (lv == NULL)
    {
      wxFAIL;
      return false;
    }

    if (id >= wxID_VIEW_DETAILS &&  id <= wxID_VIEW_LIST)
    {
      long view = 0;
      switch (id)
      {
      case wxID_VIEW_DETAILS: view = wxLC_REPORT; break;
      case wxID_VIEW_LIST: view = wxLC_LIST; break;
      case wxID_VIEW_SMALLICONS: view = wxLC_SMALL_ICON; break;
      default: wxFAIL;
      }

      lv->SetSingleStyle(view);
      wxExApp::GetConfig()->Set("List/Style", view);
    }
    else
    {
    switch (id)
    {
    case ID_LIST_ALL_ITEMS:
      {
        if (font.IsOk())
        {
          lv->SetFont(font);
        }

        lv->ItemsUpdate();
      }
      break;

    case ID_LIST_ALL_CLOSE:
      if (!lv->Continue()) return false;
      if (!notebook->DeletePage(page)) return false;
      break;

    default: wxFAIL;
    }
    }
  }

  return true;
}

bool wxExMake(const wxFileName& makefile)
{
  const wxString cwd = wxGetCwd();

  wxSetWorkingDirectory(makefile.GetPath());

  const bool ret = wxExListViewFile::ProcessRun(
    wxExApp::GetConfig("Make", "make") + " " +
    wxExApp::GetConfig("MakeSwitch", "-f") + " " +
    makefile.GetFullPath());

  wxSetWorkingDirectory(cwd);

  return ret;
}

void wxExOpenFiles(
  wxExFrameWithHistory* frame,
  const wxArrayString& files,
  long flags)
{
  for (size_t i = 0; i < files.GetCount(); i++)
  {
    wxString file = files[i]; // cannot be const because of file = later on

    if (file.Contains("*") || file.Contains("?"))
    {
      wxExDirWithListView dir(frame, wxGetCwd(), file, flags);
      dir.FindFiles();
    }
    else
    {
      int line = 0;

      if (file.Contains(":"))
      {
        line = atoi(files[i].AfterFirst(':').c_str());

        if (line != 0) // this indicates an error in the number
        {
          file = file.BeforeFirst(':');
        }
      }

      const wxExFileName filename(file);
      frame->OpenFile(filename, line, wxEmptyString, flags);
    }
  }
}

wxExDirWithListView::wxExDirWithListView(const wxExTool& tool,
  const wxString& fullpath, const wxString& filespec)
  : wxExDir(fullpath, filespec)
  , m_Statistics(fullpath)
  , m_Frame(NULL)
  , m_ListView(NULL)
  , m_Flags(0)
  , m_Tool(tool)
{
}

wxExDirWithListView::wxExDirWithListView(wxExListViewFile* listview,
  const wxString& fullpath, const wxString& filespec)
  : wxExDir(fullpath, filespec)
  , m_Statistics(fullpath)
  , m_Frame(NULL)
  , m_ListView(listview)
  , m_Flags(0)
  , m_Tool(ID_TOOL_LOWEST)
{
}

wxExDirWithListView::wxExDirWithListView(wxExFrameWithHistory* frame,
  const wxString& fullpath, const wxString& filespec, long flags)
  : wxExDir(fullpath, filespec)
  , m_Statistics(fullpath)
  , m_Frame(frame)
  , m_ListView(NULL)
  , m_Flags(flags)
  , m_Tool(ID_TOOL_LOWEST)
{
}

void wxExDirWithListView::OnFile(const wxString& file)
{
  if (m_Frame == NULL && m_ListView == NULL)
  {
    const wxExFileName filename(file);

    if (filename.GetStat().IsOk())
    {
      if (wxFileName::DirExists(file)) return;
      wxExTextFileWithListView report(filename, m_Tool);
      report.RunTool();
      m_Statistics += report.GetStatistics();
    }
  }
  else
  {
    if (m_Frame != NULL)
    {
      if (wxFileName::DirExists(file)) return;
      m_Frame->OpenFile(file, 0, wxEmptyString, m_Flags);
    }
    else if (m_ListView != NULL)
    {
      wxExListItemWithFileName item(m_ListView, file, GetFileSpec());
      item.Insert();

      // Don't move next code into insert, as it itself inserts!
      if (m_ListView->GetType() == wxExListViewFile::LIST_VERSION)
      {
        wxExListItemWithFileName item(m_ListView, m_ListView->GetItemCount() - 1);

        wxExTextFileWithListView report(item.m_Statistics, ID_TOOL_REVISION_RECENT);
        if (report.SetupTool(ID_TOOL_REVISION_RECENT))
        {
          report.RunTool();
          item.UpdateRevisionList(report.GetRCS());
        }
      }
    }
  }
}

/// Offers a find combobox that allows you to find text
/// on a current STC on an wxExFrameWithHistory.
class ComboBox : public wxComboBox
{
public:
  /// Constructor. Fills the combobox box with values from FindReplace from config.
  ComboBox(
    wxWindow* parent,
    wxExFrameWithHistory* frame,
    wxWindowID id = wxID_ANY,
    const wxPoint& pos = wxDefaultPosition,
    const wxSize& size = wxDefaultSize);
private:
  void OnCommand(wxCommandEvent& event);
  void OnKey(wxKeyEvent& event);
  wxExFrameWithHistory* m_Frame;

  DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(ComboBox, wxComboBox)
  EVT_CHAR(ComboBox::OnKey)
  EVT_MENU(wxID_DELETE, ComboBox::OnCommand)
END_EVENT_TABLE()

ComboBox::ComboBox(
  wxWindow* parent,
  wxExFrameWithHistory* frame,
  wxWindowID id,
  const wxPoint& pos,
  const wxSize& size)
  : wxComboBox(parent, id, wxEmptyString, pos, size)
  , m_Frame(frame)
{
  const int accels = 1;
  wxAcceleratorEntry entries[accels];
  entries[0].Set(wxACCEL_NORMAL, WXK_DELETE, wxID_DELETE);
  wxAcceleratorTable accel(accels, entries);
  SetAcceleratorTable(accel);

  const wxFont font(
    wxExApp::GetConfig("FindFont", 8),
    wxFONTFAMILY_DEFAULT,
    wxFONTSTYLE_NORMAL,
    wxFONTWEIGHT_NORMAL);

  SetFont(font);

  wxExComboBoxFromString(this, wxExApp::GetConfig("FindReplace/FindStrings"));

  // And override the value set by previous, as we want text to be same as in Find.
  SetValue(wxExApp::GetConfig()->GetFindReplaceData()->GetFindString());
}

void ComboBox::OnCommand(wxCommandEvent& event)
{
  // README: The delete key default behaviour does not delete the char right from insertion point.
  // Instead, the event is sent to the editor and a char is deleted from the editor.
  // Therefore implement the delete here.
  if (event.GetId() == wxID_DELETE)
  {
    Remove(GetInsertionPoint(), GetInsertionPoint() + 1);
  }
  else
  {
    event.Skip();
  }
}

void ComboBox::OnKey(wxKeyEvent& event)
{
  const int key = event.GetKeyCode();

  if (key == WXK_RETURN)
  {
    wxExSTCWithFrame* stc = m_Frame->GetCurrentSTC();

    if (stc != NULL)
    {
      stc->FindNext(GetValue());
      wxExApp::GetConfig()->GetFindReplaceData()->SetFindString(GetValue());

      // And keep the changed text in the combo box.
      wxString text;

      if (wxExComboBoxToString(this, text))
      {
        wxExApp::GetConfig()->Set("FindReplace/FindStrings", text);
        Clear(); // so wxExComboBoxFromString can append again
        wxExComboBoxFromString(this, text);
        SetValue(wxExApp::GetConfig()->GetFindReplaceData()->GetFindString());
      }
    }
  }
  else
  {
    event.Skip();
  }
}

BEGIN_EVENT_TABLE(wxExFindToolBar, wxAuiToolBar)
  EVT_CHECKBOX(ID_MATCH_WHOLE_WORD, wxExFindToolBar::OnCommand)
  EVT_CHECKBOX(ID_MATCH_CASE, wxExFindToolBar::OnCommand)
  EVT_CHECKBOX(ID_REGULAR_EXPRESSION, wxExFindToolBar::OnCommand)
END_EVENT_TABLE()

wxExFindToolBar::wxExFindToolBar(
  wxWindow* parent,
  wxExFrameWithHistory* frame,
  wxWindowID id)
  : wxAuiToolBar(parent, id)
{
  m_MatchCase = new wxCheckBox(this, ID_MATCH_CASE, _("Match case"));
  m_MatchWholeWord = new wxCheckBox(this, ID_MATCH_WHOLE_WORD, _("Match whole word"));
  m_RegularExpression = new wxCheckBox(this, ID_REGULAR_EXPRESSION, _("Regular expression"));

  m_MatchCase->SetValue(wxExApp::GetConfig()->GetFindReplaceData()->MatchCase());
  m_MatchWholeWord->SetValue(wxExApp::GetConfig()->GetFindReplaceData()->MatchWord());
  m_RegularExpression->SetValue(wxExApp::GetConfig()->GetFindReplaceData()->IsRegularExpression());

#ifdef __WXMSW__
  const wxSize size(150, 20);
#else
  const wxSize size(150, -1);
#endif
  AddControl(new ComboBox(this, frame, ID_FIND_TEXT, wxDefaultPosition, size));
  AddSeparator();
  AddControl(m_MatchWholeWord);
  AddControl(m_MatchCase);
  AddControl(m_RegularExpression);

  Realize();
}

void wxExFindToolBar::OnCommand(wxCommandEvent& event)
{
  switch (event.GetId())
  {
  case ID_MATCH_WHOLE_WORD:
  case ID_MATCH_CASE:
  case ID_REGULAR_EXPRESSION:
    wxExApp::GetConfig()->SetFindReplaceData(
      m_MatchWholeWord->GetValue(),
      m_MatchCase->GetValue(),
      m_RegularExpression->GetValue());
    break;
  }
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////
// Sun Calc
////////////////////////////////////////////////////////////
#include <Arduino.h>

#include <PMS.h>
#include <GPSParser.h>

#include "Pins.h"
#include "GaryCooper.h"
#include "Telemetry.h"
#include "sunriset.h"
#include "SunCalc.h"

////////////////////////////////////////////////////////////
// Use GPS data to calculate sunrise, sunset, and current times.
// Times are in UTC and represented as float with hour in the
// integer and decimal hours instead of minutes in the fractional.
// ie 11:30 AM (UTC) is represented as 11.50, 11:45 AM (UTC)
// is represented as 11.75, and so on.
////////////////////////////////////////////////////////////

CSunCalc::CSunCalc()
{
	m_currentTime = CSunCalc_INVALID_TIME;

	m_sunriseTime_astro = CSunCalc_INVALID_TIME;
	m_sunsetTime_aastro = CSunCalc_INVALID_TIME;

	m_sunriseTime_naut = CSunCalc_INVALID_TIME;
	m_sunsetTime_naut = CSunCalc_INVALID_TIME;

	m_sunriseTime_civil = CSunCalc_INVALID_TIME;
	m_sunsetTime_civil = CSunCalc_INVALID_TIME;

	m_sunriseTime_comm = CSunCalc_INVALID_TIME;
	m_sunsetTime_comm = CSunCalc_INVALID_TIME;
}

CSunCalc::~CSunCalc()
{

}

double CSunCalc::getSunriseTime(eSunrise_Sunset_T _type)
{
	switch(_type)
	{
	case srsst_astronomical:
		return m_sunriseTime_astro;
		break;

	case srsst_nautical:
		return m_sunriseTime_naut;
		break;

	case srsst_civil:
		return m_sunriseTime_civil;
		break;

	case srsst_common:
		return m_sunriseTime_comm;
		break;

	default:
		break;
	}

	return CSunCalc_INVALID_TIME;
}

double CSunCalc::getSunsetTime(eSunrise_Sunset_T _type)
{
	switch(_type)
	{
	case srsst_astronomical:
		return m_sunsetTime_aastro;
		break;

	case srsst_nautical:
		return m_sunsetTime_naut;
		break;

	case srsst_civil:
		return m_sunsetTime_civil;
		break;

	case srsst_common:
		return m_sunsetTime_comm;
		break;

	default:
		break;
	}

	return CSunCalc_INVALID_TIME;
}

bool CSunCalc::processGPSData(CGPSParserData &_gpsData)
{
	// Assume he worst
	m_currentTime = CSunCalc_INVALID_TIME;

	m_sunriseTime_astro = CSunCalc_INVALID_TIME;
	m_sunsetTime_aastro = CSunCalc_INVALID_TIME;

	m_sunriseTime_naut = CSunCalc_INVALID_TIME;
	m_sunsetTime_naut = CSunCalc_INVALID_TIME;

	m_sunriseTime_civil = CSunCalc_INVALID_TIME;
	m_sunsetTime_civil = CSunCalc_INVALID_TIME;

	m_sunriseTime_comm = CSunCalc_INVALID_TIME;
	m_sunsetTime_comm = CSunCalc_INVALID_TIME;

	// If we don't have a lock then
	// don't bother with the other stuff because
	// it will not be set
#ifdef DEBUG_SUNCALC
	DEBUG_SERIAL.println();
#endif

	if(!_gpsData.m_GPSLocked)
	{
#ifdef DEBUG_SUNCALC
		DEBUG_SERIAL.println(PMS("CSunCalc: GPS not locked."));
#endif
		telemetrySend(telemetry_tag_error, telemetry_error_GPS_not_locked);
		return false;
	}

#ifdef DEBUG_SUNCALC
	DEBUG_SERIAL.println(PMS("CSunCalc: GPS locked."));
#endif

	// Date
	int year = _gpsData.m_date.m_year;
	int month = _gpsData.m_date.m_month;
	int day = _gpsData.m_date.m_day;
	int hour = _gpsData.m_time.m_hour;
	int minute = _gpsData.m_time.m_minute;

	// Location
	double lat = _gpsData.m_position.m_lat;
	double lon = _gpsData.m_position.m_lon;

#ifdef DEBUG_SUNCALC
	DEBUG_SERIAL.print(PMS("Date: "));
	DEBUG_SERIAL.print(month);
	DEBUG_SERIAL.print(PMS("/"));
	DEBUG_SERIAL.print(day);
	DEBUG_SERIAL.print(PMS("/"));
	DEBUG_SERIAL.println(year);

	DEBUG_SERIAL.print(PMS("Lat: "));
	DEBUG_SERIAL.print(lat);
	DEBUG_SERIAL.print(PMS(" Lon: "));
	DEBUG_SERIAL.println(lon);
	DEBUG_SERIAL.println();
#endif

	// Make sure we have good data
	if(!GPS_IS_VALID_DATA(year) ||
		!GPS_IS_VALID_DATA(month) ||
		!GPS_IS_VALID_DATA(day) ||
		!GPS_IS_VALID_DATA(hour) ||
		!GPS_IS_VALID_DATA(minute) ||
		!GPS_IS_VALID_DATA(lat) ||
		!GPS_IS_VALID_DATA(lon))
	{
		telemetrySend(telemetry_tag_error, telemetry_error_GPS_bad_data);
		return false;
	}

	// Figure current time
	m_currentTime = hour + (minute / 60.);

	// Get the rise and set times
	astronomical_twilight( year, month, day, lon, lat,
						   &m_sunriseTime_astro, &m_sunsetTime_aastro );

	nautical_twilight( year, month, day, lon, lat,
					   &m_sunriseTime_naut, &m_sunsetTime_naut );

	civil_twilight( year, month, day, lon, lat,
					&m_sunriseTime_civil, &m_sunsetTime_civil );

	sun_rise_set( year, month, day, lon, lat,
				  &m_sunriseTime_comm, &m_sunsetTime_comm );

	// Telemetry
	telemetrySend(telemetry_tag_currentTime, m_currentTime);
	telemetrySend(telemetry_tag_GPSNSats, _gpsData.m_nSatellites);

#ifdef DEBUG_SUNCALC
	DEBUG_SERIAL.print(PMS("Current Time (UTC): "));
	debugPrintDoubleTime(m_currentTime);

	DEBUG_SERIAL.print(PMS("Astronomical (UTC): "));
	debugPrintDoubleTime(m_sunriseTime_astro, false);
	DEBUG_SERIAL.print(" - ");
	debugPrintDoubleTime(m_sunsetTime_aastro);

	DEBUG_SERIAL.print(PMS("Nautical (UTC): "));
	debugPrintDoubleTime(m_sunriseTime_naut, false);
	DEBUG_SERIAL.print(" - ");
	debugPrintDoubleTime(m_sunsetTime_naut);

	DEBUG_SERIAL.print(PMS("Civil (UTC): "));
	debugPrintDoubleTime(m_sunriseTime_civil, false);
	DEBUG_SERIAL.print(" - ");
	debugPrintDoubleTime(m_sunsetTime_civil);

	DEBUG_SERIAL.print(PMS("Common (UTC): "));
	debugPrintDoubleTime(m_sunriseTime_comm, false);
	DEBUG_SERIAL.print(PMS(" - "));
	debugPrintDoubleTime(m_sunsetTime_comm);
	DEBUG_SERIAL.println();

#endif

	return true;
}

bool timeIsBetween(double _currentTime, double _first, double _second)
{
	if(_first < _second)
	{
		if((_currentTime >= _first) && (_currentTime < _second))
			return true;
		else
			return false;
	}
	else
	{
		if(	((_currentTime >= _first) && (_currentTime < 24.)) ||
				((_currentTime > 0.) && (_currentTime < _second)) )
			return true;
		else
			return false;
	}
}
<commit_msg>Add a missing telemetry item<commit_after>////////////////////////////////////////////////////////////
// Sun Calc
////////////////////////////////////////////////////////////
#include <Arduino.h>

#include <PMS.h>
#include <GPSParser.h>

#include "Pins.h"
#include "GaryCooper.h"
#include "Telemetry.h"
#include "sunriset.h"
#include "SunCalc.h"

////////////////////////////////////////////////////////////
// Use GPS data to calculate sunrise, sunset, and current times.
// Times are in UTC and represented as float with hour in the
// integer and decimal hours instead of minutes in the fractional.
// ie 11:30 AM (UTC) is represented as 11.50, 11:45 AM (UTC)
// is represented as 11.75, and so on.
////////////////////////////////////////////////////////////

CSunCalc::CSunCalc()
{
	m_currentTime = CSunCalc_INVALID_TIME;

	m_sunriseTime_astro = CSunCalc_INVALID_TIME;
	m_sunsetTime_aastro = CSunCalc_INVALID_TIME;

	m_sunriseTime_naut = CSunCalc_INVALID_TIME;
	m_sunsetTime_naut = CSunCalc_INVALID_TIME;

	m_sunriseTime_civil = CSunCalc_INVALID_TIME;
	m_sunsetTime_civil = CSunCalc_INVALID_TIME;

	m_sunriseTime_comm = CSunCalc_INVALID_TIME;
	m_sunsetTime_comm = CSunCalc_INVALID_TIME;
}

CSunCalc::~CSunCalc()
{

}

double CSunCalc::getSunriseTime(eSunrise_Sunset_T _type)
{
	switch(_type)
	{
	case srsst_astronomical:
		return m_sunriseTime_astro;
		break;

	case srsst_nautical:
		return m_sunriseTime_naut;
		break;

	case srsst_civil:
		return m_sunriseTime_civil;
		break;

	case srsst_common:
		return m_sunriseTime_comm;
		break;

	default:
		break;
	}

	return CSunCalc_INVALID_TIME;
}

double CSunCalc::getSunsetTime(eSunrise_Sunset_T _type)
{
	switch(_type)
	{
	case srsst_astronomical:
		return m_sunsetTime_aastro;
		break;

	case srsst_nautical:
		return m_sunsetTime_naut;
		break;

	case srsst_civil:
		return m_sunsetTime_civil;
		break;

	case srsst_common:
		return m_sunsetTime_comm;
		break;

	default:
		break;
	}

	return CSunCalc_INVALID_TIME;
}

bool CSunCalc::processGPSData(CGPSParserData &_gpsData)
{
	// Assume he worst
	m_currentTime = CSunCalc_INVALID_TIME;

	m_sunriseTime_astro = CSunCalc_INVALID_TIME;
	m_sunsetTime_aastro = CSunCalc_INVALID_TIME;

	m_sunriseTime_naut = CSunCalc_INVALID_TIME;
	m_sunsetTime_naut = CSunCalc_INVALID_TIME;

	m_sunriseTime_civil = CSunCalc_INVALID_TIME;
	m_sunsetTime_civil = CSunCalc_INVALID_TIME;

	m_sunriseTime_comm = CSunCalc_INVALID_TIME;
	m_sunsetTime_comm = CSunCalc_INVALID_TIME;

	// If we don't have a lock then
	// don't bother with the other stuff because
	// it will not be set
#ifdef DEBUG_SUNCALC
	DEBUG_SERIAL.println();
#endif

	if(!_gpsData.m_GPSLocked)
	{
#ifdef DEBUG_SUNCALC
		DEBUG_SERIAL.println(PMS("CSunCalc: GPS not locked."));
#endif
		telemetrySend(telemetry_tag_error, telemetry_error_GPS_not_locked);
		return false;
	}

#ifdef DEBUG_SUNCALC
	DEBUG_SERIAL.println(PMS("CSunCalc: GPS locked."));
#endif

	// Date
	int year = _gpsData.m_date.m_year;
	int month = _gpsData.m_date.m_month;
	int day = _gpsData.m_date.m_day;
	int hour = _gpsData.m_time.m_hour;
	int minute = _gpsData.m_time.m_minute;

	// Location
	double lat = _gpsData.m_position.m_lat;
	double lon = _gpsData.m_position.m_lon;

#ifdef DEBUG_SUNCALC
	DEBUG_SERIAL.print(PMS("Date: "));
	DEBUG_SERIAL.print(month);
	DEBUG_SERIAL.print(PMS("/"));
	DEBUG_SERIAL.print(day);
	DEBUG_SERIAL.print(PMS("/"));
	DEBUG_SERIAL.println(year);

	DEBUG_SERIAL.print(PMS("Lat: "));
	DEBUG_SERIAL.print(lat);
	DEBUG_SERIAL.print(PMS(" Lon: "));
	DEBUG_SERIAL.println(lon);
	DEBUG_SERIAL.println();
#endif

	// Make sure we have good data
	if(!GPS_IS_VALID_DATA(year) ||
		!GPS_IS_VALID_DATA(month) ||
		!GPS_IS_VALID_DATA(day) ||
		!GPS_IS_VALID_DATA(hour) ||
		!GPS_IS_VALID_DATA(minute) ||
		!GPS_IS_VALID_DATA(lat) ||
		!GPS_IS_VALID_DATA(lon))
	{
		telemetrySend(telemetry_tag_error, telemetry_error_GPS_bad_data);
		return false;
	}

	// Figure current time
	m_currentTime = hour + (minute / 60.);

	// Get the rise and set times
	astronomical_twilight( year, month, day, lon, lat,
						   &m_sunriseTime_astro, &m_sunsetTime_aastro );

	nautical_twilight( year, month, day, lon, lat,
					   &m_sunriseTime_naut, &m_sunsetTime_naut );

	civil_twilight( year, month, day, lon, lat,
					&m_sunriseTime_civil, &m_sunsetTime_civil );

	sun_rise_set( year, month, day, lon, lat,
				  &m_sunriseTime_comm, &m_sunsetTime_comm );

	// Telemetry
	telemetrySend(telemetry_tag_currentTime, m_currentTime);

	telemetrySend(telemetry_tag_GPSNSats, _gpsData.m_nSatellites);
	telemetrySend(telemetry_tag_lat, lat);
	telemetrySend(telemetry_tag_lon, lon);

#ifdef DEBUG_SUNCALC
	DEBUG_SERIAL.print(PMS("Current Time (UTC): "));
	debugPrintDoubleTime(m_currentTime);

	DEBUG_SERIAL.print(PMS("Astronomical (UTC): "));
	debugPrintDoubleTime(m_sunriseTime_astro, false);
	DEBUG_SERIAL.print(" - ");
	debugPrintDoubleTime(m_sunsetTime_aastro);

	DEBUG_SERIAL.print(PMS("Nautical (UTC): "));
	debugPrintDoubleTime(m_sunriseTime_naut, false);
	DEBUG_SERIAL.print(" - ");
	debugPrintDoubleTime(m_sunsetTime_naut);

	DEBUG_SERIAL.print(PMS("Civil (UTC): "));
	debugPrintDoubleTime(m_sunriseTime_civil, false);
	DEBUG_SERIAL.print(" - ");
	debugPrintDoubleTime(m_sunsetTime_civil);

	DEBUG_SERIAL.print(PMS("Common (UTC): "));
	debugPrintDoubleTime(m_sunriseTime_comm, false);
	DEBUG_SERIAL.print(PMS(" - "));
	debugPrintDoubleTime(m_sunsetTime_comm);
	DEBUG_SERIAL.println();

#endif

	return true;
}

bool timeIsBetween(double _currentTime, double _first, double _second)
{
	if(_first < _second)
	{
		if((_currentTime >= _first) && (_currentTime < _second))
			return true;
		else
			return false;
	}
	else
	{
		if(	((_currentTime >= _first) && (_currentTime < 24.)) ||
				((_currentTime > 0.) && (_currentTime < _second)) )
			return true;
		else
			return false;
	}
}
<|endoftext|>


<commit_before>
#include <iostream>
#include <string>

#include <thread>
#include <mutex>

#include <boost/program_options.hpp>

#include "filter/compiler.h"

#include "fileemitor.h"
#include "version.h"
#include "worker.h"
namespace po = boost::program_options;

std::mutex screenMutex;

std::string recordSeparator = "\n";
void outDocument(const std::string &what) {
    std::lock_guard<std::mutex> screenLock(screenMutex);
    std::cout.write(what.data(), what.size());
    std::cout << recordSeparator;
}

void updateSeparator(std::string &sep) {
    std::string res;
    bool esc = false;
    for(char c : sep) {
        if (esc) {
            switch(c) {
                case 'n':
                    res += '\n';
                    break;
                case 't':
                    res += '\t';
                    break;
                default:
                    res += c;
            }
            esc = false;
            continue;
        }
        if (c == '\\') {
            esc = true;
        } else {
            res += c;
        }
    }
    sep = res;
}

void correctJobsNumber(u_int &jobs) {
    if (jobs < 1) {
        std::cerr << "hint: adjusing threads number to 1" << std::endl;
        jobs = 1;
    } else if (jobs > 10) {
        std::cerr << "hint: do not be so greedy. adjusing threads number to 10" << std::endl;
        jobs = 10;
    }
}

int main(int argc, const char * argv[]) {

    std::cout.sync_with_stdio(false);

    std::string condition;
    int limit = -1;
    u_int jobs = 1;
    std::string fields;
    bool printProcessingFile = false;
    bool countMode = false;
    bool disableParseLoop = false;
    bool displayVersionAndExit = false;
    std::string fieldSeparator = "\t";

    po::options_description desc("Allowed options");
    desc.add_options()
        ("input-file,f", po::value< std::vector<std::string> >(), "Input files")
        ("condition,c", po::value< std::string >(&condition), "Expression")
        ("limit,n", po::value< int >(&limit)->default_value(-1), "Maximum number of records (default -1 means no limit)")
        ("fields,l", po::value< std::string >(&fields), "Fields to output")
        ("print-file", po::bool_switch(&printProcessingFile), "Print name of processing file")
        ("jobs,j", po::value< u_int >(&jobs)->default_value(1), "Number of threads to run")
        ("count-only", po::bool_switch(&countMode), "Count of matched records, don't print them")
        ("record-separator", po::value<std::string>(&recordSeparator)->default_value("\\n"), "Record separator (\\n by default)")
        ("field-separator", po::value<std::string>(&fieldSeparator)->default_value("\\t"), "Field separator for TSV output (\\t by default)")
        ("disable-parse-loop", po::bool_switch(&disableParseLoop), "Disable experimental parsing mode (enabled by default)")
        ("help,h", "Show help message")
        ("version", po::bool_switch(&displayVersionAndExit), "Display version and exit")
    ;

    po::positional_options_description p;
    p.add("input-file", -1);

    po::variables_map vm;
    try {
        po::store(po::command_line_parser(argc, argv).
                  options(desc).positional(p).run(), vm);
        po::notify(vm);
    } catch(const boost::program_options::error &e) {
        std::cerr << "Sorry, I couldn't parse arguments: " << e.what() << std::endl;
        std::cerr << "Try --help" << std::endl;
        return 1;
    }
    if (vm.count("help")) {
        std::cout << desc << std::endl;
        return 0;
    }
    if (displayVersionAndExit) {
        std::cout << "aq version: " << avroqVersion() << std::endl;
        return 0;
    }

    updateSeparator(recordSeparator);
    updateSeparator(fieldSeparator);

    filter::Compiler filterCompiler;
    std::shared_ptr<filter::Filter> filter;

    if (!condition.empty()) {
        try {
            filter = filterCompiler.compile(condition);
        } catch(const filter::Compiler::CompileError &e) {
            std::cerr << "Condition complile failed: " << std::endl;
            std::cerr << e.what() << std::endl;
            return 1;
        }
    }

    if (vm.count("input-file")) {

        const auto &fileList = vm["input-file"].as< std::vector<std::string> >();

        FileEmitor emitor(fileList, limit, outDocument);

        emitor.setFilter(filter);
        emitor.setTsvFieldList(fields, fieldSeparator);
        if (printProcessingFile) {
            emitor.enablePrintProcessingFile();
        }
        if (countMode) {
            emitor.enableCountOnlyMode();
        }
        if ( ! disableParseLoop ) {
            emitor.enableParseLoop();
        }
        std::vector<std::thread> workers;

        correctJobsNumber(jobs);

        for(u_int i = 0; i < jobs; ++i) { // TODO: check for inadequate values
            workers.emplace_back(
                    std::thread(Worker(emitor))
                );
        }

        for(auto &p : workers) {
            p.join();
        }

        if (countMode) {
            std::cout << "Matched documents: " << emitor.getCountedDocuments() << std::endl;
        }

        if (emitor.getLastError().size() > 0) {
            std::cerr << emitor.getLastError() << std::endl;
            return 1;
        }

    } else {
        std::cerr << "No input files" << std::endl;
        return 1;
    }

    return 0;
}
<commit_msg>Fixed misprint in warning<commit_after>
#include <iostream>
#include <string>

#include <thread>
#include <mutex>

#include <boost/program_options.hpp>

#include "filter/compiler.h"

#include "fileemitor.h"
#include "version.h"
#include "worker.h"
namespace po = boost::program_options;

std::mutex screenMutex;

std::string recordSeparator = "\n";
void outDocument(const std::string &what) {
    std::lock_guard<std::mutex> screenLock(screenMutex);
    std::cout.write(what.data(), what.size());
    std::cout << recordSeparator;
}

void updateSeparator(std::string &sep) {
    std::string res;
    bool esc = false;
    for(char c : sep) {
        if (esc) {
            switch(c) {
                case 'n':
                    res += '\n';
                    break;
                case 't':
                    res += '\t';
                    break;
                default:
                    res += c;
            }
            esc = false;
            continue;
        }
        if (c == '\\') {
            esc = true;
        } else {
            res += c;
        }
    }
    sep = res;
}

void correctJobsNumber(u_int &jobs) {
    if (jobs < 1) {
        std::cerr << "hint: adjusting threads number to 1" << std::endl;
        jobs = 1;
    } else if (jobs > 10) {
        std::cerr << "hint: do not be so greedy. adjusting threads number to 10" << std::endl;
        jobs = 10;
    }
}

int main(int argc, const char * argv[]) {

    std::cout.sync_with_stdio(false);

    std::string condition;
    int limit = -1;
    u_int jobs = 1;
    std::string fields;
    bool printProcessingFile = false;
    bool countMode = false;
    bool disableParseLoop = false;
    bool displayVersionAndExit = false;
    std::string fieldSeparator = "\t";

    po::options_description desc("Allowed options");
    desc.add_options()
        ("input-file,f", po::value< std::vector<std::string> >(), "Input files")
        ("condition,c", po::value< std::string >(&condition), "Expression")
        ("limit,n", po::value< int >(&limit)->default_value(-1), "Maximum number of records (default -1 means no limit)")
        ("fields,l", po::value< std::string >(&fields), "Fields to output")
        ("print-file", po::bool_switch(&printProcessingFile), "Print name of processing file")
        ("jobs,j", po::value< u_int >(&jobs)->default_value(1), "Number of threads to run")
        ("count-only", po::bool_switch(&countMode), "Count of matched records, don't print them")
        ("record-separator", po::value<std::string>(&recordSeparator)->default_value("\\n"), "Record separator (\\n by default)")
        ("field-separator", po::value<std::string>(&fieldSeparator)->default_value("\\t"), "Field separator for TSV output (\\t by default)")
        ("disable-parse-loop", po::bool_switch(&disableParseLoop), "Disable experimental parsing mode (enabled by default)")
        ("help,h", "Show help message")
        ("version", po::bool_switch(&displayVersionAndExit), "Display version and exit")
    ;

    po::positional_options_description p;
    p.add("input-file", -1);

    po::variables_map vm;
    try {
        po::store(po::command_line_parser(argc, argv).
                  options(desc).positional(p).run(), vm);
        po::notify(vm);
    } catch(const boost::program_options::error &e) {
        std::cerr << "Sorry, I couldn't parse arguments: " << e.what() << std::endl;
        std::cerr << "Try --help" << std::endl;
        return 1;
    }
    if (vm.count("help")) {
        std::cout << desc << std::endl;
        return 0;
    }
    if (displayVersionAndExit) {
        std::cout << "aq version: " << avroqVersion() << std::endl;
        return 0;
    }

    updateSeparator(recordSeparator);
    updateSeparator(fieldSeparator);

    filter::Compiler filterCompiler;
    std::shared_ptr<filter::Filter> filter;

    if (!condition.empty()) {
        try {
            filter = filterCompiler.compile(condition);
        } catch(const filter::Compiler::CompileError &e) {
            std::cerr << "Condition complile failed: " << std::endl;
            std::cerr << e.what() << std::endl;
            return 1;
        }
    }

    if (vm.count("input-file")) {

        const auto &fileList = vm["input-file"].as< std::vector<std::string> >();

        FileEmitor emitor(fileList, limit, outDocument);

        emitor.setFilter(filter);
        emitor.setTsvFieldList(fields, fieldSeparator);
        if (printProcessingFile) {
            emitor.enablePrintProcessingFile();
        }
        if (countMode) {
            emitor.enableCountOnlyMode();
        }
        if ( ! disableParseLoop ) {
            emitor.enableParseLoop();
        }
        std::vector<std::thread> workers;

        correctJobsNumber(jobs);

        for(u_int i = 0; i < jobs; ++i) { // TODO: check for inadequate values
            workers.emplace_back(
                    std::thread(Worker(emitor))
                );
        }

        for(auto &p : workers) {
            p.join();
        }

        if (countMode) {
            std::cout << "Matched documents: " << emitor.getCountedDocuments() << std::endl;
        }

        if (emitor.getLastError().size() > 0) {
            std::cerr << emitor.getLastError() << std::endl;
            return 1;
        }

    } else {
        std::cerr << "No input files" << std::endl;
        return 1;
    }

    return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Nicholas Corgan (n.corgan@gmail.com)
 *
 * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
 * or copy at http://opensource.org/licenses/MIT)
 */
#ifndef INCLUDED_SPEC_PKMN_GEN3IMPL_HPP
#define INCLUDED_SPEC_PKMN_GEN3IMPL_HPP

#include <string>

#include <pkmnsim/base_pkmn.hpp>
#include <pkmnsim/pkmn_nature.hpp>
#include <pkmnsim/spec_pkmn.hpp>

namespace pkmnsim
{
    class spec_pkmn_gen345impl: public spec_pkmn
    {
        public:
            spec_pkmn_gen345impl(base_pkmn::sptr base, int lvl, int gen,
                               std::string m1, std::string m2, std::string m3,
                               std::string m4);
            dict<std::string, int> get_stats();
            dict<std::string, int> get_IVs();
            dict<std::string, int> get_EVs();
            std::string get_held_item();
            std::string get_info();
            std::string get_info_verbose();
            int get_gender();
            bool is_shiny();
            pkmn_nature::sptr get_nature();
            std::string get_ability();
            void set_form(int form);
            void set_form(std::string form);

        private:
            int gender;
            std::string ability;
            pkmn_nature::sptr nature;
            dict<std::string, int> volatile_status_map;

            void reset_volatile_status_map();
            int get_hp_from_iv_ev();
            int get_stat_from_iv_ev(std::string, int, int);
            int determine_gender();
            pkmn_nature::sptr determine_nature();
            std::string determine_ability();
    };
}

#endif /*INCLUDED_SPEC_PKMN_GEN3IMPL_HPP*/
<commit_msg>Fixed spec_pkmn_345impl include guards<commit_after>/*
 * Copyright (c) 2013 Nicholas Corgan (n.corgan@gmail.com)
 *
 * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
 * or copy at http://opensource.org/licenses/MIT)
 */
#ifndef INCLUDED_SPEC_PKMN_GEN345IMPL_HPP
#define INCLUDED_SPEC_PKMN_GEN345IMPL_HPP

#include <string>

#include <pkmnsim/base_pkmn.hpp>
#include <pkmnsim/pkmn_nature.hpp>
#include <pkmnsim/spec_pkmn.hpp>

namespace pkmnsim
{
    class spec_pkmn_gen345impl: public spec_pkmn
    {
        public:
            spec_pkmn_gen345impl(base_pkmn::sptr base, int lvl, int gen,
                               std::string m1, std::string m2, std::string m3,
                               std::string m4);
            dict<std::string, int> get_stats();
            dict<std::string, int> get_IVs();
            dict<std::string, int> get_EVs();
            std::string get_held_item();
            std::string get_info();
            std::string get_info_verbose();
            int get_gender();
            bool is_shiny();
            pkmn_nature::sptr get_nature();
            std::string get_ability();
            void set_form(int form);
            void set_form(std::string form);

        private:
            int gender;
            std::string ability;
            pkmn_nature::sptr nature;
            dict<std::string, int> volatile_status_map;

            void reset_volatile_status_map();
            int get_hp_from_iv_ev();
            int get_stat_from_iv_ev(std::string, int, int);
            int determine_gender();
            pkmn_nature::sptr determine_nature();
            std::string determine_ability();
    };
}

#endif /*INCLUDED_SPEC_PKMN_GEN345IMPL_HPP*/
<|endoftext|>


<commit_before>#include <cthun-agent/request_processor.hpp>
#include <cthun-agent/action_outcome.hpp>
#include <cthun-agent/configuration.hpp>
#include <cthun-agent/file_utils.hpp>
#include <cthun-agent/string_utils.hpp>
#include <cthun-agent/rpc_schemas.hpp>
#include <cthun-agent/timer.hpp>
#include <cthun-agent/uuid.hpp>
#include <cthun-agent/external_module.hpp>
#include <cthun-agent/modules/echo.hpp>
#include <cthun-agent/modules/inventory.hpp>
#include <cthun-agent/modules/ping.hpp>
#include <cthun-agent/modules/status.hpp>

#define LEATHERMAN_LOGGING_NAMESPACE "puppetlabs.cthun_agent.action_executer"
#include <leatherman/logging/logging.hpp>

#include <boost/filesystem/operations.hpp>

#include <vector>
#include <atomic>
#include <functional>

namespace CthunAgent {

namespace fs = boost::filesystem;

//
// Results Storage
//

class ResultsStorage {
  public:
    // Throw a file_error in case of failure while writing to any of
    // result files
    ResultsStorage(const ActionRequest& request, const std::string results_dir_)
            : module { request.module() },
              action { request.action() },
              out_path { results_dir_ + "/stdout" },
              err_path { results_dir_ + "/stderr" },
              status_path { results_dir_ + "/status" },
              action_status {} {
        initialize(request);
    }

    void write(const ActionOutcome& outcome, const std::string& exec_error,
               const std::string& duration) {
        action_status.set<std::string>("status", "completed");
        action_status.set<std::string>("duration", duration);
        FileUtils::writeToFile(action_status.toString() + "\n", status_path);

        if (exec_error.empty()) {
            if (outcome.type == ActionOutcome::Type::External) {
                FileUtils::writeToFile(outcome.stdout + "\n", out_path);
                if (!outcome.stderr.empty()) {
                    FileUtils::writeToFile(outcome.stderr + "\n", err_path);
                }
            } else {
                // ActionOutcome::Type::Internal
                FileUtils::writeToFile(outcome.results.toString() + "\n", out_path);
            }
        } else {
            std::string err_msg { "Failed to execute '" + module + " " + action
                                  + "': " + exec_error + "\n" };
            FileUtils::writeToFile(err_msg, err_path);
        }
    }

  private:
    std::string module;
    std::string action;
    std::string out_path;
    std::string err_path;
    std::string status_path;
    CthunClient::DataContainer action_status;

    void initialize(const ActionRequest& request) {
        action_status.set<std::string>("module", module);
        action_status.set<std::string>("action", action);
        action_status.set<std::string>("status", "running");
        action_status.set<std::string>("duration", "0 s");

        if (!request.paramsTxt().empty()) {
            action_status.set<std::string>("input", request.paramsTxt());
        } else {
            action_status.set<std::string>("input", "none");
        }

        FileUtils::writeToFile("", out_path);
        FileUtils::writeToFile("", err_path);
        FileUtils::writeToFile(action_status.toString() + "\n", status_path);
    }
};

//
// Non-blocking action task
//

void nonBlockingActionTask(std::shared_ptr<Module> module_ptr,
                           ActionRequest&& request,
                           std::string job_id,
                           ResultsStorage results_storage,
                           std::shared_ptr<CthunConnector> connector_ptr,
                           std::shared_ptr<std::atomic<bool>> done) {
    CthunAgent::Timer timer {};
    std::string exec_error {};
    ActionOutcome outcome {};

    try {
        outcome = module_ptr->executeAction(request);

        if (request.parsedChunks().data.get<bool>("notify_outcome")) {
            connector_ptr->sendNonBlockingResponse(request, outcome.results, job_id);
        }
    } catch (request_error& e) {
        exec_error = e.what();
        connector_ptr->sendRPCError(request, exec_error);
    }

    // Store results on disk
    auto duration = std::to_string(timer.elapsedSeconds()) + " s";
    results_storage.write(outcome, exec_error, duration);

    // Flag end of processing
    *done = true;
}

//
// Public interface
//

RequestProcessor::RequestProcessor(std::shared_ptr<CthunConnector> connector_ptr,
                                   const std::string& modules_dir)
        : thread_container_ { "Action Executer" },
          connector_ptr_ { connector_ptr },
          spool_dir_ { Configuration::Instance().get<std::string>("spool-dir") } {
    if (!fs::exists(spool_dir_)) {
        LOG_INFO("Creating spool directory '%1%'", spool_dir_);
        if (!FileUtils::createDirectory(spool_dir_)) {
            throw fatal_error { "failed to create the results directory '"
                                + spool_dir_ + "'" };
        }
    }

    // NB: certificate paths are validated by HW
    loadInternalModules();

    if (!modules_dir.empty()) {
        loadExternalModulesFrom(modules_dir);
    } else {
        LOG_INFO("The modules directory was not provided; no external module "
                 "will be loaded");
    }

    logLoadedModules();
}

void RequestProcessor::processRequest(const RequestType& request_type,
                                      const CthunClient::ParsedChunks& parsed_chunks) {
    try {
        // Inspect and validate the request message format
        ActionRequest request { request_type, parsed_chunks };

        LOG_INFO("About to process %1% request %2% by %3%, transaction %4%",
                 requestTypeNames[request_type], request.id(), request.sender(),
                 request.transactionId());

        try {
            // We can access the request content; validate it
            validateRequestContent(request);
        } catch (request_validation_error& e) {
            // Invalid request; send *RPC error*

            LOG_ERROR("Invalid %1% request %2% by %3%, transaction %4%: %5%",
                      requestTypeNames[request_type], request.id(),
                      request.sender(), request.transactionId(), e.what());
            connector_ptr_->sendRPCError(request, e.what());
            return;
        }

        try {
            if (request.type() == RequestType::Blocking) {
                processBlockingRequest(request);
            } else {
                processNonBlockingRequest(request);
            }
        } catch (request_error& e) {
            // Process failure; send *RPC error*
            LOG_ERROR("Failed to process %1% request %2% by %3%, transaction %4%: "
                      "%5%", requestTypeNames[request.type()], request.id(),
                      request.sender(), request.transactionId(), e.what());
            connector_ptr_->sendRPCError(request, e.what());
        }
    } catch (request_format_error& e) {
        // Failed to instantiate ActionRequest - bad message; send *Cthun error*

        auto id = parsed_chunks.envelope.get<std::string>("id");
        auto sender = parsed_chunks.envelope.get<std::string>("sender");
        std::vector<std::string> endpoints { sender };
        LOG_ERROR("Invalid %1% request by %2%: %3%", id, sender, e.what());
        connector_ptr_->sendCthunError(id, e.what(), endpoints);
    }
}

//
// Private interface
//

void RequestProcessor::validateRequestContent(const ActionRequest& request) {
    try {
        if (!modules_.at(request.module())->hasAction(request.action())) {
            throw request_validation_error { "unknown action '" + request.action()
                                             + "' for module " + request.module() };
        }
    } catch (std::out_of_range& e) {
        throw request_validation_error { "unknown module: " + request.module() };
    }
}

void RequestProcessor::processBlockingRequest(const ActionRequest& request) {
    // Execute action; possible request errors will be propagated
    auto outcome = modules_[request.module()]->executeAction(request);

    connector_ptr_->sendBlockingResponse(request, outcome.results);
}

void RequestProcessor::processNonBlockingRequest(const ActionRequest& request) {
    auto job_id = UUID::getUUID();

    // HERE(ale): assuming spool_dir ends with '/' (up to Configuration)
    std::string results_dir { spool_dir_ + job_id };

    if (!fs::exists(results_dir)) {
        LOG_DEBUG("Creating results directory for the '%1% %2%' job with ID %3% "
                  "for request transaction %4%", request.module(),
                  request.action(), job_id, request.transactionId());
        if (!FileUtils::createDirectory(results_dir)) {
            throw request_processing_error { "failed to create directory '"
                                             + results_dir + "'" };
        }
    }

    LOG_DEBUG("Starting '%1% %2%' job with ID %3% for non-blocking request %4% "
              "by %5%, transaction %6%", request.module(), request.action(),
              job_id, request.id(), request.sender(), request.transactionId());

    // Keep track of errors to write on file
    std::string err_msg {};

    try {
        // Flag to enable signaling from task to thread_container
        auto done = std::make_shared<std::atomic<bool>>(false);

        thread_container_.add(std::thread(&nonBlockingActionTask,
                                          modules_[request.module()],
                                          request,
                                          job_id,
                                          ResultsStorage { request, results_dir },
                                          connector_ptr_,
                                          done),
                              done);
    } catch (file_error& e) {
        // Failed to instantiate ResultsStorage
        LOG_ERROR("Failed to initialize the result files for '%1% %2%' action "
                  "job with ID %3%: %4%", request.module(), request.action(),
                  job_id, e.what());
        err_msg = std::string { "failed to initialize result files: " } + e.what();
    } catch (std::exception& e) {
        LOG_ERROR("Failed to spawn '%1% %2%' action job with ID %3%: %4%",
                  request.module(), request.action(), job_id, e.what());
        err_msg = std::string { "failed to start action task: " } + e.what();
    }

    // Send back provisional data
    connector_ptr_->sendProvisionalResponse(request, job_id, err_msg);
}

void RequestProcessor::loadInternalModules() {
    modules_["echo"] = std::shared_ptr<Module>(new Modules::Echo);
    modules_["inventory"] = std::shared_ptr<Module>(new Modules::Inventory);
    modules_["ping"] = std::shared_ptr<Module>(new Modules::Ping);
    modules_["status"] = std::shared_ptr<Module>(new Modules::Status);
}

void RequestProcessor::loadExternalModulesFrom(fs::path dir_path) {
    LOG_INFO("Loading external modules from %1%", dir_path.string());

    if (fs::is_directory(dir_path)) {
        fs::directory_iterator end;

        for (auto f = fs::directory_iterator(dir_path); f != end; ++f) {
            if (!fs::is_directory(f->status())) {
                auto f_p = f->path().string();

                try {
                    ExternalModule* e_m = new ExternalModule(f_p);
                    modules_[e_m->module_name] = std::shared_ptr<Module>(e_m);
                } catch (module_error& e) {
                    LOG_ERROR("Failed to load %1%; %2%", f_p, e.what());
                } catch (std::exception& e) {
                    LOG_ERROR("Unexpected error when loading %1%; %2%",
                              f_p, e.what());
                } catch (...) {
                    LOG_ERROR("Unexpected error when loading %1%", f_p);
                }
            }
        }
    } else {
        LOG_WARNING("Failed to locate the modules directory; no external "
                    "module will be loaded");
    }
}

void RequestProcessor::logLoadedModules() const {
    for (auto& module : modules_) {
        std::string txt { "found no action" };
        std::string actions_list { "" };

        for (auto& action : module.second->actions) {
            if (actions_list.empty()) {
                txt = "action";
                actions_list += ": ";
            } else {
                actions_list += ", ";
            }
            actions_list += action;
        }

        auto txt_suffix = StringUtils::plural(module.second->actions.size());
        LOG_INFO("Loaded '%1%' module - %2%%3%%4%",
                 module.first, txt, txt_suffix, actions_list);
    }
}

}  // namespace CthunAgent
<commit_msg>(maint) Create results dir in ResultsStorage<commit_after>#include <cthun-agent/request_processor.hpp>
#include <cthun-agent/action_outcome.hpp>
#include <cthun-agent/configuration.hpp>
#include <cthun-agent/file_utils.hpp>
#include <cthun-agent/string_utils.hpp>
#include <cthun-agent/rpc_schemas.hpp>
#include <cthun-agent/timer.hpp>
#include <cthun-agent/uuid.hpp>
#include <cthun-agent/external_module.hpp>
#include <cthun-agent/modules/echo.hpp>
#include <cthun-agent/modules/inventory.hpp>
#include <cthun-agent/modules/ping.hpp>
#include <cthun-agent/modules/status.hpp>

#define LEATHERMAN_LOGGING_NAMESPACE "puppetlabs.cthun_agent.action_executer"
#include <leatherman/logging/logging.hpp>

#include <boost/filesystem/operations.hpp>

#include <vector>
#include <atomic>
#include <functional>
#include <stdexcept>  // out_of_range

namespace CthunAgent {

namespace fs = boost::filesystem;

//
// Results Storage
//

class ResultsStorage {
  public:
    // Throw a file_error in case of failure while writing to any of
    // result files
    ResultsStorage(const ActionRequest& request, const std::string& results_dir)
            : module { request.module() },
              action { request.action() },
              out_path { results_dir + "/stdout" },
              err_path { results_dir + "/stderr" },
              status_path { results_dir + "/status" },
              action_status {} {
        initialize(request, results_dir);
    }

    void write(const ActionOutcome& outcome, const std::string& exec_error,
               const std::string& duration) {
        action_status.set<std::string>("status", "completed");
        action_status.set<std::string>("duration", duration);
        FileUtils::writeToFile(action_status.toString() + "\n", status_path);

        if (exec_error.empty()) {
            if (outcome.type == ActionOutcome::Type::External) {
                FileUtils::writeToFile(outcome.stdout + "\n", out_path);
                if (!outcome.stderr.empty()) {
                    FileUtils::writeToFile(outcome.stderr + "\n", err_path);
                }
            } else {
                // ActionOutcome::Type::Internal
                FileUtils::writeToFile(outcome.results.toString() + "\n", out_path);
            }
        } else {
            FileUtils::writeToFile(exec_error, err_path);
        }
    }

  private:
    std::string module;
    std::string action;
    std::string out_path;
    std::string err_path;
    std::string status_path;
    CthunClient::DataContainer action_status;

    void initialize(const ActionRequest& request, const std::string results_dir) {
        if (!fs::exists(results_dir)) {
            LOG_DEBUG("Creating results directory for '%1% %2%', transaction "
                       "%3%, in '%4%'", request.module(), request.action(),
                       request.transactionId(), results_dir);
            if (!FileUtils::createDirectory(results_dir)) {
                throw file_error { "failed to create directory '"
                                   + results_dir + "'" };
            }
        }

        action_status.set<std::string>("module", module);
        action_status.set<std::string>("action", action);
        action_status.set<std::string>("status", "running");
        action_status.set<std::string>("duration", "0 s");

        if (!request.paramsTxt().empty()) {
            action_status.set<std::string>("input", request.paramsTxt());
        } else {
            action_status.set<std::string>("input", "none");
        }

        FileUtils::writeToFile("", out_path);
        FileUtils::writeToFile("", err_path);
        FileUtils::writeToFile(action_status.toString() + "\n", status_path);
    }
};

//
// Non-blocking action task
//

void nonBlockingActionTask(std::shared_ptr<Module> module_ptr,
                           ActionRequest&& request,
                           std::string job_id,
                           ResultsStorage results_storage,
                           std::shared_ptr<CthunConnector> connector_ptr,
                           std::shared_ptr<std::atomic<bool>> done) {
    CthunAgent::Timer timer {};
    std::string exec_error {};
    ActionOutcome outcome {};

    try {
        outcome = module_ptr->executeAction(request);

        if (request.parsedChunks().data.get<bool>("notify_outcome")) {
            connector_ptr->sendNonBlockingResponse(request, outcome.results, job_id);
        }
    } catch (request_error& e) {
        connector_ptr->sendRPCError(request, e.what());
        exec_error = "Failed to execute '" + request.module() + " "
                     + request.action() + "': " + e.what() + "\n";
    }

    // Store results on disk
    auto duration = std::to_string(timer.elapsedSeconds()) + " s";
    results_storage.write(outcome, exec_error, duration);

    // Flag end of processing
    *done = true;
}

//
// Public interface
//

RequestProcessor::RequestProcessor(std::shared_ptr<CthunConnector> connector_ptr,
                                   const std::string& modules_dir)
        : thread_container_ { "Action Executer" },
          connector_ptr_ { connector_ptr },
          spool_dir_ { Configuration::Instance().get<std::string>("spool-dir") } {
    if (!fs::exists(spool_dir_)) {
        LOG_INFO("Creating spool directory '%1%'", spool_dir_);
        if (!FileUtils::createDirectory(spool_dir_)) {
            throw fatal_error { "failed to create the results directory '"
                                + spool_dir_ + "'" };
        }
    }

    // NB: certificate paths are validated by HW
    loadInternalModules();

    if (!modules_dir.empty()) {
        loadExternalModulesFrom(modules_dir);
    } else {
        LOG_INFO("The modules directory was not provided; no external module "
                 "will be loaded");
    }

    logLoadedModules();
}

void RequestProcessor::processRequest(const RequestType& request_type,
                                      const CthunClient::ParsedChunks& parsed_chunks) {
    try {
        // Inspect and validate the request message format
        ActionRequest request { request_type, parsed_chunks };

        LOG_INFO("About to process %1% request %2% by %3%, transaction %4%",
                 requestTypeNames[request_type], request.id(), request.sender(),
                 request.transactionId());

        try {
            // We can access the request content; validate it
            validateRequestContent(request);
        } catch (request_validation_error& e) {
            // Invalid request; send *RPC error*

            LOG_ERROR("Invalid %1% request %2% by %3%, transaction %4%: %5%",
                      requestTypeNames[request_type], request.id(),
                      request.sender(), request.transactionId(), e.what());
            connector_ptr_->sendRPCError(request, e.what());
            return;
        }

        try {
            if (request.type() == RequestType::Blocking) {
                processBlockingRequest(request);
            } else {
                processNonBlockingRequest(request);
            }
        } catch (request_error& e) {
            // Process failure; send *RPC error*
            LOG_ERROR("Failed to process %1% request %2% by %3%, transaction %4%: "
                      "%5%", requestTypeNames[request.type()], request.id(),
                      request.sender(), request.transactionId(), e.what());
            connector_ptr_->sendRPCError(request, e.what());
        }
    } catch (request_format_error& e) {
        // Failed to instantiate ActionRequest - bad message; send *Cthun error*

        auto id = parsed_chunks.envelope.get<std::string>("id");
        auto sender = parsed_chunks.envelope.get<std::string>("sender");
        std::vector<std::string> endpoints { sender };
        LOG_ERROR("Invalid %1% request by %2%: %3%", id, sender, e.what());
        connector_ptr_->sendCthunError(id, e.what(), endpoints);
    }
}

//
// Private interface
//

void RequestProcessor::validateRequestContent(const ActionRequest& request) {
    try {
        if (!modules_.at(request.module())->hasAction(request.action())) {
            throw request_validation_error { "unknown action '" + request.action()
                                             + "' for module " + request.module() };
        }
    } catch (std::out_of_range& e) {
        throw request_validation_error { "unknown module: " + request.module() };
    }
}

void RequestProcessor::processBlockingRequest(const ActionRequest& request) {
    // Execute action; possible request errors will be propagated
    auto outcome = modules_[request.module()]->executeAction(request);

    connector_ptr_->sendBlockingResponse(request, outcome.results);
}

void RequestProcessor::processNonBlockingRequest(const ActionRequest& request) {
    auto job_id = UUID::getUUID();

    // HERE(ale): assuming spool_dir ends with '/' (up to Configuration)
    std::string results_dir { spool_dir_ + job_id };

    LOG_DEBUG("Starting '%1% %2%' job with ID %3% for non-blocking request %4% "
              "by %5%, transaction %6%", request.module(), request.action(),
              job_id, request.id(), request.sender(), request.transactionId());

    // To keep track of errors and write them on file
    std::string err_msg {};

    try {
        // Flag to enable signaling from task to thread_container
        auto done = std::make_shared<std::atomic<bool>>(false);

        thread_container_.add(std::thread(&nonBlockingActionTask,
                                          modules_[request.module()],
                                          request,
                                          job_id,
                                          ResultsStorage { request, results_dir },
                                          connector_ptr_,
                                          done),
                              done);
    } catch (file_error& e) {
        // Failed to instantiate ResultsStorage
        LOG_ERROR("Failed to initialize the result files for '%1% %2%' action "
                  "job with ID %3%: %4%", request.module(), request.action(),
                  job_id, e.what());
        err_msg = std::string { "failed to initialize result files: " } + e.what();
    } catch (std::exception& e) {
        LOG_ERROR("Failed to spawn '%1% %2%' action job with ID %3%: %4%",
                  request.module(), request.action(), job_id, e.what());
        err_msg = std::string { "failed to start action task: " } + e.what();
    }

    // Send back provisional data
    connector_ptr_->sendProvisionalResponse(request, job_id, err_msg);
}

void RequestProcessor::loadInternalModules() {
    modules_["echo"] = std::shared_ptr<Module>(new Modules::Echo);
    modules_["inventory"] = std::shared_ptr<Module>(new Modules::Inventory);
    modules_["ping"] = std::shared_ptr<Module>(new Modules::Ping);
    modules_["status"] = std::shared_ptr<Module>(new Modules::Status);
}

void RequestProcessor::loadExternalModulesFrom(fs::path dir_path) {
    LOG_INFO("Loading external modules from %1%", dir_path.string());

    if (fs::is_directory(dir_path)) {
        fs::directory_iterator end;

        for (auto f = fs::directory_iterator(dir_path); f != end; ++f) {
            if (!fs::is_directory(f->status())) {
                auto f_p = f->path().string();

                try {
                    ExternalModule* e_m = new ExternalModule(f_p);
                    modules_[e_m->module_name] = std::shared_ptr<Module>(e_m);
                } catch (module_error& e) {
                    LOG_ERROR("Failed to load %1%; %2%", f_p, e.what());
                } catch (std::exception& e) {
                    LOG_ERROR("Unexpected error when loading %1%; %2%",
                              f_p, e.what());
                } catch (...) {
                    LOG_ERROR("Unexpected error when loading %1%", f_p);
                }
            }
        }
    } else {
        LOG_WARNING("Failed to locate the modules directory; no external "
                    "module will be loaded");
    }
}

void RequestProcessor::logLoadedModules() const {
    for (auto& module : modules_) {
        std::string txt { "found no action" };
        std::string actions_list { "" };

        for (auto& action : module.second->actions) {
            if (actions_list.empty()) {
                txt = "action";
                actions_list += ": ";
            } else {
                actions_list += ", ";
            }
            actions_list += action;
        }

        auto txt_suffix = StringUtils::plural(module.second->actions.size());
        LOG_INFO("Loaded '%1%' module - %2%%3%%4%",
                 module.first, txt, txt_suffix, actions_list);
    }
}

}  // namespace CthunAgent
<|endoftext|>


<commit_before>/*
    This file is part of the "pfkutils" tools written by Phil Knaack
    (pknaack1@netscape.net).
    Copyright (C) 2008  Phillip F Knaack

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

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

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

/** \file main.C
 * \brief the "main" entry point for pfkbak tool.
 * \author Phillip F Knaack
 */

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#include <pfkutils_config.h>

#include "Btree.H"
#include "params.H"
#include "database_elements.H"
#include "protos.H"

#define SEPARATE_DATA_FILE 0
#define META_CACHE_SIZE (256*1024*1024)
#define DATA_CACHE_SIZE ( 16*1024*1024)
#define BTREE_ORDER  15

/** the btree key for the database info uses this string constant.
 */
const char * PfkBackupDbInfoKey::INFO_KEY = "PfkBakDbInfo";

/** display command line help for this tool, and exit.
 */
static void
usage(void)
{
    fprintf(stderr, "version: %s\n", PACKAGE_STRING);
    fprintf(stderr, "usage: \n"
"create file:    pfkbak -C[Vv] BACKUP-FILE\n"
"\n"
"list backups:   pfkbak -L[Vv] BACKUP-FILE\n"
"create backup:  pfkbak -c[Vv] BACKUP-FILE BACKUP-NAME DIR COMMENT\n"
"delete backup:  (unimplemented) pfkbak -D[Vv] BACKUP-FILE BACKUP-NAME\n"
"update backup:  pfkbak -u[Vv] BACKUP-FILE BACKUP-NAME\n"
"\n"
"delete gens:    pfkbak -d[Vv] BACKUP-FILE BACKUP-NAME GenN A-B -B\n"
"\n"
"list files:     pfkbak -l[Vv] BACKUP-FILE BACKUP-NAME GenN\n"
"\n"
"extract:        pfkbak -e[Vv] BACKUP-FILE BACKUP-NAME GenN   (unimplemented [FILE...])\n"
"extract list:   (unimplemented) pfkbak -E[Vv] BACKUP-FILE BACKUP-NAME GenN LIST-FILE\n"
            "\n"
        );
    exit(1);
}

/** describe what -v or -V command line options were specified.
 */
verblevel  pfkbak_verb;

Btree              * pfkbak_meta;
FileBlockInterface * pfkbak_data;
char               * pfkbak_file;
char               * pfkbak_data_file;
char               * pfkbak_name;

/** the main function of this tool.
 * it is not called 'main' because it is referenced by the pfkutils
 * tool's main function.
 */
extern "C" int
pfkbak_main(int argc, char ** argv)
{
    backop     pfkbak_op;
    UINT32     baknum = 0;

    pfkbak_op = BAK_NONE;
    pfkbak_verb = VERB_QUIET;

    if (argc < 3 || argv[1][0] != '-')
        usage();

    char * cmd = &argv[1][1];
    if (cmd[1] == 'v')
        pfkbak_verb = VERB_1;
    else if (cmd[1] == 'V')
        pfkbak_verb = VERB_2;

    // in every command format, argv[2] is the backup file.
    // in every command which has argv[3], it is the backup name.
    pfkbak_file = argv[2];
    if (argc > 3)
        pfkbak_name = argv[3];
    else
        pfkbak_name = NULL;

    pfkbak_data_file = (char*) malloc(strlen(pfkbak_file)+5);
    sprintf(pfkbak_data_file, "%s.data", pfkbak_file);

    // assign pfkbak_op
#define OP(c,op) case c: pfkbak_op = BAK_##op; break;
    switch (*cmd)
    {
        OP('C',CREATE_FILE);
        OP('L',LIST_BACKUPS);
        OP('c',CREATE_BACKUP);
        OP('D',DELETE_BACKUP);
        OP('u',UPDATE_BACKUP);
        OP('d',DELETE_GENS);
        OP('l',LIST_FILES);
        OP('e',EXTRACT);
        OP('E',EXTRACT_LIST);
    default:
        usage();
    }
#undef OP

    // verify number of command line arguments
    switch (pfkbak_op)
    {
    case BAK_CREATE_FILE:
    case BAK_LIST_BACKUPS:
        if (argc != 3) usage(); break;

    case BAK_DELETE_BACKUP:
    case BAK_UPDATE_BACKUP:
        if (argc != 4) usage(); break;

    case BAK_LIST_FILES:
        if (argc != 5) usage(); break;

    case BAK_DELETE_GENS:
        if (argc < 5)  usage(); break;

    case BAK_EXTRACT:
        if (argc < 5)  usage(); break;

    case BAK_CREATE_BACKUP:
    case BAK_EXTRACT_LIST:
        if (argc != 6) usage(); break;

    case BAK_NONE:
    default:
        // shouldn't be reached but it satisifies compiler.
        usage();
    }

    Btree * bt = NULL;
    FileBlockInterface * fbi = NULL;

#if SEPARATE_DATA_FILE
    int bt_cache_size = META_CACHE_SIZE;
#else
    int bt_cache_size = DATA_CACHE_SIZE;
#endif

    // create or open btree file
    if (pfkbak_op == BAK_CREATE_FILE)
    {
        bt = Btree::createFile( pfkbak_file, bt_cache_size,
                                0600, BTREE_ORDER );

        if (!bt)
        {
            fprintf(stderr, "unable to create file: %s: %s\n",
                    pfkbak_file, strerror(errno));
            return 1;
        }

#if SEPARATE_DATA_FILE
        fbi = FileBlockInterface::createFile( pfkbak_data_file,
                                              DATA_CACHE_SIZE, 0600 );

        if (!fbi)
        {
            fprintf(stderr, "unable to create file: %s: %s\n",
                    pfkbak_data_file, strerror(errno));
            return 1;
        }
#else
        fbi = bt->get_fbi();
#endif
    }
    else
    {
        bt = Btree::openFile( pfkbak_file, bt_cache_size );

        if (!bt)
        {
            fprintf(stderr, "Unable to open backup file: %s: %s\n",
                    pfkbak_file, strerror(errno));
            return 1;
        }

        PfkBackupDbInfo   info(bt);
        if (!pfkbak_get_info( &info ))
        {
            delete bt;
            fprintf(stderr, "unable to validate database!\n");
            return 1;
        }

#if SEPARATE_DATA_FILE
        fbi = FileBlockInterface::openFile( pfkbak_data_file,
                                            DATA_CACHE_SIZE );

        if (!fbi)
        {
            fprintf(stderr, "Unable to open backup data file: %s: %s\n",
                    pfkbak_data_file, strerror(errno));
            return 1;
        }
#else
        fbi = bt->get_fbi();
#endif
    }

    pfkbak_meta = bt;
    pfkbak_data = fbi;

    switch (pfkbak_op)
    {
    case BAK_CREATE_BACKUP:
        baknum = pfkbak_find_backup(pfkbak_name);
        if (baknum != 0)
        {
            delete bt;
            fprintf(stderr, "a backup named '%s' already exists "
                    "in this database.\n", pfkbak_name);
            return 1;
        }
        break;

    case BAK_DELETE_BACKUP:
    case BAK_UPDATE_BACKUP:
    case BAK_DELETE_GENS:
    case BAK_LIST_FILES:
    case BAK_EXTRACT:
    case BAK_EXTRACT_LIST:
        baknum = pfkbak_find_backup(pfkbak_name);
        if (baknum == 0)
        {
            delete bt;
            fprintf(stderr, "a backup named '%s' is not found "
                    "in this database.\n", pfkbak_name);
            return 1;
        }
        break;
    }

    // now call the relevant function to handle the op.
    switch (pfkbak_op)
    {
    case BAK_CREATE_FILE:
        pfkbak_create_file( pfkbak_file );
        break;
    case BAK_LIST_BACKUPS:
        pfkbak_list_backups();
        break;
    case BAK_CREATE_BACKUP:
        pfkbak_create_backup( pfkbak_name, argv[4], argv[5] );
        break;
    case BAK_DELETE_BACKUP:
        pfkbak_delete_backup( baknum );
        break;
    case BAK_UPDATE_BACKUP:
        pfkbak_update_backup( baknum );
        break;
    case BAK_DELETE_GENS:
        pfkbak_delete_gens( baknum, argc-4, argv+4 );
        break;
    case BAK_LIST_FILES:
        pfkbak_list_files( baknum, atoi(argv[4]) );
        break;
    case BAK_EXTRACT:
        pfkbak_extract( baknum, atoi(argv[4]), argc-5, argv+5 );
        break;
    case BAK_EXTRACT_LIST:
        pfkbak_extract_list( baknum, atoi(argv[4]), argv[5] );
        break;
    case BAK_NONE:
    default:
        // shouldn't be reached but it satisifies compiler.
        usage();
    }

    delete bt;
#if SEPARATE_DATA_FILE
    delete fbi;
#endif
    return 0;
}
<commit_msg>use one file for backups and use a large cache<commit_after>/*
    This file is part of the "pfkutils" tools written by Phil Knaack
    (pknaack1@netscape.net).
    Copyright (C) 2008  Phillip F Knaack

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

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

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

/** \file main.C
 * \brief the "main" entry point for pfkbak tool.
 * \author Phillip F Knaack
 */

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#include <pfkutils_config.h>

#include "Btree.H"
#include "params.H"
#include "database_elements.H"
#include "protos.H"

#define SEPARATE_DATA_FILE 0
#define CACHE_SIZE (512*1024*1024)
#define BTREE_ORDER  25

/** the btree key for the database info uses this string constant.
 */
const char * PfkBackupDbInfoKey::INFO_KEY = "PfkBakDbInfo";

/** display command line help for this tool, and exit.
 */
static void
usage(void)
{
    fprintf(stderr, "version: %s\n", PACKAGE_STRING);
    fprintf(stderr, "usage: \n"
"create file:    pfkbak -C[Vv] BACKUP-FILE\n"
"\n"
"list backups:   pfkbak -L[Vv] BACKUP-FILE\n"
"create backup:  pfkbak -c[Vv] BACKUP-FILE BACKUP-NAME DIR COMMENT\n"
"delete backup:  (unimplemented) pfkbak -D[Vv] BACKUP-FILE BACKUP-NAME\n"
"update backup:  pfkbak -u[Vv] BACKUP-FILE BACKUP-NAME\n"
"\n"
"delete gens:    pfkbak -d[Vv] BACKUP-FILE BACKUP-NAME GenN A-B -B\n"
"\n"
"list files:     pfkbak -l[Vv] BACKUP-FILE BACKUP-NAME GenN\n"
"\n"
"extract:        pfkbak -e[Vv] BACKUP-FILE BACKUP-NAME GenN   (unimplemented [FILE...])\n"
"extract list:   (unimplemented) pfkbak -E[Vv] BACKUP-FILE BACKUP-NAME GenN LIST-FILE\n"
            "\n"
        );
    exit(1);
}

/** describe what -v or -V command line options were specified.
 */
verblevel  pfkbak_verb;

Btree              * pfkbak_meta;
FileBlockInterface * pfkbak_data;
char               * pfkbak_file;
char               * pfkbak_data_file;
char               * pfkbak_name;

/** the main function of this tool.
 * it is not called 'main' because it is referenced by the pfkutils
 * tool's main function.
 */
extern "C" int
pfkbak_main(int argc, char ** argv)
{
    backop     pfkbak_op;
    UINT32     baknum = 0;

    pfkbak_op = BAK_NONE;
    pfkbak_verb = VERB_QUIET;

    if (argc < 3 || argv[1][0] != '-')
        usage();

    char * cmd = &argv[1][1];
    if (cmd[1] == 'v')
        pfkbak_verb = VERB_1;
    else if (cmd[1] == 'V')
        pfkbak_verb = VERB_2;

    // in every command format, argv[2] is the backup file.
    // in every command which has argv[3], it is the backup name.
    pfkbak_file = argv[2];
    if (argc > 3)
        pfkbak_name = argv[3];
    else
        pfkbak_name = NULL;

    pfkbak_data_file = (char*) malloc(strlen(pfkbak_file)+5);
    sprintf(pfkbak_data_file, "%s.data", pfkbak_file);

    // assign pfkbak_op
#define OP(c,op) case c: pfkbak_op = BAK_##op; break;
    switch (*cmd)
    {
        OP('C',CREATE_FILE);
        OP('L',LIST_BACKUPS);
        OP('c',CREATE_BACKUP);
        OP('D',DELETE_BACKUP);
        OP('u',UPDATE_BACKUP);
        OP('d',DELETE_GENS);
        OP('l',LIST_FILES);
        OP('e',EXTRACT);
        OP('E',EXTRACT_LIST);
    default:
        usage();
    }
#undef OP

    // verify number of command line arguments
    switch (pfkbak_op)
    {
    case BAK_CREATE_FILE:
    case BAK_LIST_BACKUPS:
        if (argc != 3) usage(); break;

    case BAK_DELETE_BACKUP:
    case BAK_UPDATE_BACKUP:
        if (argc != 4) usage(); break;

    case BAK_LIST_FILES:
        if (argc != 5) usage(); break;

    case BAK_DELETE_GENS:
        if (argc < 5)  usage(); break;

    case BAK_EXTRACT:
        if (argc < 5)  usage(); break;

    case BAK_CREATE_BACKUP:
    case BAK_EXTRACT_LIST:
        if (argc != 6) usage(); break;

    case BAK_NONE:
    default:
        // shouldn't be reached but it satisifies compiler.
        usage();
    }

    Btree * bt = NULL;
    FileBlockInterface * fbi = NULL;

    int bt_cache_size = CACHE_SIZE;

    // create or open btree file
    if (pfkbak_op == BAK_CREATE_FILE)
    {
        bt = Btree::createFile( pfkbak_file, bt_cache_size,
                                0600, BTREE_ORDER );

        if (!bt)
        {
            fprintf(stderr, "unable to create file: %s: %s\n",
                    pfkbak_file, strerror(errno));
            return 1;
        }

#if SEPARATE_DATA_FILE
        fbi = FileBlockInterface::createFile( pfkbak_data_file,
                                              DATA_CACHE_SIZE, 0600 );

        if (!fbi)
        {
            fprintf(stderr, "unable to create file: %s: %s\n",
                    pfkbak_data_file, strerror(errno));
            return 1;
        }
#else
        fbi = bt->get_fbi();
#endif
    }
    else
    {
        bt = Btree::openFile( pfkbak_file, bt_cache_size );

        if (!bt)
        {
            fprintf(stderr, "Unable to open backup file: %s: %s\n",
                    pfkbak_file, strerror(errno));
            return 1;
        }

        PfkBackupDbInfo   info(bt);
        if (!pfkbak_get_info( &info ))
        {
            delete bt;
            fprintf(stderr, "unable to validate database!\n");
            return 1;
        }

#if SEPARATE_DATA_FILE
        fbi = FileBlockInterface::openFile( pfkbak_data_file,
                                            DATA_CACHE_SIZE );

        if (!fbi)
        {
            fprintf(stderr, "Unable to open backup data file: %s: %s\n",
                    pfkbak_data_file, strerror(errno));
            return 1;
        }
#else
        fbi = bt->get_fbi();
#endif
    }

    pfkbak_meta = bt;
    pfkbak_data = fbi;

    switch (pfkbak_op)
    {
    case BAK_CREATE_BACKUP:
        baknum = pfkbak_find_backup(pfkbak_name);
        if (baknum != 0)
        {
            delete bt;
            fprintf(stderr, "a backup named '%s' already exists "
                    "in this database.\n", pfkbak_name);
            return 1;
        }
        break;

    case BAK_DELETE_BACKUP:
    case BAK_UPDATE_BACKUP:
    case BAK_DELETE_GENS:
    case BAK_LIST_FILES:
    case BAK_EXTRACT:
    case BAK_EXTRACT_LIST:
        baknum = pfkbak_find_backup(pfkbak_name);
        if (baknum == 0)
        {
            delete bt;
            fprintf(stderr, "a backup named '%s' is not found "
                    "in this database.\n", pfkbak_name);
            return 1;
        }
        break;
    }

    // now call the relevant function to handle the op.
    switch (pfkbak_op)
    {
    case BAK_CREATE_FILE:
        pfkbak_create_file( pfkbak_file );
        break;
    case BAK_LIST_BACKUPS:
        pfkbak_list_backups();
        break;
    case BAK_CREATE_BACKUP:
        pfkbak_create_backup( pfkbak_name, argv[4], argv[5] );
        break;
    case BAK_DELETE_BACKUP:
        pfkbak_delete_backup( baknum );
        break;
    case BAK_UPDATE_BACKUP:
        pfkbak_update_backup( baknum );
        break;
    case BAK_DELETE_GENS:
        pfkbak_delete_gens( baknum, argc-4, argv+4 );
        break;
    case BAK_LIST_FILES:
        pfkbak_list_files( baknum, atoi(argv[4]) );
        break;
    case BAK_EXTRACT:
        pfkbak_extract( baknum, atoi(argv[4]), argc-5, argv+5 );
        break;
    case BAK_EXTRACT_LIST:
        pfkbak_extract_list( baknum, atoi(argv[4]), argv[5] );
        break;
    case BAK_NONE:
    default:
        // shouldn't be reached but it satisifies compiler.
        usage();
    }

    delete bt;
#if SEPARATE_DATA_FILE
    delete fbi;
#endif
    return 0;
}
<|endoftext|>


<commit_before>#include <cstdlib>
#include "pkcs11test.h"

using namespace std;  // So sue me

TEST_F(PKCS11Test, EnumerateSlots) {
  // First determine how many slots.
  CK_ULONG slot_count;
  EXPECT_CKR_OK(g_fns->C_GetSlotList(CK_FALSE, NULL_PTR, &slot_count));
  unique_ptr<CK_SLOT_ID, freer> slot((CK_SLOT_ID*)malloc(slot_count * sizeof(CK_SLOT_ID)));
  // Retrieve slot list.
  EXPECT_CKR_OK(g_fns->C_GetSlotList(CK_FALSE, slot.get(), &slot_count));
  for (int ii = 0; ii < slot_count; ii++) {
    CK_SLOT_INFO slot_info;
    EXPECT_CKR_OK(g_fns->C_GetSlotInfo(slot.get()[ii], &slot_info));
    cout << "slot[" << ii << "] = " << (unsigned int)slot.get()[ii] << " = " << slot_description(&slot_info) << endl;
    if (slot_info.flags & CKF_TOKEN_PRESENT) {
      CK_TOKEN_INFO token;
      EXPECT_CKR_OK(g_fns->C_GetTokenInfo(slot.get()[ii], &token));
      cout << "  " << token_description(&token) << endl;
    }

  }
}
<commit_msg>Add mechanism enumeration<commit_after>#include <cstdlib>
#include "pkcs11test.h"

using namespace std;  // So sue me

TEST_F(PKCS11Test, EnumerateSlots) {
  // First determine how many slots.
  CK_ULONG slot_count;
  EXPECT_CKR_OK(g_fns->C_GetSlotList(CK_FALSE, NULL_PTR, &slot_count));
  unique_ptr<CK_SLOT_ID, freer> slot((CK_SLOT_ID*)malloc(slot_count * sizeof(CK_SLOT_ID)));
  // Retrieve slot list.
  EXPECT_CKR_OK(g_fns->C_GetSlotList(CK_FALSE, slot.get(), &slot_count));
  for (int ii = 0; ii < slot_count; ii++) {
    CK_SLOT_INFO slot_info;
    EXPECT_CKR_OK(g_fns->C_GetSlotInfo(slot.get()[ii], &slot_info));
    cout << "slot[" << ii << "] = " << (unsigned int)slot.get()[ii] << " = " << slot_description(&slot_info) << endl;
    if (slot_info.flags & CKF_TOKEN_PRESENT) {
      CK_TOKEN_INFO token;
      EXPECT_CKR_OK(g_fns->C_GetTokenInfo(slot.get()[ii], &token));
      cout << "  " << token_description(&token) << endl;
    }
  }
}

TEST_F(PKCS11Test, EnumerateMechanisms) {
  CK_ULONG mechanism_count;
  EXPECT_CKR_OK(g_fns->C_GetMechanismList(g_slot_id, NULL_PTR, &mechanism_count));
  unique_ptr<CK_MECHANISM_TYPE, freer> mechanism((CK_MECHANISM_TYPE_PTR)malloc(mechanism_count * sizeof(CK_MECHANISM_TYPE)));
  EXPECT_CKR_OK(g_fns->C_GetMechanismList(g_slot_id, mechanism.get(), &mechanism_count));
  for (int ii = 0; ii < mechanism_count; ii++) {
    CK_MECHANISM_INFO mechanism_info;
    EXPECT_CKR_OK(g_fns->C_GetMechanismInfo(g_slot_id, mechanism.get()[ii], &mechanism_info));
    cout << "mechanism[" << ii << "]=" << mechanism_type_name(mechanism.get()[ii]) << mechanism_info_description(&mechanism_info) << endl;
  }
}
<|endoftext|>