<commit_before>/*******************************************************************************
* Copyright 2016 ROBOTIS CO., LTD.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/

/* Authors: Taehoon Lim (Darby) */

#include <ros/ros.h>
#include <sensor_msgs/BatteryState.h>
#include <sensor_msgs/Imu.h>
#include <sensor_msgs/MagneticField.h>
#include <sensor_msgs/LaserScan.h>
#include <diagnostic_msgs/DiagnosticArray.h>
#include <turtlebot3_msgs/SensorState.h>
#include <turtlebot3_msgs/VersionInfo.h>

#define SOFTWARE_VERSION "1.0.0"
#define FIRMWARE_VERSION "1.2.0"
#define HARDWARE_VERSION "1.0.0"

ros::Publisher tb3_diagnostics_pub;
diagnostic_msgs::DiagnosticArray tb3_diagnostics;

diagnostic_msgs::DiagnosticStatus imu_state;
diagnostic_msgs::DiagnosticStatus motor_state;
diagnostic_msgs::DiagnosticStatus LDS_state;
diagnostic_msgs::DiagnosticStatus battery_state;
diagnostic_msgs::DiagnosticStatus button_state;

void setDiagnosisMsg(diagnostic_msgs::DiagnosticStatus *diag, uint8_t level, std::string name, std::string message, std::string hardware_id)
{
  diag->level = level;
  diag->name  = name;
  diag->message = message;
  diag->hardware_id = hardware_id;
}

void setIMUDiagnosis(uint8_t level, std::string message)
{
  setDiagnosisMsg(&imu_state, level, "IMU Sensor", message, "MPU9250");
}

void setMotorDiagnosis(uint8_t level, std::string message)
{
  setDiagnosisMsg(&motor_state, level, "Actuator", message, "DYNAMIXEL X");
}

void setBatteryDiagnosis(uint8_t level, std::string message)
{
  setDiagnosisMsg(&battery_state, level, "Power System", message, "Battery");
}

void setLDSDiagnosis(uint8_t level, std::string message)
{
  setDiagnosisMsg(&LDS_state, level, "Lidar Sensor", message, "HLS-LFCD-LDS");
}

void setButtonDiagnosis(uint8_t level, std::string message)
{
  setDiagnosisMsg(&button_state, level, "Analog Button", message, "OpenCR Button");
}

void imuMsgCallback(const sensor_msgs::Imu::ConstPtr &msg)
{
  setIMUDiagnosis(diagnostic_msgs::DiagnosticStatus::OK, "Good Condition");
}

void LDSMsgCallback(const sensor_msgs::LaserScan::ConstPtr &msg)
{
  setLDSDiagnosis(diagnostic_msgs::DiagnosticStatus::OK, "Good Condition");
}

void sensorStateMsgCallback(const turtlebot3_msgs::SensorState::ConstPtr &msg)
{
  if (msg->battery > 11.0)
    setBatteryDiagnosis(diagnostic_msgs::DiagnosticStatus::OK, "Good Condition");
  else
    setBatteryDiagnosis(diagnostic_msgs::DiagnosticStatus::WARN, "Charge!!! Charge!!!");

  if (msg->button == turtlebot3_msgs::SensorState::BUTTON0)
    setButtonDiagnosis(diagnostic_msgs::DiagnosticStatus::OK, "BUTTON 0 IS PUSHED");
  else if (msg->button == turtlebot3_msgs::SensorState::BUTTON1)
    setButtonDiagnosis(diagnostic_msgs::DiagnosticStatus::OK, "BUTTON 1 IS PUSHED");
  else
    setButtonDiagnosis(diagnostic_msgs::DiagnosticStatus::OK, "Pushed Nothing");

  if (msg->torque == true)
    setMotorDiagnosis(diagnostic_msgs::DiagnosticStatus::OK, "Torque ON");
  else
    setMotorDiagnosis(diagnostic_msgs::DiagnosticStatus::WARN, "Torque OFF");
}

void versionMsgCallback(const turtlebot3_msgs::VersionInfo::ConstPtr &msg)
{
  static bool check_version = false;

  if (check_version == false)
  {
    if (std::string(msg->software) != std::string(SOFTWARE_VERSION))
      ROS_WARN("Check turtlebot3 repository and Update your software!!");

    if (std::string(msg->hardware) != std::string(HARDWARE_VERSION))
      ROS_WARN("Check turtlebot3 wiki page and Update your hardware!!");

    if (std::string(msg->firmware) != std::string(FIRMWARE_VERSION))
      ROS_WARN("Check OpenCR update and change your firmware!!");

    check_version = true;
  }
}

void msgPub()
{
  tb3_diagnostics.header.stamp = ros::Time::now();

  tb3_diagnostics.status.clear();
  tb3_diagnostics.status.push_back(imu_state);
  tb3_diagnostics.status.push_back(motor_state);
  tb3_diagnostics.status.push_back(LDS_state);
  tb3_diagnostics.status.push_back(battery_state);
  tb3_diagnostics.status.push_back(button_state);

  tb3_diagnostics_pub.publish(tb3_diagnostics);
}

int main(int argc, char **argv)
{
  ros::init(argc, argv, "turtlebot3_diagnostic");
  ros::NodeHandle nh;

  tb3_diagnostics_pub  = nh.advertise<diagnostic_msgs::DiagnosticArray>("diagnostics", 10);

  ros::Subscriber imu         = nh.subscribe("imu", 10, imuMsgCallback);
  ros::Subscriber lds         = nh.subscribe("scan", 10, LDSMsgCallback);
  ros::Subscriber tb3_sensor  = nh.subscribe("sensor_state", 10, sensorStateMsgCallback);
  ros::Subscriber version     = nh.subscribe("version_info", 10, versionMsgCallback);

  ros::Rate loop_rate(1);

  while (ros::ok())
  {
    msgPub();
    ros::spinOnce();
    loop_rate.sleep();
  }

  return 0;
}
<commit_msg>update firmware version from 1.2.0 to 1.2.1<commit_after>/*******************************************************************************
* Copyright 2016 ROBOTIS CO., LTD.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/

/* Authors: Taehoon Lim (Darby) */

#include <ros/ros.h>
#include <sensor_msgs/BatteryState.h>
#include <sensor_msgs/Imu.h>
#include <sensor_msgs/MagneticField.h>
#include <sensor_msgs/LaserScan.h>
#include <diagnostic_msgs/DiagnosticArray.h>
#include <turtlebot3_msgs/SensorState.h>
#include <turtlebot3_msgs/VersionInfo.h>

#define SOFTWARE_VERSION "1.0.0"
#define FIRMWARE_VERSION "1.2.1"
#define HARDWARE_VERSION "1.0.0"

ros::Publisher tb3_diagnostics_pub;
diagnostic_msgs::DiagnosticArray tb3_diagnostics;

diagnostic_msgs::DiagnosticStatus imu_state;
diagnostic_msgs::DiagnosticStatus motor_state;
diagnostic_msgs::DiagnosticStatus LDS_state;
diagnostic_msgs::DiagnosticStatus battery_state;
diagnostic_msgs::DiagnosticStatus button_state;

void setDiagnosisMsg(diagnostic_msgs::DiagnosticStatus *diag, uint8_t level, std::string name, std::string message, std::string hardware_id)
{
  diag->level = level;
  diag->name  = name;
  diag->message = message;
  diag->hardware_id = hardware_id;
}

void setIMUDiagnosis(uint8_t level, std::string message)
{
  setDiagnosisMsg(&imu_state, level, "IMU Sensor", message, "MPU9250");
}

void setMotorDiagnosis(uint8_t level, std::string message)
{
  setDiagnosisMsg(&motor_state, level, "Actuator", message, "DYNAMIXEL X");
}

void setBatteryDiagnosis(uint8_t level, std::string message)
{
  setDiagnosisMsg(&battery_state, level, "Power System", message, "Battery");
}

void setLDSDiagnosis(uint8_t level, std::string message)
{
  setDiagnosisMsg(&LDS_state, level, "Lidar Sensor", message, "HLS-LFCD-LDS");
}

void setButtonDiagnosis(uint8_t level, std::string message)
{
  setDiagnosisMsg(&button_state, level, "Analog Button", message, "OpenCR Button");
}

void imuMsgCallback(const sensor_msgs::Imu::ConstPtr &msg)
{
  setIMUDiagnosis(diagnostic_msgs::DiagnosticStatus::OK, "Good Condition");
}

void LDSMsgCallback(const sensor_msgs::LaserScan::ConstPtr &msg)
{
  setLDSDiagnosis(diagnostic_msgs::DiagnosticStatus::OK, "Good Condition");
}

void sensorStateMsgCallback(const turtlebot3_msgs::SensorState::ConstPtr &msg)
{
  if (msg->battery > 11.0)
    setBatteryDiagnosis(diagnostic_msgs::DiagnosticStatus::OK, "Good Condition");
  else
    setBatteryDiagnosis(diagnostic_msgs::DiagnosticStatus::WARN, "Charge!!! Charge!!!");

  if (msg->button == turtlebot3_msgs::SensorState::BUTTON0)
    setButtonDiagnosis(diagnostic_msgs::DiagnosticStatus::OK, "BUTTON 0 IS PUSHED");
  else if (msg->button == turtlebot3_msgs::SensorState::BUTTON1)
    setButtonDiagnosis(diagnostic_msgs::DiagnosticStatus::OK, "BUTTON 1 IS PUSHED");
  else
    setButtonDiagnosis(diagnostic_msgs::DiagnosticStatus::OK, "Pushed Nothing");

  if (msg->torque == true)
    setMotorDiagnosis(diagnostic_msgs::DiagnosticStatus::OK, "Torque ON");
  else
    setMotorDiagnosis(diagnostic_msgs::DiagnosticStatus::WARN, "Torque OFF");
}

void versionMsgCallback(const turtlebot3_msgs::VersionInfo::ConstPtr &msg)
{
  static bool check_version = false;

  if (check_version == false)
  {
    if (std::string(msg->software) != std::string(SOFTWARE_VERSION))
      ROS_WARN("Check turtlebot3 repository and Update your software!!");

    if (std::string(msg->hardware) != std::string(HARDWARE_VERSION))
      ROS_WARN("Check turtlebot3 wiki page and Update your hardware!!");

    if (std::string(msg->firmware) != std::string(FIRMWARE_VERSION))
      ROS_WARN("Check OpenCR update and change your firmware!!");

    check_version = true;
  }
}

void msgPub()
{
  tb3_diagnostics.header.stamp = ros::Time::now();

  tb3_diagnostics.status.clear();
  tb3_diagnostics.status.push_back(imu_state);
  tb3_diagnostics.status.push_back(motor_state);
  tb3_diagnostics.status.push_back(LDS_state);
  tb3_diagnostics.status.push_back(battery_state);
  tb3_diagnostics.status.push_back(button_state);

  tb3_diagnostics_pub.publish(tb3_diagnostics);
}

int main(int argc, char **argv)
{
  ros::init(argc, argv, "turtlebot3_diagnostic");
  ros::NodeHandle nh;

  tb3_diagnostics_pub  = nh.advertise<diagnostic_msgs::DiagnosticArray>("diagnostics", 10);

  ros::Subscriber imu         = nh.subscribe("imu", 10, imuMsgCallback);
  ros::Subscriber lds         = nh.subscribe("scan", 10, LDSMsgCallback);
  ros::Subscriber tb3_sensor  = nh.subscribe("sensor_state", 10, sensorStateMsgCallback);
  ros::Subscriber version     = nh.subscribe("version_info", 10, versionMsgCallback);

  ros::Rate loop_rate(1);

  while (ros::ok())
  {
    msgPub();
    ros::spinOnce();
    loop_rate.sleep();
  }

  return 0;
}
<|endoftext|>


<commit_before>#include "extend.hpp"
#include "context.hpp"
#include "contextualize.hpp"
#include "to_string.hpp"
#include "backtrace.hpp"
#include <iostream>

namespace Sass {

  Extend::Extend(Context& ctx, multimap<Compound_Selector, Complex_Selector*>& extensions, Subset_Map<string, pair<Complex_Selector*, Compound_Selector*> >& ssm, Backtrace* bt)
  : ctx(ctx), extensions(extensions), subset_map(ssm), backtrace(bt)
  { }

  void Extend::operator()(Block* b)
  {
    for (size_t i = 0, L = b->length(); i < L; ++i) {
      (*b)[i]->perform(this);
    }
  }

  void Extend::operator()(Ruleset* r)
  {
    Selector_List* sg = static_cast<Selector_List*>(r->selector());
    Selector_List* ng = 0;
    bool extended = false;
    if (sg->has_placeholder()) {
      // To_String to_string;
      Compound_Selector* placeholder = new (ctx.mem) Compound_Selector(sg->path(), sg->line(), 1);
      *placeholder << sg->find_placeholder();
      // cerr << "placeholder: " << placeholder->perform(&to_string) << endl;
      // if the placeholder needs to be subbed
      if (extensions.count(*placeholder)) {
        // cerr << "need to sub " << placeholder->perform(&to_string) << " " << extensions.count(*placeholder) << " times" << endl;
        // perform each substitution and append it to the selector group of the ruleset
        ng = new (ctx.mem) Selector_List(sg->path(), sg->line(), extensions.count(*placeholder));
        for (multimap<Compound_Selector, Complex_Selector*>::iterator extender = extensions.lower_bound(*placeholder), E = extensions.upper_bound(*placeholder);
             extender != E;
             ++extender) {
          // cerr << "performing a substitution: " << placeholder->perform(&to_string) << " -> " << extender->second->perform(&to_string) << endl;
          Contextualize sub_plc(ctx, 0, 0, backtrace, placeholder, extender->second);
          Selector_List* subbed = static_cast<Selector_List*>(sg->perform(&sub_plc));
          // if (subbed) cerr << "subbed: " << subbed->perform(&to_string) << endl;
          *ng += subbed;
          extended = true;
        }
        ng->has_placeholder(false);
      }
      // otherwise prevent it from rendering
      else {
        // r->selector(0);
      }
    }
    else {
      To_String to_string;
      ng = new (ctx.mem) Selector_List(sg->path(), sg->line(), sg->length());
      // for each selector in the group
      for (size_t i = 0, L = sg->length(); i < L; ++i) {
        Complex_Selector* sel = (*sg)[i];
        *ng << sel;
        // if it's supposed to be extended
        Compound_Selector* sel_base = sel->base();
        if (sel_base && extensions.count(*sel_base)) {
          // extend it wrt each of its extenders
          for (multimap<Compound_Selector, Complex_Selector*>::iterator extender = extensions.lower_bound(*sel_base), E = extensions.upper_bound(*sel_base);
               extender != E;
               ++extender) {
            *ng += generate_extension(sel, extender->second);
            extended = true;
          }
        }
      }
    }
    if (extended) r->selector(ng);

    // let's try the new stuff here; eventually it should replace the preceding
    set<Compound_Selector> seen;
    Selector_List* new_list = new (ctx.mem) Selector_List(sg->path(), sg->line());
    for (size_t i = 0, L = sg->length(); i < L; ++i)
    {
      *new_list += extend_complex((*sg)[i], seen);
    }

    r->block()->perform(this);
  }

  void Extend::operator()(Media_Block* m)
  {
    m->block()->perform(this);
  }

  void Extend::operator()(At_Rule* a)
  {
    if (a->block()) a->block()->perform(this);
  }

  Selector_List* Extend::generate_extension(Complex_Selector* extendee, Complex_Selector* extender)
  {
    To_String to_string;
    Selector_List* new_group = new (ctx.mem) Selector_List(extendee->path(), extendee->line());
    Complex_Selector* extendee_context = extendee->context(ctx);
    Complex_Selector* extender_context = extender->context(ctx);
    if (extendee_context && extender_context) {
      Complex_Selector* base = new (ctx.mem) Complex_Selector(new_group->path(), new_group->line(), Complex_Selector::ANCESTOR_OF, extender->base(), 0);
      extendee_context->innermost()->tail(extender);
      *new_group << extendee_context;
      // make another one so we don't erroneously share tails
      extendee_context = extendee->context(ctx);
      extendee_context->innermost()->tail(base);
      extender_context->innermost()->tail(extendee_context);
      *new_group << extender_context;
    }
    else if (extendee_context) {
      extendee_context->innermost()->tail(extender);
      *new_group << extendee_context;
    }
    else if (extender_context) {
      *new_group << extender;
    }
    else {
      *new_group << extender;
    }
    return new_group;
  }

  Selector_List* Extend::extend_complex(Complex_Selector* sel, set<Compound_Selector>& seen)
  {
    To_String to_string;
    cerr << "EXTENDING COMPLEX: " << sel->perform(&to_string) << endl;
    Selector_List* choices = new (ctx.mem) Selector_List(sel->path(), sel->line());
    Selector_List* extended;

    Compound_Selector* h = sel->head();
    Complex_Selector* t = sel->tail();
    if (h && !h->is_empty_reference())
    {
      Selector_List* extended = extend_compound(h, seen);
      // TODO: check extended for something or other
      *choices += extended;
    }
    while(t)
    {
      h = t->head();
      t = t->tail();
      if (h && !h->is_empty_reference())
      {
        Selector_List* extended = extend_compound(h, seen);
        // TODO: check extended for something or other
        *choices += extended;
      }
    }

    return choices;
  }

  Selector_List* Extend::extend_compound(Compound_Selector* sel, set<Compound_Selector>& seen)
  {
    To_String to_string;
    cerr << "EXTENDING COMPOUND: " << sel->perform(&to_string) << endl;
    return new (ctx.mem) Selector_List(sel->path(), sel->line());
  }

}<commit_msg>tweaking the log output<commit_after>#include "extend.hpp"
#include "context.hpp"
#include "contextualize.hpp"
#include "to_string.hpp"
#include "backtrace.hpp"
#include <iostream>

namespace Sass {

  Extend::Extend(Context& ctx, multimap<Compound_Selector, Complex_Selector*>& extensions, Subset_Map<string, pair<Complex_Selector*, Compound_Selector*> >& ssm, Backtrace* bt)
  : ctx(ctx), extensions(extensions), subset_map(ssm), backtrace(bt)
  { }

  void Extend::operator()(Block* b)
  {
    for (size_t i = 0, L = b->length(); i < L; ++i) {
      (*b)[i]->perform(this);
    }
  }

  void Extend::operator()(Ruleset* r)
  {
    Selector_List* sg = static_cast<Selector_List*>(r->selector());
    Selector_List* ng = 0;
    bool extended = false;
    if (sg->has_placeholder()) {
      // To_String to_string;
      Compound_Selector* placeholder = new (ctx.mem) Compound_Selector(sg->path(), sg->line(), 1);
      *placeholder << sg->find_placeholder();
      // cerr << "placeholder: " << placeholder->perform(&to_string) << endl;
      // if the placeholder needs to be subbed
      if (extensions.count(*placeholder)) {
        // cerr << "need to sub " << placeholder->perform(&to_string) << " " << extensions.count(*placeholder) << " times" << endl;
        // perform each substitution and append it to the selector group of the ruleset
        ng = new (ctx.mem) Selector_List(sg->path(), sg->line(), extensions.count(*placeholder));
        for (multimap<Compound_Selector, Complex_Selector*>::iterator extender = extensions.lower_bound(*placeholder), E = extensions.upper_bound(*placeholder);
             extender != E;
             ++extender) {
          // cerr << "performing a substitution: " << placeholder->perform(&to_string) << " -> " << extender->second->perform(&to_string) << endl;
          Contextualize sub_plc(ctx, 0, 0, backtrace, placeholder, extender->second);
          Selector_List* subbed = static_cast<Selector_List*>(sg->perform(&sub_plc));
          // if (subbed) cerr << "subbed: " << subbed->perform(&to_string) << endl;
          *ng += subbed;
          extended = true;
        }
        ng->has_placeholder(false);
      }
      // otherwise prevent it from rendering
      else {
        // r->selector(0);
      }
    }
    else {
      To_String to_string;
      ng = new (ctx.mem) Selector_List(sg->path(), sg->line(), sg->length());
      // for each selector in the group
      for (size_t i = 0, L = sg->length(); i < L; ++i) {
        Complex_Selector* sel = (*sg)[i];
        *ng << sel;
        // if it's supposed to be extended
        Compound_Selector* sel_base = sel->base();
        if (sel_base && extensions.count(*sel_base)) {
          // extend it wrt each of its extenders
          for (multimap<Compound_Selector, Complex_Selector*>::iterator extender = extensions.lower_bound(*sel_base), E = extensions.upper_bound(*sel_base);
               extender != E;
               ++extender) {
            *ng += generate_extension(sel, extender->second);
            extended = true;
          }
        }
      }
    }
    if (extended) r->selector(ng);

    // let's try the new stuff here; eventually it should replace the preceding
    set<Compound_Selector> seen;
    Selector_List* new_list = new (ctx.mem) Selector_List(sg->path(), sg->line());
    for (size_t i = 0, L = sg->length(); i < L; ++i)
    {
      *new_list += extend_complex((*sg)[i], seen);
    }

    r->block()->perform(this);
  }

  void Extend::operator()(Media_Block* m)
  {
    m->block()->perform(this);
  }

  void Extend::operator()(At_Rule* a)
  {
    if (a->block()) a->block()->perform(this);
  }

  Selector_List* Extend::generate_extension(Complex_Selector* extendee, Complex_Selector* extender)
  {
    To_String to_string;
    Selector_List* new_group = new (ctx.mem) Selector_List(extendee->path(), extendee->line());
    Complex_Selector* extendee_context = extendee->context(ctx);
    Complex_Selector* extender_context = extender->context(ctx);
    if (extendee_context && extender_context) {
      Complex_Selector* base = new (ctx.mem) Complex_Selector(new_group->path(), new_group->line(), Complex_Selector::ANCESTOR_OF, extender->base(), 0);
      extendee_context->innermost()->tail(extender);
      *new_group << extendee_context;
      // make another one so we don't erroneously share tails
      extendee_context = extendee->context(ctx);
      extendee_context->innermost()->tail(base);
      extender_context->innermost()->tail(extendee_context);
      *new_group << extender_context;
    }
    else if (extendee_context) {
      extendee_context->innermost()->tail(extender);
      *new_group << extendee_context;
    }
    else if (extender_context) {
      *new_group << extender;
    }
    else {
      *new_group << extender;
    }
    return new_group;
  }

  Selector_List* Extend::extend_complex(Complex_Selector* sel, set<Compound_Selector>& seen)
  {
    To_String to_string;
    cerr << "EXTENDING COMPLEX: " << sel->perform(&to_string) << endl;
    Selector_List* choices = new (ctx.mem) Selector_List(sel->path(), sel->line());

    Compound_Selector* h = sel->head();
    Complex_Selector* t = sel->tail();
    if (h && !h->is_empty_reference())
    {
      Selector_List* extended = extend_compound(h, seen);
      // TODO: check extended for something or other
      *choices += extended;
    }
    while(t)
    {
      h = t->head();
      t = t->tail();
      if (h && !h->is_empty_reference())
      {
        Selector_List* extended = extend_compound(h, seen);
        // TODO: check extended for something or other
        *choices += extended;
      }
    }

    return choices;
  }

  Selector_List* Extend::extend_compound(Compound_Selector* sel, set<Compound_Selector>& seen)
  {
    To_String to_string;
    Selector_List* results = new (ctx.mem) Selector_List(sel->path(), sel->line());

    vector<pair<Complex_Selector*, Compound_Selector*> > entries = subset_map.get_v(sel->to_str_vec());

    for (size_t i = 0, S = entries.size(); i < S; ++i)
    {
      cerr << "COMPOUND: " << sel->perform(&to_string) << " KEYS TO " << entries[i].first->perform(&to_string) << " AND " << entries[i].second->perform(&to_string) << endl;
    }

    return results;
  }

}<|endoftext|>


<commit_before>#include <limits>
#include <iostream>
#include <iomanip>
#include <unistd.h>
#include <algorithm>

#include "libpstack/util.h"
#include "libpstack/elf.h"
#ifdef WITH_ZLIB
#include "libpstack/inflatereader.h"
#endif
#ifdef WITH_LZMA
#include "libpstack/lzmareader.h"
#endif

using std::string;
using std::make_shared;
using std::shared_ptr;

std::ostream *debug = &std::clog;
int verbose = 0;
static uint32_t elf_hash(string);
bool noDebugLibs;

GlobalDebugDirectories globalDebugDirectories;
GlobalDebugDirectories::GlobalDebugDirectories()
{
   add("/usr/lib/debug");
   add("/usr/lib/debug/usr"); // Add as a hack for when linker loads from /lib, but package has /usr/lib
}

void
GlobalDebugDirectories::add(const std::string &str)
{
   dirs.push_back(str);
}

ElfNoteIter
ElfNotes::begin() const
{
   return ElfNoteIter(object, true);
}

ElfNoteIter
ElfNotes::end() const
{
   return ElfNoteIter(object, false);
}

std::string
ElfNoteDesc::name() const
{
   return io->readString(sizeof note);
}

std::shared_ptr<const Reader>
ElfNoteDesc::data() const
{
   return std::make_shared<OffsetReader>(io, sizeof note + roundup2(note.n_namesz, 4), note.n_descsz);
}

size_t
ElfNoteDesc::size() const
{
   return note.n_descsz;
}

ElfObject::ElfObject(ImageCache &cache, shared_ptr<const Reader> io_)
   : notes(this)
   , imageCache(cache)
{
    debugLoaded = false;
    io = io_;
    int i;
    size_t off;
    io->readObj(0, &elfHeader);

    /* Validate the ELF header */
    if (!IS_ELF(elfHeader) || elfHeader.e_ident[EI_VERSION] != EV_CURRENT)
        throw Exception() << *io << ": content is not an ELF image";

    for (off = elfHeader.e_phoff, i = 0; i < elfHeader.e_phnum; i++) {
        Elf_Phdr hdr;
        io->readObj(off, &hdr);
        programHeaders[hdr.p_type].push_back(hdr);
        off += elfHeader.e_phentsize;
    }

    sectionHeaders.resize(elfHeader.e_shnum);
    for (off = elfHeader.e_shoff, i = 0; i < elfHeader.e_shnum; i++) {
        sectionHeaders[i].open(io, off);
        off += elfHeader.e_shentsize;
    }

    if (elfHeader.e_shstrndx != SHN_UNDEF) {
        auto &sshdr = sectionHeaders[elfHeader.e_shstrndx];
        for (auto &h : sectionHeaders) {
            auto name = sshdr.io->readString(h.shdr.sh_name);
            namedSection[name] = &h;
            // .gnu_debugdata is a separate LZMA-compressed ELF image with just
            // a symbol table.
            if (name == ".gnu_debugdata")
#ifdef WITH_LZMA
                debugData = std::make_shared<ElfObject>(imageCache,
                      std::make_shared<const LzmaReader>(h.io));
#else
                std::clog << "warning: no compiled support for LZMA - "
                      "can't decode debug data in " << *io << "\n";
#endif
        }
        auto &tab = getSection(".hash", SHT_HASH);
        auto &syms = getSection(tab.shdr.sh_link);
        auto &strings = getSection(syms.shdr.sh_link);
        if (tab && syms && strings)
            hash.reset(new ElfSymHash(tab.io, syms.io, strings.io));
    } else {
        hash = 0;
    }
}

const Elf_Phdr *
ElfObject::getSegmentForAddress(Elf_Off a) const
{
    for (const auto &hdr : getSegments(PT_LOAD))
        if (hdr.p_vaddr <= a && hdr.p_vaddr + hdr.p_memsz > a)
            return &hdr;
    return 0;
}

const ElfObject::ProgramHeaders &
ElfObject::getSegments(Elf_Word type) const
{
    auto it = programHeaders.find(type);
    if (it == programHeaders.end()) {
        static const ProgramHeaders empty;
        return empty;
    }
    return it->second;
}

Elf_Addr
ElfObject::getBase() const
{
    auto base = std::numeric_limits<Elf_Off>::max();
    auto &segments = getSegments(PT_LOAD);
    for (auto &seg : segments)
        if (Elf_Off(seg.p_vaddr) <= base)
            base = Elf_Off(seg.p_vaddr);
    return base;
}

std::string
ElfObject::getInterpreter() const
{
    for (auto &seg : getSegments(PT_INTERP))
        return io->readString(seg.p_offset);
    return "";
}

std::pair<const Elf_Sym, const string>
SymbolIterator::operator *()
{
    Elf_Sym sym;
    sec->symbols->readObj(off, &sym);
    string name = sec->strings->readString(sym.st_name);
    return std::make_pair(sym, name);
}

/*
 * Find the symbol that represents a particular address.
 */
bool
ElfObject::findSymbolByAddress(Elf_Addr addr, int type, Elf_Sym &sym, string &name)
{
    /* Try to find symbols in these sections */
    for (auto secname : { ".symtab", ".dynsym" }) {
        const auto &symSection = getSection(secname, SHT_NULL);
        if (symSection.shdr.sh_type == SHT_NOBITS)
            continue;
        SymbolSection syms(symSection.io, getSection(symSection.shdr.sh_link).io);
        for (auto syminfo : syms) {
            auto &candidate = syminfo.first;
            if (candidate.st_shndx >= sectionHeaders.size())
                continue;
            if (type != STT_NOTYPE && ELF_ST_TYPE(candidate.st_info) != type)
                continue;
            if (candidate.st_value > addr)
                continue;
            if (candidate.st_size + candidate.st_value <= addr)
                continue;
            auto &sec = sectionHeaders[candidate.st_shndx];
            if (!(sec.shdr.sh_flags & SHF_ALLOC))
                continue;
            sym = candidate;
            name = syminfo.second;
            return true;
        }
    }
    if (debugData)
        return debugData->findSymbolByAddress(addr, type, sym, name);
    return false;;
}

const ElfSection &
ElfObject::getSection(const std::string &name, Elf_Word type) const
{
    auto s = namedSection.find(name);
    if (s == namedSection.end() || (s->second->shdr.sh_type != type && type != SHT_NULL))
        return sectionHeaders[0];
    return *s->second;
}

const ElfSection &
ElfObject::getSection(Elf_Word idx) const
{
    return sectionHeaders[idx];
}

SymbolSection
ElfObject::getSymbols(const std::string &tableName)
{
    ElfObject *elf = debugData ? debugData.get() : this;
    auto &table = elf->getSection(tableName, SHT_NULL);
    auto &strings = elf->getSection(table.shdr.sh_link);
    return SymbolSection(table.io, strings.io);
}

bool
SymbolSection::linearSearch(const string &name, Elf_Sym &sym)
{
    for (const auto &info : *this) {
        if (name == info.second) {
            sym = info.first;
            return true;
        }
    }
    return false;
}

ElfSymHash::ElfSymHash(std::shared_ptr<const Reader> hash_,
      std::shared_ptr<const Reader> syms_, std::shared_ptr<const Reader> strings_)
    : hash(hash_)
    , syms(syms_)
    , strings(strings_)
{
    // read the hash table into local memory.
    size_t words = hash->size() / sizeof (Elf_Word);
    data.resize(words);
    hash->readObj(0, &data[0], words);
    nbucket = data[0];
    nchain = data[1];
    buckets = &data[0] + 2;
    chains = buckets + nbucket;
}

bool
ElfSymHash::findSymbol(Elf_Sym &sym, const string &name)
{
    uint32_t bucket = elf_hash(name) % nbucket;
    for (Elf_Word i = buckets[bucket]; i != STN_UNDEF; i = chains[i]) {
        Elf_Sym candidate;
        syms->readObj(i * sizeof candidate, &candidate);
        string candidateName = strings->readString(candidate.st_name);
        if (candidateName == name) {
            sym = candidate;
            return true;
        }
    }
    return false;
}

/*
 * Locate a named symbol in an ELF image.
 */
bool
ElfObject::findSymbolByName(const string &name, Elf_Sym &sym)
{
    if (debugData)
        return debugData->findSymbolByName(name, sym);

    if (hash && hash->findSymbol(sym, name))
        return true;

    for (const char *sec : { ".dynsym", ".symtab" }) {
        SymbolSection sect = getSymbols(sec);
        if (sect.linearSearch(name, sym))
            return true;
    }
    return false;
}

ElfObject::~ElfObject()
{
}

std::shared_ptr<ElfObject>
ElfObject::getDebug(std::shared_ptr<ElfObject> &in)
{
    if (noDebugLibs)
        return in;

    if (!in->debugLoaded) {
        in->debugLoaded = true;
        auto &hdr = in->getSection(".gnu_debuglink", SHT_PROGBITS);
        auto link = hdr.io->readString(0);
        auto dir = dirname(stringify(*in->io));
        in->debugObject = in->imageCache.getDebugImage(dir + "/" + link);

        if (!in->debugObject) {
            for (auto note : in->notes) {
                if (note.name() == "GNU" && note.type() == GNU_BUILD_ID) {
                    std::ostringstream dir;
                    dir << ".build-id/";
                    size_t i;
                    auto io = note.data();
                    std::vector<unsigned char> data(io->size());
                    io->readObj(0, &data[0], io->size());
                    dir << std::hex << std::setw(2) << std::setfill('0') << int(data[0]);
                    dir << "/";
                    for (i = 1; i < note.size(); ++i)
                        dir << std::setw(2) << int(data[i]);
                    dir << ".debug";
                    in->debugObject = in->imageCache.getDebugImage(dir.str());
                    break;
                }
            }
        }
        if (in->debugObject && verbose >= 2)
            *debug << "found debug object " << *in->debugObject->io << " for " << *in->io << "\n";
    }
    return in->debugObject ? in->debugObject : in;
}

/*
 * Culled from System V Application Binary Interface
 */
static uint32_t
elf_hash(string name)
{
    uint32_t h = 0, g;
    for (auto c : name) {
        h = (h << 4) + c;
        if ((g = h & 0xf0000000) != 0)
            h ^= g >> 24;
        h &= ~g;
    }
    return (h);
}

void
ElfSection::open(const std::shared_ptr<const Reader> &image, off_t off)
{
    image->readObj(off, &shdr);
    auto rawIo = std::make_shared<OffsetReader>(image, shdr.sh_offset, shdr.sh_size);
    if ((shdr.sh_flags & SHF_COMPRESSED) == 0) {
        io = rawIo;
    } else {

#ifdef WITH_ZLIB
        Elf_Chdr chdr;
        rawIo->readObj(0, &chdr);
        io = std::make_shared<InflateReader>(chdr.ch_size,
              std::make_shared<OffsetReader>(rawIo,
                 sizeof chdr, shdr.sh_size - sizeof chdr));
#else
        static bool warned = false;
        if (!warned) {
            warned = true;
            std::clog <<"warning: no support configured for compressed debug info in " << *image << std::endl;
        }
        io = std::make_shared<NullReader>();
#endif
    }
}

std::shared_ptr<ElfObject>
ImageCache::getImageForName(const std::string &name) {
    bool found;
    auto res = getImageIfLoaded(name, found);
    if (found)
        return res;
    auto &item = cache[name];
    item = std::make_shared<ElfObject>(*this, loadFile(name));
    return item;
}

ImageCache::ImageCache() : elfHits(0), elfLookups(0) {}
ImageCache::~ImageCache() {
    if (verbose >= 2) {
        *debug << "ELF image cache: lookups: " << elfLookups << ", hits=" << elfHits << std::endl;
        for (auto &items : cache) {
            if (items.second)
                *debug << "\t" << *items.second->io << std::endl;
            else
                *debug << "\t" << "NEGATIVE: " << items.first << std::endl;
        }
    }
}

std::shared_ptr<ElfObject>
ImageCache::getImageIfLoaded(const std::string &name, bool &found)
{
    elfLookups++;
    auto it = cache.find(name);
    if (it != cache.end()) {
        elfHits++;
        found = true;
        return it->second;
    }
    found = false;
    return std::shared_ptr<ElfObject>();
}

std::shared_ptr<ElfObject>
ImageCache::getDebugImage(const std::string &name) {
    // XXX: verify checksum.
    for (auto dir : globalDebugDirectories.dirs) {
        bool found;
        auto img = getImageIfLoaded(dir + "/" + name, found);
        if (found)
            return img;
    }
    for (auto dir : globalDebugDirectories.dirs) {
        try {
           return getImageForName(dir + "/" + name);
        }
        catch (const std::exception &ex) {
            continue;
        }
    }
    return std::shared_ptr<ElfObject>();
}
<commit_msg>only warn about lzma being missing once.<commit_after>#include <limits>
#include <iostream>
#include <iomanip>
#include <unistd.h>
#include <algorithm>

#include "libpstack/util.h"
#include "libpstack/elf.h"
#ifdef WITH_ZLIB
#include "libpstack/inflatereader.h"
#endif
#ifdef WITH_LZMA
#include "libpstack/lzmareader.h"
#endif

using std::string;
using std::make_shared;
using std::shared_ptr;

std::ostream *debug = &std::clog;
int verbose = 0;
static uint32_t elf_hash(string);
bool noDebugLibs;

GlobalDebugDirectories globalDebugDirectories;
GlobalDebugDirectories::GlobalDebugDirectories()
{
   add("/usr/lib/debug");
   add("/usr/lib/debug/usr"); // Add as a hack for when linker loads from /lib, but package has /usr/lib
}

void
GlobalDebugDirectories::add(const std::string &str)
{
   dirs.push_back(str);
}

ElfNoteIter
ElfNotes::begin() const
{
   return ElfNoteIter(object, true);
}

ElfNoteIter
ElfNotes::end() const
{
   return ElfNoteIter(object, false);
}

std::string
ElfNoteDesc::name() const
{
   return io->readString(sizeof note);
}

std::shared_ptr<const Reader>
ElfNoteDesc::data() const
{
   return std::make_shared<OffsetReader>(io, sizeof note + roundup2(note.n_namesz, 4), note.n_descsz);
}

size_t
ElfNoteDesc::size() const
{
   return note.n_descsz;
}

ElfObject::ElfObject(ImageCache &cache, shared_ptr<const Reader> io_)
   : notes(this)
   , imageCache(cache)
{
    debugLoaded = false;
    io = io_;
    int i;
    size_t off;
    io->readObj(0, &elfHeader);

    /* Validate the ELF header */
    if (!IS_ELF(elfHeader) || elfHeader.e_ident[EI_VERSION] != EV_CURRENT)
        throw Exception() << *io << ": content is not an ELF image";

    for (off = elfHeader.e_phoff, i = 0; i < elfHeader.e_phnum; i++) {
        Elf_Phdr hdr;
        io->readObj(off, &hdr);
        programHeaders[hdr.p_type].push_back(hdr);
        off += elfHeader.e_phentsize;
    }

    sectionHeaders.resize(elfHeader.e_shnum);
    for (off = elfHeader.e_shoff, i = 0; i < elfHeader.e_shnum; i++) {
        sectionHeaders[i].open(io, off);
        off += elfHeader.e_shentsize;
    }

    if (elfHeader.e_shstrndx != SHN_UNDEF) {
        auto &sshdr = sectionHeaders[elfHeader.e_shstrndx];
        for (auto &h : sectionHeaders) {
            auto name = sshdr.io->readString(h.shdr.sh_name);
            namedSection[name] = &h;
            // .gnu_debugdata is a separate LZMA-compressed ELF image with just
            // a symbol table.
            if (name == ".gnu_debugdata") {
#ifdef WITH_LZMA
                debugData = std::make_shared<ElfObject>(imageCache,
                      std::make_shared<const LzmaReader>(h.io));
#else
                static bool warned = false;
                if (!warned) {
                    std::clog << "warning: no compiled support for LZMA - "
                          "can't decode debug data in " << *io << "\n";
                    warned = true;
                }
#endif
            }
        }
        auto &tab = getSection(".hash", SHT_HASH);
        auto &syms = getSection(tab.shdr.sh_link);
        auto &strings = getSection(syms.shdr.sh_link);
        if (tab && syms && strings)
            hash.reset(new ElfSymHash(tab.io, syms.io, strings.io));
    } else {
        hash = 0;
    }
}

const Elf_Phdr *
ElfObject::getSegmentForAddress(Elf_Off a) const
{
    for (const auto &hdr : getSegments(PT_LOAD))
        if (hdr.p_vaddr <= a && hdr.p_vaddr + hdr.p_memsz > a)
            return &hdr;
    return 0;
}

const ElfObject::ProgramHeaders &
ElfObject::getSegments(Elf_Word type) const
{
    auto it = programHeaders.find(type);
    if (it == programHeaders.end()) {
        static const ProgramHeaders empty;
        return empty;
    }
    return it->second;
}

Elf_Addr
ElfObject::getBase() const
{
    auto base = std::numeric_limits<Elf_Off>::max();
    auto &segments = getSegments(PT_LOAD);
    for (auto &seg : segments)
        if (Elf_Off(seg.p_vaddr) <= base)
            base = Elf_Off(seg.p_vaddr);
    return base;
}

std::string
ElfObject::getInterpreter() const
{
    for (auto &seg : getSegments(PT_INTERP))
        return io->readString(seg.p_offset);
    return "";
}

std::pair<const Elf_Sym, const string>
SymbolIterator::operator *()
{
    Elf_Sym sym;
    sec->symbols->readObj(off, &sym);
    string name = sec->strings->readString(sym.st_name);
    return std::make_pair(sym, name);
}

/*
 * Find the symbol that represents a particular address.
 */
bool
ElfObject::findSymbolByAddress(Elf_Addr addr, int type, Elf_Sym &sym, string &name)
{
    /* Try to find symbols in these sections */
    for (auto secname : { ".symtab", ".dynsym" }) {
        const auto &symSection = getSection(secname, SHT_NULL);
        if (symSection.shdr.sh_type == SHT_NOBITS)
            continue;
        SymbolSection syms(symSection.io, getSection(symSection.shdr.sh_link).io);
        for (auto syminfo : syms) {
            auto &candidate = syminfo.first;
            if (candidate.st_shndx >= sectionHeaders.size())
                continue;
            if (type != STT_NOTYPE && ELF_ST_TYPE(candidate.st_info) != type)
                continue;
            if (candidate.st_value > addr)
                continue;
            if (candidate.st_size + candidate.st_value <= addr)
                continue;
            auto &sec = sectionHeaders[candidate.st_shndx];
            if (!(sec.shdr.sh_flags & SHF_ALLOC))
                continue;
            sym = candidate;
            name = syminfo.second;
            return true;
        }
    }
    if (debugData)
        return debugData->findSymbolByAddress(addr, type, sym, name);
    return false;;
}

const ElfSection &
ElfObject::getSection(const std::string &name, Elf_Word type) const
{
    auto s = namedSection.find(name);
    if (s == namedSection.end() || (s->second->shdr.sh_type != type && type != SHT_NULL))
        return sectionHeaders[0];
    return *s->second;
}

const ElfSection &
ElfObject::getSection(Elf_Word idx) const
{
    return sectionHeaders[idx];
}

SymbolSection
ElfObject::getSymbols(const std::string &tableName)
{
    ElfObject *elf = debugData ? debugData.get() : this;
    auto &table = elf->getSection(tableName, SHT_NULL);
    auto &strings = elf->getSection(table.shdr.sh_link);
    return SymbolSection(table.io, strings.io);
}

bool
SymbolSection::linearSearch(const string &name, Elf_Sym &sym)
{
    for (const auto &info : *this) {
        if (name == info.second) {
            sym = info.first;
            return true;
        }
    }
    return false;
}

ElfSymHash::ElfSymHash(std::shared_ptr<const Reader> hash_,
      std::shared_ptr<const Reader> syms_, std::shared_ptr<const Reader> strings_)
    : hash(hash_)
    , syms(syms_)
    , strings(strings_)
{
    // read the hash table into local memory.
    size_t words = hash->size() / sizeof (Elf_Word);
    data.resize(words);
    hash->readObj(0, &data[0], words);
    nbucket = data[0];
    nchain = data[1];
    buckets = &data[0] + 2;
    chains = buckets + nbucket;
}

bool
ElfSymHash::findSymbol(Elf_Sym &sym, const string &name)
{
    uint32_t bucket = elf_hash(name) % nbucket;
    for (Elf_Word i = buckets[bucket]; i != STN_UNDEF; i = chains[i]) {
        Elf_Sym candidate;
        syms->readObj(i * sizeof candidate, &candidate);
        string candidateName = strings->readString(candidate.st_name);
        if (candidateName == name) {
            sym = candidate;
            return true;
        }
    }
    return false;
}

/*
 * Locate a named symbol in an ELF image.
 */
bool
ElfObject::findSymbolByName(const string &name, Elf_Sym &sym)
{
    if (debugData)
        return debugData->findSymbolByName(name, sym);

    if (hash && hash->findSymbol(sym, name))
        return true;

    for (const char *sec : { ".dynsym", ".symtab" }) {
        SymbolSection sect = getSymbols(sec);
        if (sect.linearSearch(name, sym))
            return true;
    }
    return false;
}

ElfObject::~ElfObject()
{
}

std::shared_ptr<ElfObject>
ElfObject::getDebug(std::shared_ptr<ElfObject> &in)
{
    if (noDebugLibs)
        return in;

    if (!in->debugLoaded) {
        in->debugLoaded = true;
        auto &hdr = in->getSection(".gnu_debuglink", SHT_PROGBITS);
        auto link = hdr.io->readString(0);
        auto dir = dirname(stringify(*in->io));
        in->debugObject = in->imageCache.getDebugImage(dir + "/" + link);

        if (!in->debugObject) {
            for (auto note : in->notes) {
                if (note.name() == "GNU" && note.type() == GNU_BUILD_ID) {
                    std::ostringstream dir;
                    dir << ".build-id/";
                    size_t i;
                    auto io = note.data();
                    std::vector<unsigned char> data(io->size());
                    io->readObj(0, &data[0], io->size());
                    dir << std::hex << std::setw(2) << std::setfill('0') << int(data[0]);
                    dir << "/";
                    for (i = 1; i < note.size(); ++i)
                        dir << std::setw(2) << int(data[i]);
                    dir << ".debug";
                    in->debugObject = in->imageCache.getDebugImage(dir.str());
                    break;
                }
            }
        }
        if (in->debugObject && verbose >= 2)
            *debug << "found debug object " << *in->debugObject->io << " for " << *in->io << "\n";
    }
    return in->debugObject ? in->debugObject : in;
}

/*
 * Culled from System V Application Binary Interface
 */
static uint32_t
elf_hash(string name)
{
    uint32_t h = 0, g;
    for (auto c : name) {
        h = (h << 4) + c;
        if ((g = h & 0xf0000000) != 0)
            h ^= g >> 24;
        h &= ~g;
    }
    return (h);
}

void
ElfSection::open(const std::shared_ptr<const Reader> &image, off_t off)
{
    image->readObj(off, &shdr);
    auto rawIo = std::make_shared<OffsetReader>(image, shdr.sh_offset, shdr.sh_size);
    if ((shdr.sh_flags & SHF_COMPRESSED) == 0) {
        io = rawIo;
    } else {

#ifdef WITH_ZLIB
        Elf_Chdr chdr;
        rawIo->readObj(0, &chdr);
        io = std::make_shared<InflateReader>(chdr.ch_size,
              std::make_shared<OffsetReader>(rawIo,
                 sizeof chdr, shdr.sh_size - sizeof chdr));
#else
        static bool warned = false;
        if (!warned) {
            warned = true;
            std::clog <<"warning: no support configured for compressed debug info in " << *image << std::endl;
        }
        io = std::make_shared<NullReader>();
#endif
    }
}

std::shared_ptr<ElfObject>
ImageCache::getImageForName(const std::string &name) {
    bool found;
    auto res = getImageIfLoaded(name, found);
    if (found)
        return res;
    auto &item = cache[name];
    item = std::make_shared<ElfObject>(*this, loadFile(name));
    return item;
}

ImageCache::ImageCache() : elfHits(0), elfLookups(0) {}
ImageCache::~ImageCache() {
    if (verbose >= 2) {
        *debug << "ELF image cache: lookups: " << elfLookups << ", hits=" << elfHits << std::endl;
        for (auto &items : cache) {
            if (items.second)
                *debug << "\t" << *items.second->io << std::endl;
            else
                *debug << "\t" << "NEGATIVE: " << items.first << std::endl;
        }
    }
}

std::shared_ptr<ElfObject>
ImageCache::getImageIfLoaded(const std::string &name, bool &found)
{
    elfLookups++;
    auto it = cache.find(name);
    if (it != cache.end()) {
        elfHits++;
        found = true;
        return it->second;
    }
    found = false;
    return std::shared_ptr<ElfObject>();
}

std::shared_ptr<ElfObject>
ImageCache::getDebugImage(const std::string &name) {
    // XXX: verify checksum.
    for (auto dir : globalDebugDirectories.dirs) {
        bool found;
        auto img = getImageIfLoaded(dir + "/" + name, found);
        if (found)
            return img;
    }
    for (auto dir : globalDebugDirectories.dirs) {
        try {
           return getImageForName(dir + "/" + name);
        }
        catch (const std::exception &ex) {
            continue;
        }
    }
    return std::shared_ptr<ElfObject>();
}
<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
#include <stack>
#include <cmath>
#include <time.h>
using namespace std;

void goToSemi(ifstream &myfile, int &i){             // stream goes to the next semicolon with it already extracted
    int where=myfile.get();
    while(where!=59){
        if(where==10){
            i++;
        }
        where=myfile.get();
    }
}

void goToNextLine(ifstream &myfile){                //stream goes to the next line
            int where = myfile.get();
             while(where!=10){                    // until the next line     10= int '\n'
                where=myfile.get();
            }
}

int goToLoc(ifstream &myfile){                          // stream goes to the next number with it already extracted and gives back its value
                                                        // if there is no number before the next semicolon it returns 0 (and semicolon is extractet)
    int where = myfile.get();
    int where2=where;
    while ((where<48 || where > 57)&&where!=59){

        where = myfile.get();
    }
    if(where==59){
        where=48;
    }
    return where-48;
}

void addNumber(ifstream &myfile, long double &w, long int n, int &i){                        //extracts the next number and calculates the actual geometric mean given the location

    long double v=0.0;
    int where = myfile.get();
    int count = 0;
    int point =0;
    stack<int> number;

    while (where<48 || where > 57){
        if(where==10){
            i++;
        }
        where = myfile.get();
    }

    while ((where>=48&&where<=57)||where==46){

        if(where==46){
            point=count;
        }
        else{
            count++;
            number.push(where-48);
        }
        where=myfile.get();
    }
        myfile.unget();

    for(int j=count; j>0; j--){
        v=v+(number.top())*(pow(10,(point-j)));
        number.pop();
    }
    if(w==0){
        w=v;
    }
    else{
        long double exponent=(1+(log(1/w))/(log(v)))/(n+1);
        w=w*pow(v,exponent);
    }

}

void mean(long double * means){

    ifstream myfile;
    myfile.open("ex1.dat");

    //cout<<myfile.eof()<<endl;
    int i=1;
    int where=myfile.get();
    int loc;
    long int loc1=0;
    long int loc2=0;

    while(where!=-1){

        if(where==35){                  // ignore lines with #

            where=myfile.get();

            while(where!=10 && where!=-1){       // goes to next line
               where=myfile.get();
            }
            i++;
            where=myfile.get();
        }

        else{

            goToSemi(myfile,i);
            loc=goToLoc(myfile);
            goToSemi(myfile,i);

            if(loc==1){
                addNumber(myfile,means[0],loc1, i);
                loc1++;
            }
            else if(loc==2){
                addNumber(myfile,means[1],loc2, i);
                loc2++;
            }

            else{

            }

            goToNextLine(myfile);
            where=myfile.peek();
            i++;

        }

    }

    cout<< "File: ex1.dat with :"<< i << "  lines"<< endl;
    cout<<" Valid Values Loc1:"<< loc1<< "  With GeoMean: "<<means[0]<<endl;
    cout<<" Valid Values Loc1:"<< loc2<< "  With GeoMean: "<<means[1]<<endl;

}

int main(){
    clock_t tStart = clock();
    ifstream myfile;
    myfile.open("ex1.dat");

    long double means[2]={0.0,0.0};
    mean(means);

    cout<<"Time needed: ";
    cout<<(double)(clock()-tStart)/CLOCKS_PER_SEC<<" seconds"<<endl;
}<commit_msg>Delete ex1.cc<commit_after><|endoftext|>


<commit_before>/* Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */

#include "paddle/fluid/operators/cvm_op.h"
#include <memory>
#include "paddle/fluid/operators/math/math_function.h"

namespace paddle {
namespace operators {

using Tensor = framework::Tensor;

class CVMOp : public framework::OperatorWithKernel {
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

  void InferShape(framework::InferShapeContext* ctx) const override {
    PADDLE_ENFORCE(ctx->HasInput("X"), "Input(X) should be not null.");
    PADDLE_ENFORCE(ctx->HasInput("CVM"), "Input(CVM) should be not null.");
    PADDLE_ENFORCE(ctx->HasOutput("Y"), "Output(Y) should be not null.");

    auto x_dims = ctx->GetInputDim("X");
    auto cvm_dims = ctx->GetInputDim("CVM");
    PADDLE_ENFORCE_EQ(x_dims.size(), 2UL, "Input(X)'s rank should be 2.");
    PADDLE_ENFORCE_EQ(cvm_dims.size(), 2UL, "Input(CVM)'s rank should be 2.");
    PADDLE_ENFORCE_EQ(cvm_dims[1], 2UL,
                      "The 2nd dimension of "
                      "Input(CVM) should be 2.");

    if (ctx->Attrs().Get<bool>("use_cvm")) {
      ctx->SetOutputDim("Y", {x_dims[0], x_dims[1]});
    } else {
      ctx->SetOutputDim("Y", {x_dims[0], x_dims[1] - 2});
    }
    ctx->ShareLoD("X", /*->*/ "Y");
  }

 protected:
  // Explicitly set that the data type of computation kernel of
  // cvm
  // is determined by its input "X".
  framework::OpKernelType GetExpectedKernelType(
      const framework::ExecutionContext& ctx) const override {
    return framework::OpKernelType(ctx.Input<Tensor>("X")->type(),
                                   platform::CPUPlace());
  }
};

class CVMGradientOp : public framework::OperatorWithKernel {
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

  void InferShape(framework::InferShapeContext* ctx) const override {
    PADDLE_ENFORCE(ctx->HasInput("X"), "Input(X) should be not null.");
    PADDLE_ENFORCE(ctx->HasInput("CVM"), "Input(CVM) should be not null.");
    PADDLE_ENFORCE(ctx->HasInput(framework::GradVarName("Y")),
                   "Input(Y@GRAD) should be not null.");
    PADDLE_ENFORCE(ctx->HasOutput(framework::GradVarName("X")),
                   "Output(X@GRAD) should be not null.");

    auto x_dims = ctx->GetInputDim("X");
    auto cvm_dims = ctx->GetInputDim("CVM");
    auto dy_dims = ctx->GetInputDim(framework::GradVarName("Y"));
    PADDLE_ENFORCE_EQ(x_dims.size(), 2, "Input(X)'s rank should be 2.");
    PADDLE_ENFORCE_EQ(dy_dims.size(), 2, "Input(Y@Grad)'s rank should be 2.");
    PADDLE_ENFORCE_EQ(cvm_dims.size(), 2, "Input(CVM)'s rank should be 2.");

    PADDLE_ENFORCE_EQ(x_dims[0], dy_dims[0],
                      "The 1st dimension of Input(X) and Input(Y@Grad) should "
                      "be equal.");

    PADDLE_ENFORCE_EQ(cvm_dims[1], 2,
                      "When Attr(soft_label) == false, the 2nd dimension of "
                      "Input(CVM) should be 2.");
    ctx->SetOutputDim(framework::GradVarName("X"), x_dims);
    ctx->ShareLoD("X", framework::GradVarName("X"));
  }

 protected:
  // Explicitly set that the data type of computation kernel of
  // cvm
  // is determined by its input "X".
  framework::OpKernelType GetExpectedKernelType(
      const framework::ExecutionContext& ctx) const override {
    return framework::OpKernelType(ctx.Input<Tensor>("X")->type(),
                                   platform::CPUPlace());
  }
};

class CVMOpMaker : public framework::OpProtoAndCheckerMaker {
 public:
  void Make() override {
    AddInput("X",
             "(LodTensor, default LodTensor<float>), a 2-D tensor with shape "
             "[N x D],"
             " where N is the batch size and D is the emebdding dim. ");
    AddInput("CVM",
             "(Tensor),  a 2-D Tensor with shape [N x 2], where N is the batch "
             "size, 2 is show and click.");
    AddOutput("Y",
              "(LodTensor, default LodTensor<float>), a 2-D tensor with shape "
              "[N x K].");
    AddAttr<bool>("use_cvm", "bool, use cvm or not").SetDefault(true);
    AddComment(R"DOC(
CVM Operator.

      We assume that input X is a embedding vector with cvm_feature(show and click), which shape is [N * D] (D is 2(cvm_feature) + embedding dim, N is batch_size)
      if use_cvm is True, we will log(cvm_feature), and output shape is [N * D].
      if use_cvm is False, we will remove cvm_feature from input, and output shape is [N * (D - 2)].

)DOC");
  }
};

class CVMGradOpDescMaker : public framework::SingleGradOpDescMaker {
 public:
  using framework::SingleGradOpDescMaker::SingleGradOpDescMaker;

 protected:
  std::unique_ptr<framework::OpDesc> Apply() const override {
    std::unique_ptr<framework::OpDesc> op(new framework::OpDesc());
    op->SetType("cvm_grad");
    op->SetInput("CVM", Input("CVM"));
    op->SetInput(framework::GradVarName("Y"), OutputGrad("Y"));
    op->SetOutput(framework::GradVarName("X"), InputGrad("X"));
    op->SetAttrMap(Attrs());
    return op;
  }
};

}  // namespace operators
}  // namespace paddle

namespace ops = paddle::operators;
REGISTER_OPERATOR(cvm, ops::CVMOp, ops::CVMOpMaker, ops::CVMGradOpDescMaker);

REGISTER_OPERATOR(cvm_grad, ops::CVMGradientOp);

REGISTER_OP_CPU_KERNEL(cvm, ops::CVMOpKernel<float>, ops::CVMOpKernel<double>);

REGISTER_OP_CPU_KERNEL(cvm_grad, ops::CVMGradOpKernel<float>,
                       ops::CVMGradOpKernel<double>);
<commit_msg>add X to grad test=develop<commit_after>/* Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserve.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */

#include "paddle/fluid/operators/cvm_op.h"
#include <memory>
#include "paddle/fluid/operators/math/math_function.h"

namespace paddle {
namespace operators {

using Tensor = framework::Tensor;

class CVMOp : public framework::OperatorWithKernel {
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

  void InferShape(framework::InferShapeContext* ctx) const override {
    PADDLE_ENFORCE(ctx->HasInput("X"), "Input(X) should be not null.");
    PADDLE_ENFORCE(ctx->HasInput("CVM"), "Input(CVM) should be not null.");
    PADDLE_ENFORCE(ctx->HasOutput("Y"), "Output(Y) should be not null.");

    auto x_dims = ctx->GetInputDim("X");
    auto cvm_dims = ctx->GetInputDim("CVM");
    PADDLE_ENFORCE_EQ(x_dims.size(), 2UL, "Input(X)'s rank should be 2.");
    PADDLE_ENFORCE_EQ(cvm_dims.size(), 2UL, "Input(CVM)'s rank should be 2.");
    PADDLE_ENFORCE_EQ(cvm_dims[1], 2UL,
                      "The 2nd dimension of "
                      "Input(CVM) should be 2.");

    if (ctx->Attrs().Get<bool>("use_cvm")) {
      ctx->SetOutputDim("Y", {x_dims[0], x_dims[1]});
    } else {
      ctx->SetOutputDim("Y", {x_dims[0], x_dims[1] - 2});
    }
    ctx->ShareLoD("X", /*->*/ "Y");
  }

 protected:
  // Explicitly set that the data type of computation kernel of
  // cvm
  // is determined by its input "X".
  framework::OpKernelType GetExpectedKernelType(
      const framework::ExecutionContext& ctx) const override {
    return framework::OpKernelType(ctx.Input<Tensor>("X")->type(),
                                   platform::CPUPlace());
  }
};

class CVMGradientOp : public framework::OperatorWithKernel {
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

  void InferShape(framework::InferShapeContext* ctx) const override {
    PADDLE_ENFORCE(ctx->HasInput("X"), "Input(X) should be not null.");
    PADDLE_ENFORCE(ctx->HasInput("CVM"), "Input(CVM) should be not null.");
    PADDLE_ENFORCE(ctx->HasInput(framework::GradVarName("Y")),
                   "Input(Y@GRAD) should be not null.");
    PADDLE_ENFORCE(ctx->HasOutput(framework::GradVarName("X")),
                   "Output(X@GRAD) should be not null.");

    auto x_dims = ctx->GetInputDim("X");
    auto cvm_dims = ctx->GetInputDim("CVM");
    auto dy_dims = ctx->GetInputDim(framework::GradVarName("Y"));
    PADDLE_ENFORCE_EQ(x_dims.size(), 2, "Input(X)'s rank should be 2.");
    PADDLE_ENFORCE_EQ(dy_dims.size(), 2, "Input(Y@Grad)'s rank should be 2.");
    PADDLE_ENFORCE_EQ(cvm_dims.size(), 2, "Input(CVM)'s rank should be 2.");

    PADDLE_ENFORCE_EQ(x_dims[0], dy_dims[0],
                      "The 1st dimension of Input(X) and Input(Y@Grad) should "
                      "be equal.");

    PADDLE_ENFORCE_EQ(cvm_dims[1], 2,
                      "When Attr(soft_label) == false, the 2nd dimension of "
                      "Input(CVM) should be 2.");
    ctx->SetOutputDim(framework::GradVarName("X"), x_dims);
    ctx->ShareLoD("X", framework::GradVarName("X"));
  }

 protected:
  // Explicitly set that the data type of computation kernel of
  // cvm
  // is determined by its input "X".
  framework::OpKernelType GetExpectedKernelType(
      const framework::ExecutionContext& ctx) const override {
    return framework::OpKernelType(ctx.Input<Tensor>("X")->type(),
                                   platform::CPUPlace());
  }
};

class CVMOpMaker : public framework::OpProtoAndCheckerMaker {
 public:
  void Make() override {
    AddInput("X",
             "(LodTensor, default LodTensor<float>), a 2-D tensor with shape "
             "[N x D],"
             " where N is the batch size and D is the emebdding dim. ");
    AddInput("CVM",
             "(Tensor),  a 2-D Tensor with shape [N x 2], where N is the batch "
             "size, 2 is show and click.");
    AddOutput("Y",
              "(LodTensor, default LodTensor<float>), a 2-D tensor with shape "
              "[N x K].");
    AddAttr<bool>("use_cvm", "bool, use cvm or not").SetDefault(true);
    AddComment(R"DOC(
CVM Operator.

      We assume that input X is a embedding vector with cvm_feature(show and click), which shape is [N * D] (D is 2(cvm_feature) + embedding dim, N is batch_size)
      if use_cvm is True, we will log(cvm_feature), and output shape is [N * D].
      if use_cvm is False, we will remove cvm_feature from input, and output shape is [N * (D - 2)].

)DOC");
  }
};

class CVMGradOpDescMaker : public framework::SingleGradOpDescMaker {
 public:
  using framework::SingleGradOpDescMaker::SingleGradOpDescMaker;

 protected:
  std::unique_ptr<framework::OpDesc> Apply() const override {
    std::unique_ptr<framework::OpDesc> op(new framework::OpDesc());
    op->SetType("cvm_grad");
    op->SetInput("X", Input("X"));
    op->SetInput("CVM", Input("CVM"));
    op->SetInput(framework::GradVarName("Y"), OutputGrad("Y"));
    op->SetOutput(framework::GradVarName("X"), InputGrad("X"));
    op->SetAttrMap(Attrs());
    return op;
  }
};

}  // namespace operators
}  // namespace paddle

namespace ops = paddle::operators;
REGISTER_OPERATOR(cvm, ops::CVMOp, ops::CVMOpMaker, ops::CVMGradOpDescMaker);

REGISTER_OPERATOR(cvm_grad, ops::CVMGradientOp);

REGISTER_OP_CPU_KERNEL(cvm, ops::CVMOpKernel<float>, ops::CVMOpKernel<double>);

REGISTER_OP_CPU_KERNEL(cvm_grad, ops::CVMGradOpKernel<float>,
                       ops::CVMGradOpKernel<double>);
<|endoftext|>


<commit_before>/*
**
* BEGIN_COPYRIGHT
*
* load_tools is a plugin for SciDB.  Copyright (C) 2008-2014 SciDB, Inc.
*
* load_tools is free software: you can redistribute it and/or modify
* it under the terms of the AFFERO GNU General Public License as published by
* the Free Software Foundation.
*
* load_tools is distributed "AS-IS" AND WITHOUT ANY WARRANTY OF ANY KIND,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* NON-INFRINGEMENT, OR FITNESS FOR A PARTICULAR PURPOSE. See
* the AFFERO GNU General Public License for the complete license terms.
*
* You should have received a copy of the AFFERO GNU General Public License
* along with load_tools.  If not, see <http://www.gnu.org/licenses/agpl-3.0.html>
*
* END_COPYRIGHT
*/

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <vector>

#include <boost/lexical_cast.hpp>
#include <boost/assign.hpp>
#include <boost/algorithm/string.hpp>

#include "query/FunctionLibrary.h"
#include "query/FunctionDescription.h"
#include "system/ErrorsLibrary.h"

using namespace std;
using namespace boost;
using namespace boost::assign;
using namespace scidb;

/**
 * DCAST: cast with default, does not throw an error. Tries to cast input to the appropriate type. If the cast fails,
 * returns the supplied default.
 */
template <typename T>
static void dcast(const Value** args, Value *res, void*)
{
  if(args[0]->isNull())
  {
      res->setNull(args[0]->getMissingReason());
      return;
  }
  const char* s = args[0]->getString();
  try
  {
      T result = lexical_cast<T>(s);
      res->set<T>(result);
  }
  catch(...)
  {
      Value const* def = args[1];
      if(def->isNull())
      {
          res->setNull( def->getMissingReason());
      }
      else
      {
          res->set<T>(def->get<T>());
      }
  }
}

static scidb::UserDefinedFunction dcast_double (scidb::FunctionDescription("dcast", list_of("string")("double"), "double", &dcast<double> ));
static scidb::UserDefinedFunction dcast_float (scidb::FunctionDescription("dcast", list_of("string")("float"), "float", &dcast<float> ));
static scidb::UserDefinedFunction dcast_bool (scidb::FunctionDescription("dcast", list_of("string")("bool"), "bool", &dcast<bool> ));
static scidb::UserDefinedFunction dcast_int64  (scidb::FunctionDescription("dcast", list_of("string")("int64"),  "int64" , &dcast<int64_t>));
static scidb::UserDefinedFunction dcast_int32 (scidb::FunctionDescription("dcast", list_of("string")("int32"), "int32", &dcast<int32_t> ));
static scidb::UserDefinedFunction dcast_int16 (scidb::FunctionDescription("dcast", list_of("string")("int16"), "int16", &dcast<int16_t> ));
static scidb::UserDefinedFunction dcast_int8 (scidb::FunctionDescription("dcast", list_of("string")("int8"), "int8", &dcast<int8_t> ));
static scidb::UserDefinedFunction dcast_uint64 (scidb::FunctionDescription("dcast", list_of("string")("uint64"), "uint64", &dcast<uint64_t> ));
static scidb::UserDefinedFunction dcast_uint32 (scidb::FunctionDescription("dcast", list_of("string")("uint32"), "uint32", &dcast<uint32_t> ));
static scidb::UserDefinedFunction dcast_uint16 (scidb::FunctionDescription("dcast", list_of("string")("uint16"), "uint16", &dcast<uint16_t> ));
static scidb::UserDefinedFunction dcast_uint8 (scidb::FunctionDescription("dcast", list_of("string")("uint8"), "uint8", &dcast<uint8_t> ));

// XXX How to add datetime conversion here? The naive approach:
//static scidb::UserDefinedFunction dcast_datetimetz (scidb::FunctionDescription("dcast", list_of("string")("datetimetz"), "datetimetz", &dcast<datetimetz> ));
// doesn't work!

/**
 * first argument: the string to trim
 * second argument: a set of characters to trim, represented as another string {S}
 * returns: the first argument with all occurrences of any of the characters in S removed from the beginning or end of the string
 */
static void trim (const Value** args, Value *res, void*)
{
    if(args[0]->isNull())
    {
      res->setNull(args[0]->getMissingReason());
      return;
    }
    const char* input = args[0]->getString();
    size_t inputLen = args[0]->size();
    const char* chars = args[1]->getString();
    size_t charsLen = args[1]->size();
    if (charsLen == 0 || (charsLen ==1 && chars[0]==0))
    {
        res->setSize(inputLen);
        memcpy(res->data(), input, inputLen);
        return;
    }
    const char* start = input;
    for(size_t i=0; i<inputLen; ++i)
    {
        char ch = input[i];
        if(ch==0)
        {
            break;
        }
        bool match = false;
        for(size_t j=0; j<charsLen; ++j)
        {
            if(ch == chars[j])
            {
                match =true;
                break;
            }
        }
        if(!match)
        {
            break;
        }
        ++start;
    }
    const char* end = input + inputLen - 1;
    for(ssize_t i = inputLen-1; i>=0; --i)
    {
        char ch = input[i];
        if(ch==0)
        {
            continue;
        }
        bool match = false;
        for(size_t j=0; j<charsLen; ++j)
        {
            if(ch == chars[j])
            {
                match =true;
                break;
            }
        }
        if(!match)
        {
            break;
        }
        --end;
    }
    if (inputLen == 0 || (inputLen ==1 && input[0]==0) || start >= end)
    {
        res->setSize(1);
        ((char *) res->data())[0]=0;
        return;
    }
    size_t size = end - start + 1;
    res->setSize(size);
    memcpy(res->data(), start, (end-start));
    ((char*)res->data())[size-1]=0;
}

static scidb::UserDefinedFunction trim_str (scidb::FunctionDescription("trim", list_of("string")("string"), "string", &trim ));

static void int_to_char (const Value** args, Value *res, void*)
{
    if(args[0]->isNull())
    {
      res->setNull(args[0]->getMissingReason());
      return;
    }
    uint8_t input = args[0]->getUint8();
    res->setChar(input);
}

static scidb::UserDefinedFunction int_to_c (scidb::FunctionDescription("int_to_char", list_of("uint8"), "char", &int_to_char ));
static scidb::UserDefinedFunction c_to_int (scidb::FunctionDescription("char_to_int", list_of("char"), "uint8", &int_to_char ));

static void codify (const Value** args, Value *res, void*)
{
    if(args[0]->isNull())
    {
      res->setNull(args[0]->getMissingReason());
      return;
    }
    const char* input = args[0]->getString();
    size_t inputLen = args[0]->size();
    ostringstream out;
    for (size_t i=0; i< inputLen; ++i)
    {
        char c = input[i];
        int32_t res = c;
        out<<res<<"|";
    }
    res->setString(out.str().c_str());
}

static scidb::UserDefinedFunction asciify_str (scidb::FunctionDescription("codify", list_of("string"), "string", &codify ));

size_t match_length(
  const char* pattern,
  size_t patternLength,
  const char* str,
  size_t strLength,
  int strOffset) {

  size_t i = 0;
  for(; i < patternLength && strOffset + i < strLength && pattern[i] == str[strOffset + i]; i++)
    {}

  return i;
}

int find_next(const char* str, size_t strLen, int offset, char nextChar) {
  size_t i = 0;
  for(i = offset; i < strLen; i++) {
    if(str[i] == nextChar) { return i; }
  }

  return i;
}

int find_first_pattern_match(
  const char* pattern,
  size_t patternLength,
  const char* str,
  size_t strLength) {

  for(size_t i = 0; i < strLength; i++) {
    if(match_length(pattern, patternLength, str, strLength, i) == patternLength &&
       i + patternLength < strLength &&
       str[i+patternLength] == '=') {
       return i;
    }
  }

  return -1;
}

static void keyed_value( const Value** args, Value *res, void* ) {

  for(int i = 0; i < 2; i++) {
    if(args[i]->isNull()) {
      res->setNull(args[i]->getMissingReason());
      return;
    }
  }

  const char* infoField = args[0]->getString();
  size_t infoLen = args[0]->size();
  const char* keyName = args[1]->getString();
  size_t keyLen = args[1]->size();

  int kstart = find_first_pattern_match(keyName, keyLen-1, infoField, infoLen-1);

  if(keyLen == 0  || (keyLen == 1 && keyName[0]==0) || kstart == -1) {
    *res = *(args[2]);
    return;
  }

  int vstart = kstart + keyLen;
  int vend = find_next(infoField, infoLen-1, vstart, ';');

  size_t size = vend - vstart + 1;
  res->setSize(size);
  memcpy(res->data(), &infoField[vstart], (vend-vstart));
  ((char*)res->data())[size-1]=0;
}
static scidb::UserDefinedFunction key_value_extract( scidb::FunctionDescription("keyed_value", list_of("string")("string")("string"), "string", &keyed_value));

//in some rare cases, on older versions, string values are not null-terminated; we aim to be nice
string get_null_terminated_string(char const* input, size_t const size)
{
    if(size == 0)
    {
        return string("");
    }
    else if (input[size-1] != 0)
    {
        return string(input, size);
    }
    else
    {
        return string(input);
    }
}

/**
 * Loosely based on https://github.com/slottad/scidb-genotypes/
 * Thanks to Douglas Slotta.
 * We find we are repeating some of the work and it might make sense to merge in more of that functionality
 */
void num_csv(const scidb::Value** args, scidb::Value* res, void*)
{
    if (args[0]->isNull())
    {
        res->setNull(args[0]->getMissingReason());
        return;
    }
    string cell = get_null_terminated_string(args[0]->getString(), args[0]->size());
    uint32_t count = 0;
    if (!cell.empty())
    {
        count = 1 + std::count(cell.begin(), cell.end(), ',');
    }
    res->setUint32(count);
}
static scidb::UserDefinedFunction ncsv( scidb::FunctionDescription("num_csv", list_of("string"), "uint32", &num_csv));

void nth_csv(const scidb::Value** args, scidb::Value* res, void*)
{
    if (args[0]->isNull())
    {
        res->setNull(args[0]->getMissingReason());
        return;
    }
    if (args[1]->isNull())
    {
        res->setNull(0);
        return;
    }
    uint32_t n = args[1]->getUint32();
    string cell = get_null_terminated_string(args[0]->getString(), args[0]->size());
    vector<string> values;
    split(values, cell, is_from_range(',', ','));
    if (values.size() <= n)
    {
        res->setNull(0);
        return;
    }
    res->setString(values[n]);
}

static scidb::UserDefinedFunction ntcsv( scidb::FunctionDescription("nth_csv", list_of("string")("uint32"), "string", &nth_csv));

/**
 * arg0: FORMAT FIELD
 * arg1: sample FIELD
 * arg2: attribute name
 */
static void extract_format_field( const Value **args, Value* res, void*) {
    for(int i = 0; i < 3; i++) {
        if(args[i]->isNull()) {
            res->setNull(args[i]->getMissingReason());
            return;
        }
    }

    const char* formatField = args[0]->getString();
    size_t formatLen = args[0]->size();
    const char* sampleField = args[1]->getString();
    size_t sampleLen = args[1]->size();
    const char* attrName = args[2]->getString();
    size_t attrLen = args[2]->size();

    size_t index = 0;
    size_t j = 0, k = 0;
    bool match = false;
    for(; j < formatLen && !match; j += k) {
      if(formatField[j] == ':') { index++; j++; }
      match = true;
      for(k = 0; j+k < formatLen && formatField[j+k] != ':'; k++) {
        if(k >= attrLen || formatField[j+k] != attrName[k]) { match = false; }
      }
    }

    if(!match) {
      res->setNull(0);
      return;
    }

    size_t start = 0;
    size_t indexi = 0;
    for(; start < sampleLen && indexi < index; start++) {
        if(sampleField[start] == ':') {
            indexi += 1;
        }
    }

    size_t end = start+1;
    for(; end < sampleLen && sampleField[end] != ':'; end += 1)
        {}

    size_t size = end - start + 1;
    res->setSize(size);
    memcpy(res->data(), &sampleField[start], (end-start));
    ((char*)res->data())[size-1]=0;
}

static scidb::UserDefinedFunction format_extract(scidb::FunctionDescription("format_extract", list_of("string")("string")("string"), "string", &extract_format_field));
<commit_msg>Add maxlen_csv function<commit_after>/*
**
* BEGIN_COPYRIGHT
*
* load_tools is a plugin for SciDB.  Copyright (C) 2008-2014 SciDB, Inc.
*
* load_tools is free software: you can redistribute it and/or modify
* it under the terms of the AFFERO GNU General Public License as published by
* the Free Software Foundation.
*
* load_tools is distributed "AS-IS" AND WITHOUT ANY WARRANTY OF ANY KIND,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* NON-INFRINGEMENT, OR FITNESS FOR A PARTICULAR PURPOSE. See
* the AFFERO GNU General Public License for the complete license terms.
*
* You should have received a copy of the AFFERO GNU General Public License
* along with load_tools.  If not, see <http://www.gnu.org/licenses/agpl-3.0.html>
*
* END_COPYRIGHT
*/

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <vector>

#include <boost/lexical_cast.hpp>
#include <boost/assign.hpp>
#include <boost/algorithm/string.hpp>

#include "query/FunctionLibrary.h"
#include "query/FunctionDescription.h"
#include "system/ErrorsLibrary.h"

using namespace std;
using namespace boost;
using namespace boost::assign;
using namespace scidb;

/**
 * DCAST: cast with default, does not throw an error. Tries to cast input to the appropriate type. If the cast fails,
 * returns the supplied default.
 */
template <typename T>
static void dcast(const Value** args, Value *res, void*)
{
  if(args[0]->isNull())
  {
      res->setNull(args[0]->getMissingReason());
      return;
  }
  const char* s = args[0]->getString();
  try
  {
      T result = lexical_cast<T>(s);
      res->set<T>(result);
  }
  catch(...)
  {
      Value const* def = args[1];
      if(def->isNull())
      {
          res->setNull( def->getMissingReason());
      }
      else
      {
          res->set<T>(def->get<T>());
      }
  }
}

static scidb::UserDefinedFunction dcast_double (scidb::FunctionDescription("dcast", list_of("string")("double"), "double", &dcast<double> ));
static scidb::UserDefinedFunction dcast_float (scidb::FunctionDescription("dcast", list_of("string")("float"), "float", &dcast<float> ));
static scidb::UserDefinedFunction dcast_bool (scidb::FunctionDescription("dcast", list_of("string")("bool"), "bool", &dcast<bool> ));
static scidb::UserDefinedFunction dcast_int64  (scidb::FunctionDescription("dcast", list_of("string")("int64"),  "int64" , &dcast<int64_t>));
static scidb::UserDefinedFunction dcast_int32 (scidb::FunctionDescription("dcast", list_of("string")("int32"), "int32", &dcast<int32_t> ));
static scidb::UserDefinedFunction dcast_int16 (scidb::FunctionDescription("dcast", list_of("string")("int16"), "int16", &dcast<int16_t> ));
static scidb::UserDefinedFunction dcast_int8 (scidb::FunctionDescription("dcast", list_of("string")("int8"), "int8", &dcast<int8_t> ));
static scidb::UserDefinedFunction dcast_uint64 (scidb::FunctionDescription("dcast", list_of("string")("uint64"), "uint64", &dcast<uint64_t> ));
static scidb::UserDefinedFunction dcast_uint32 (scidb::FunctionDescription("dcast", list_of("string")("uint32"), "uint32", &dcast<uint32_t> ));
static scidb::UserDefinedFunction dcast_uint16 (scidb::FunctionDescription("dcast", list_of("string")("uint16"), "uint16", &dcast<uint16_t> ));
static scidb::UserDefinedFunction dcast_uint8 (scidb::FunctionDescription("dcast", list_of("string")("uint8"), "uint8", &dcast<uint8_t> ));

// XXX How to add datetime conversion here? The naive approach:
//static scidb::UserDefinedFunction dcast_datetimetz (scidb::FunctionDescription("dcast", list_of("string")("datetimetz"), "datetimetz", &dcast<datetimetz> ));
// doesn't work!

/**
 * first argument: the string to trim
 * second argument: a set of characters to trim, represented as another string {S}
 * returns: the first argument with all occurrences of any of the characters in S removed from the beginning or end of the string
 */
static void trim (const Value** args, Value *res, void*)
{
    if(args[0]->isNull())
    {
      res->setNull(args[0]->getMissingReason());
      return;
    }
    const char* input = args[0]->getString();
    size_t inputLen = args[0]->size();
    const char* chars = args[1]->getString();
    size_t charsLen = args[1]->size();
    if (charsLen == 0 || (charsLen ==1 && chars[0]==0))
    {
        res->setSize(inputLen);
        memcpy(res->data(), input, inputLen);
        return;
    }
    const char* start = input;
    for(size_t i=0; i<inputLen; ++i)
    {
        char ch = input[i];
        if(ch==0)
        {
            break;
        }
        bool match = false;
        for(size_t j=0; j<charsLen; ++j)
        {
            if(ch == chars[j])
            {
                match =true;
                break;
            }
        }
        if(!match)
        {
            break;
        }
        ++start;
    }
    const char* end = input + inputLen - 1;
    for(ssize_t i = inputLen-1; i>=0; --i)
    {
        char ch = input[i];
        if(ch==0)
        {
            continue;
        }
        bool match = false;
        for(size_t j=0; j<charsLen; ++j)
        {
            if(ch == chars[j])
            {
                match =true;
                break;
            }
        }
        if(!match)
        {
            break;
        }
        --end;
    }
    if (inputLen == 0 || (inputLen ==1 && input[0]==0) || start >= end)
    {
        res->setSize(1);
        ((char *) res->data())[0]=0;
        return;
    }
    size_t size = end - start + 1;
    res->setSize(size);
    memcpy(res->data(), start, (end-start));
    ((char*)res->data())[size-1]=0;
}

static scidb::UserDefinedFunction trim_str (scidb::FunctionDescription("trim", list_of("string")("string"), "string", &trim ));

static void int_to_char (const Value** args, Value *res, void*)
{
    if(args[0]->isNull())
    {
      res->setNull(args[0]->getMissingReason());
      return;
    }
    uint8_t input = args[0]->getUint8();
    res->setChar(input);
}

static scidb::UserDefinedFunction int_to_c (scidb::FunctionDescription("int_to_char", list_of("uint8"), "char", &int_to_char ));
static scidb::UserDefinedFunction c_to_int (scidb::FunctionDescription("char_to_int", list_of("char"), "uint8", &int_to_char ));

static void codify (const Value** args, Value *res, void*)
{
    if(args[0]->isNull())
    {
      res->setNull(args[0]->getMissingReason());
      return;
    }
    const char* input = args[0]->getString();
    size_t inputLen = args[0]->size();
    ostringstream out;
    for (size_t i=0; i< inputLen; ++i)
    {
        char c = input[i];
        int32_t res = c;
        out<<res<<"|";
    }
    res->setString(out.str().c_str());
}

static scidb::UserDefinedFunction asciify_str (scidb::FunctionDescription("codify", list_of("string"), "string", &codify ));

size_t match_length(
  const char* pattern,
  size_t patternLength,
  const char* str,
  size_t strLength,
  int strOffset) {

  size_t i = 0;
  for(; i < patternLength && strOffset + i < strLength && pattern[i] == str[strOffset + i]; i++)
    {}

  return i;
}

int find_next(const char* str, size_t strLen, int offset, char nextChar) {
  size_t i = 0;
  for(i = offset; i < strLen; i++) {
    if(str[i] == nextChar) { return i; }
  }

  return i;
}

int find_first_pattern_match(
  const char* pattern,
  size_t patternLength,
  const char* str,
  size_t strLength) {

  for(size_t i = 0; i < strLength; i++) {
    if(match_length(pattern, patternLength, str, strLength, i) == patternLength &&
       i + patternLength < strLength &&
       str[i+patternLength] == '=') {
       return i;
    }
  }

  return -1;
}

static void keyed_value( const Value** args, Value *res, void* ) {

  for(int i = 0; i < 2; i++) {
    if(args[i]->isNull()) {
      res->setNull(args[i]->getMissingReason());
      return;
    }
  }

  const char* infoField = args[0]->getString();
  size_t infoLen = args[0]->size();
  const char* keyName = args[1]->getString();
  size_t keyLen = args[1]->size();

  int kstart = find_first_pattern_match(keyName, keyLen-1, infoField, infoLen-1);

  if(keyLen == 0  || (keyLen == 1 && keyName[0]==0) || kstart == -1) {
    *res = *(args[2]);
    return;
  }

  int vstart = kstart + keyLen;
  int vend = find_next(infoField, infoLen-1, vstart, ';');

  size_t size = vend - vstart + 1;
  res->setSize(size);
  memcpy(res->data(), &infoField[vstart], (vend-vstart));
  ((char*)res->data())[size-1]=0;
}
static scidb::UserDefinedFunction key_value_extract( scidb::FunctionDescription("keyed_value", list_of("string")("string")("string"), "string", &keyed_value));

//in some rare cases, on older versions, string values are not null-terminated; we aim to be nice
string get_null_terminated_string(char const* input, size_t const size)
{
    if(size == 0)
    {
        return string("");
    }
    else if (input[size-1] != 0)
    {
        return string(input, size);
    }
    else
    {
        return string(input);
    }
}

/**
 * Loosely based on https://github.com/slottad/scidb-genotypes/
 * Thanks to Douglas Slotta.
 * We find we are repeating some of the work and it might make sense to merge in more of that functionality
 */
void num_csv(const scidb::Value** args, scidb::Value* res, void*)
{
    if (args[0]->isNull())
    {
        res->setNull(args[0]->getMissingReason());
        return;
    }
    string cell = get_null_terminated_string(args[0]->getString(), args[0]->size());
    uint32_t count = 0;
    if (!cell.empty())
    {
        count = 1 + std::count(cell.begin(), cell.end(), ',');
    }
    res->setUint32(count);
}
static scidb::UserDefinedFunction ncsv( scidb::FunctionDescription("num_csv", list_of("string"), "uint32", &num_csv));

void nth_csv(const scidb::Value** args, scidb::Value* res, void*)
{
    if (args[0]->isNull())
    {
        res->setNull(args[0]->getMissingReason());
        return;
    }
    if (args[1]->isNull())
    {
        res->setNull(0);
        return;
    }
    uint32_t n = args[1]->getUint32();
    string cell = get_null_terminated_string(args[0]->getString(), args[0]->size());
    vector<string> values;
    split(values, cell, is_from_range(',', ','));
    if (values.size() <= n)
    {
        res->setNull(0);
        return;
    }
    res->setString(values[n]);
}

static scidb::UserDefinedFunction ntcsv( scidb::FunctionDescription("nth_csv", list_of("string")("uint32"), "string", &nth_csv));

void maxlen_csv(const scidb::Value** args, scidb::Value* res, void*)
{
    if (args[0]->isNull())
    {
        res->setNull(args[0]->getMissingReason());
        return;
    }
    string cell = get_null_terminated_string(args[0]->getString(), args[0]->size());
    vector<string> values;
    split(values, cell, is_from_range(',', ','));
    uint32_t maxSize =0;
    for(size_t i=0, n=values.size(); i<n; ++i)
    {
        if(values[i].size()> maxSize)
            maxSize=values[i].size();
    }
    res->setUint32(maxSize);
}

static scidb::UserDefinedFunction mlcsv( scidb::FunctionDescription("maxlen_csv", list_of("string"), "uint32", &maxlen_csv));

/**
 * arg0: FORMAT FIELD
 * arg1: sample FIELD
 * arg2: attribute name
 */
static void extract_format_field( const Value **args, Value* res, void*) {
    for(int i = 0; i < 3; i++) {
        if(args[i]->isNull()) {
            res->setNull(args[i]->getMissingReason());
            return;
        }
    }

    const char* formatField = args[0]->getString();
    size_t formatLen = args[0]->size();
    const char* sampleField = args[1]->getString();
    size_t sampleLen = args[1]->size();
    const char* attrName = args[2]->getString();
    size_t attrLen = args[2]->size();

    size_t index = 0;
    size_t j = 0, k = 0;
    bool match = false;
    for(; j < formatLen && !match; j += k) {
      if(formatField[j] == ':') { index++; j++; }
      match = true;
      for(k = 0; j+k < formatLen && formatField[j+k] != ':'; k++) {
        if(k >= attrLen || formatField[j+k] != attrName[k]) { match = false; }
      }
    }

    if(!match) {
      res->setNull(0);
      return;
    }

    size_t start = 0;
    size_t indexi = 0;
    for(; start < sampleLen && indexi < index; start++) {
        if(sampleField[start] == ':') {
            indexi += 1;
        }
    }

    size_t end = start+1;
    for(; end < sampleLen && sampleField[end] != ':'; end += 1)
        {}

    size_t size = end - start + 1;
    res->setSize(size);
    memcpy(res->data(), &sampleField[start], (end-start));
    ((char*)res->data())[size-1]=0;
}

static scidb::UserDefinedFunction format_extract(scidb::FunctionDescription("format_extract", list_of("string")("string")("string"), "string", &extract_format_field));
<|endoftext|>


<commit_before>#include "global.h"
#include "GVHistory.h"
#include "Singletons.h"

#include "ui_InboxWidget.h"

GVHistory::GVHistory (QObject *parent)
: QObject (parent)
, mutex (QMutex::Recursive)
, bLoggedIn (false)
, modelInbox (NULL)
{
    // Initially, all are to be selected
    strSelectedMessages = "all";
}//GVHistory::GVHistory

GVHistory::~GVHistory(void)
{
    deinitModel ();
}//GVHistory::~GVHistory

void
GVHistory::deinitModel ()
{
    if (NULL != modelInbox) {
        delete modelInbox;
        modelInbox = NULL;
    }
}//GVHistory::deinitModel

void
GVHistory::initModel (QDeclarativeView *pMainWindow)
{
    deinitModel ();

    CacheDatabase &dbMain = Singletons::getRef().getDBMain ();
    modelInbox = dbMain.newInboxModel ();

    QDeclarativeContext *ctx = pMainWindow->rootContext();
    ctx->setContextProperty ("inboxModel", modelInbox);
    prepView ();
}//GVHistory::initModel

void
GVHistory::prepView ()
{
    CacheDatabase &dbMain = Singletons::getRef().getDBMain ();

    emit status ("Re-selecting inbox entries. This will take some time", 0);
    dbMain.refreshInboxModel (modelInbox, strSelectedMessages);

    while (modelInbox->canFetchMore ()) {
        modelInbox->fetchMore ();
    }
    emit status ("Inbox entries selected.");
}//GVHistory::prepView

void
GVHistory::refreshHistory ()
{
    QMutexLocker locker(&mutex);
    if (!bLoggedIn)
    {
        return;
    }

    CacheDatabase &dbMain = Singletons::getRef().getDBMain ();
    QDateTime dtUpdate;
    dbMain.getLastInboxUpdate (dtUpdate);

    GVAccess &webPage = Singletons::getRef().getGVAccess ();
    QVariantList l;
    l += "all";
    l += "1";
    l += "10";
    l += dtUpdate;
    QObject::connect (
        &webPage, SIGNAL (oneHistoryEvent (const GVHistoryEvent &)),
         this   , SLOT   (oneHistoryEvent (const GVHistoryEvent &)));
    emit status ("Retrieving Inbox...", 0);
    if (!webPage.enqueueWork (GVAW_getInbox, l, this,
            SLOT (getHistoryDone (bool, const QVariantList &))))
    {
        getHistoryDone (false, l);
    }
}//GVHistory::refreshHistory

void
GVHistory::refreshFullInbox ()
{
    CacheDatabase &dbMain = Singletons::getRef().getDBMain ();
    dbMain.setLastInboxUpdate (QDateTime::fromString ("2000-01-01",
                                                      "yyyy-MM-dd"));
    refreshHistory ();
}//GVHistory::refreshFullInbox

void
GVHistory::oneHistoryEvent (const GVHistoryEvent &hevent)
{
    QString strType = modelInbox->type_to_string (hevent.Type);
    if (0 == strType.size ())
    {
        return;
    }

    CacheDatabase &dbMain = Singletons::getRef().getDBMain ();
    dbMain.insertHistory (hevent);
}//GVHistory::oneHistoryEvent

void
GVHistory::getHistoryDone (bool, const QVariantList &)
{
    emit status ("Inbox retrieved. Sorting...", 0);

    GVAccess &webPage = Singletons::getRef().getGVAccess ();
    QObject::disconnect (
        &webPage, SIGNAL (oneHistoryEvent (const GVHistoryEvent &)),
         this   , SLOT   (oneHistoryEvent (const GVHistoryEvent &)));

    prepView ();

    CacheDatabase &dbMain = Singletons::getRef().getDBMain ();
    QDateTime dtUpdate;
    if (dbMain.getLatestInboxEntry (dtUpdate))
    {
        qDebug () << QString ("Latest inbox entry is : %1")
                            .arg (dtUpdate.toString ());
        dbMain.setLastInboxUpdate (dtUpdate);
    }

    emit status ("Inbox ready");
}//GVHistory::getHistoryDone

void
GVHistory::onInboxSelected (const QString &strSelection)
{
    QMutexLocker locker(&mutex);
    if (!bLoggedIn)
    {
        return;
    }

    strSelectedMessages = strSelection.toLower ();
    refreshHistory ();
}//GVHistory::onInboxSelected

void
GVHistory::loginSuccess ()
{
    QMutexLocker locker(&mutex);
    bLoggedIn = true;
}//GVHistory::loginSuccess

void
GVHistory::loggedOut ()
{
    QMutexLocker locker(&mutex);
    bLoggedIn = false;
}//GVHistory::loggedOut
<commit_msg>compile error<commit_after>#include "global.h"
#include "GVHistory.h"
#include "Singletons.h"

GVHistory::GVHistory (QObject *parent)
: QObject (parent)
, mutex (QMutex::Recursive)
, bLoggedIn (false)
, modelInbox (NULL)
{
    // Initially, all are to be selected
    strSelectedMessages = "all";
}//GVHistory::GVHistory

GVHistory::~GVHistory(void)
{
    deinitModel ();
}//GVHistory::~GVHistory

void
GVHistory::deinitModel ()
{
    if (NULL != modelInbox) {
        delete modelInbox;
        modelInbox = NULL;
    }
}//GVHistory::deinitModel

void
GVHistory::initModel (QDeclarativeView *pMainWindow)
{
    deinitModel ();

    CacheDatabase &dbMain = Singletons::getRef().getDBMain ();
    modelInbox = dbMain.newInboxModel ();

    QDeclarativeContext *ctx = pMainWindow->rootContext();
    ctx->setContextProperty ("inboxModel", modelInbox);
    prepView ();
}//GVHistory::initModel

void
GVHistory::prepView ()
{
    CacheDatabase &dbMain = Singletons::getRef().getDBMain ();

    emit status ("Re-selecting inbox entries. This will take some time", 0);
    dbMain.refreshInboxModel (modelInbox, strSelectedMessages);

    while (modelInbox->canFetchMore ()) {
        modelInbox->fetchMore ();
    }
    emit status ("Inbox entries selected.");
}//GVHistory::prepView

void
GVHistory::refreshHistory ()
{
    QMutexLocker locker(&mutex);
    if (!bLoggedIn)
    {
        return;
    }

    CacheDatabase &dbMain = Singletons::getRef().getDBMain ();
    QDateTime dtUpdate;
    dbMain.getLastInboxUpdate (dtUpdate);

    GVAccess &webPage = Singletons::getRef().getGVAccess ();
    QVariantList l;
    l += "all";
    l += "1";
    l += "10";
    l += dtUpdate;
    QObject::connect (
        &webPage, SIGNAL (oneHistoryEvent (const GVHistoryEvent &)),
         this   , SLOT   (oneHistoryEvent (const GVHistoryEvent &)));
    emit status ("Retrieving Inbox...", 0);
    if (!webPage.enqueueWork (GVAW_getInbox, l, this,
            SLOT (getHistoryDone (bool, const QVariantList &))))
    {
        getHistoryDone (false, l);
    }
}//GVHistory::refreshHistory

void
GVHistory::refreshFullInbox ()
{
    CacheDatabase &dbMain = Singletons::getRef().getDBMain ();
    dbMain.setLastInboxUpdate (QDateTime::fromString ("2000-01-01",
                                                      "yyyy-MM-dd"));
    refreshHistory ();
}//GVHistory::refreshFullInbox

void
GVHistory::oneHistoryEvent (const GVHistoryEvent &hevent)
{
    QString strType = modelInbox->type_to_string (hevent.Type);
    if (0 == strType.size ())
    {
        return;
    }

    CacheDatabase &dbMain = Singletons::getRef().getDBMain ();
    dbMain.insertHistory (hevent);
}//GVHistory::oneHistoryEvent

void
GVHistory::getHistoryDone (bool, const QVariantList &)
{
    emit status ("Inbox retrieved. Sorting...", 0);

    GVAccess &webPage = Singletons::getRef().getGVAccess ();
    QObject::disconnect (
        &webPage, SIGNAL (oneHistoryEvent (const GVHistoryEvent &)),
         this   , SLOT   (oneHistoryEvent (const GVHistoryEvent &)));

    prepView ();

    CacheDatabase &dbMain = Singletons::getRef().getDBMain ();
    QDateTime dtUpdate;
    if (dbMain.getLatestInboxEntry (dtUpdate))
    {
        qDebug () << QString ("Latest inbox entry is : %1")
                            .arg (dtUpdate.toString ());
        dbMain.setLastInboxUpdate (dtUpdate);
    }

    emit status ("Inbox ready");
}//GVHistory::getHistoryDone

void
GVHistory::onInboxSelected (const QString &strSelection)
{
    QMutexLocker locker(&mutex);
    if (!bLoggedIn)
    {
        return;
    }

    strSelectedMessages = strSelection.toLower ();
    refreshHistory ();
}//GVHistory::onInboxSelected

void
GVHistory::loginSuccess ()
{
    QMutexLocker locker(&mutex);
    bLoggedIn = true;
}//GVHistory::loginSuccess

void
GVHistory::loggedOut ()
{
    QMutexLocker locker(&mutex);
    bLoggedIn = false;
}//GVHistory::loggedOut
<|endoftext|>