<commit_before>/*
 * Copyright 2017 Google Inc.
 *
 * 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 "configuration.h"

#include <boost/program_options.hpp>
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>

#include <yaml-cpp/yaml.h>

#ifndef AGENT_VERSION
#define AGENT_VERSION 0.0
#endif

// https://gcc.gnu.org/onlinedocs/gcc-7.3.0/cpp/Stringizing.html
#define STRINGIFY_H(x) #x
#define STRINGIFY(x) STRINGIFY_H(x)

namespace google {

namespace {
constexpr const char kConfigFileFlag[] = "config-file";

constexpr const char kDefaultProjectId[] = "";
constexpr const char kDefaultCredentialsFile[] = "";
constexpr const int kMetadataApiDefaultNumThreads = 3;
constexpr const int kMetadataApiDefaultPort = 8000;
constexpr const char kMetadataApiDefaultResourceTypeSeparator[] = ".";
constexpr const int kMetadataReporterDefaultIntervalSeconds = 60;
constexpr const int kMetadataReporterDefaultPurgeDeleted = false;
constexpr const char kMetadataReporterDefaultUserAgent[] =
    "metadata-agent/" STRINGIFY(AGENT_VERSION);
constexpr const char kMetadataIngestionDefaultEndpointFormat[] =
    "https://stackdriver.googleapis.com/v1beta2/projects/{{project_id}}"
    "/resourceMetadata:batchUpdate";
constexpr const int kMetadataIngestionDefaultRequestSizeLimitBytes =
    8*1024*1024;
constexpr const int kMetadataIngestionDefaultRequestSizeLimitCount = 1000;
constexpr const char kMetadataIngestionDefaultRawContentVersion[] = "0.1";
constexpr const int kInstanceUpdaterDefaultIntervalSeconds = 60*60;
constexpr const char kDefaultInstanceResourceType[] =
    "";  // A blank value means "unspecified; detect via environment".
constexpr const int kDockerUpdaterDefaultIntervalSeconds = 0;
constexpr const char kDockerDefaultEndpointHost[] =
    "unix://%2Fvar%2Frun%2Fdocker.sock/";
constexpr const char kDockerDefaultApiVersion[] = "1.23";
constexpr const char kDockerDefaultContainerFilter[] = "limit=30";
constexpr const int kKubernetesUpdaterDefaultIntervalSeconds = 0;
constexpr const char kKubernetesDefaultEndpointHost[] =
    "https://kubernetes.default.svc";
constexpr const char kKubernetesDefaultPodLabelSelector[] = "";
constexpr const char kKubernetesDefaultClusterName[] = "";
constexpr const char kKubernetesDefaultClusterLocation[] = "";
constexpr const char kKubernetesDefaultNodeName[] = "";
constexpr const bool kKubernetesDefaultUseWatch = false;
constexpr const bool kKubernetesDefaultClusterLevelMetadata = false;
constexpr const bool kKubernetesDefaultServiceMetadata = true;
constexpr const char kDefaultInstanceId[] = "";
constexpr const char kDefaultInstanceZone[] = "";
constexpr const char kDefaultHealthCheckFile[] =
    "/var/run/metadata-agent/health/unhealthy";

}

Configuration::Configuration()
    : project_id_(kDefaultProjectId),
      credentials_file_(kDefaultCredentialsFile),
      verbose_logging_(false),
      metadata_api_num_threads_(kMetadataApiDefaultNumThreads),
      metadata_api_port_(kMetadataApiDefaultPort),
      metadata_api_resource_type_separator_(
          kMetadataApiDefaultResourceTypeSeparator),
      metadata_reporter_interval_seconds_(
          kMetadataReporterDefaultIntervalSeconds),
      metadata_reporter_purge_deleted_(
          kMetadataReporterDefaultPurgeDeleted),
      metadata_reporter_user_agent_(
          kMetadataReporterDefaultUserAgent),
      metadata_ingestion_endpoint_format_(
          kMetadataIngestionDefaultEndpointFormat),
      metadata_ingestion_request_size_limit_bytes_(
          kMetadataIngestionDefaultRequestSizeLimitBytes),
      metadata_ingestion_request_size_limit_count_(
          kMetadataIngestionDefaultRequestSizeLimitCount),
      metadata_ingestion_raw_content_version_(
          kMetadataIngestionDefaultRawContentVersion),
      instance_updater_interval_seconds_(
          kInstanceUpdaterDefaultIntervalSeconds),
      instance_resource_type_(kDefaultInstanceResourceType),
      docker_updater_interval_seconds_(kDockerUpdaterDefaultIntervalSeconds),
      docker_endpoint_host_(kDockerDefaultEndpointHost),
      docker_api_version_(kDockerDefaultApiVersion),
      docker_container_filter_(kDockerDefaultContainerFilter),
      kubernetes_updater_interval_seconds_(
          kKubernetesUpdaterDefaultIntervalSeconds),
      kubernetes_endpoint_host_(kKubernetesDefaultEndpointHost),
      kubernetes_pod_label_selector_(kKubernetesDefaultPodLabelSelector),
      kubernetes_cluster_name_(kKubernetesDefaultClusterName),
      kubernetes_cluster_location_(kKubernetesDefaultClusterLocation),
      kubernetes_node_name_(kKubernetesDefaultNodeName),
      kubernetes_use_watch_(kKubernetesDefaultUseWatch),
      kubernetes_cluster_level_metadata_(
          kKubernetesDefaultClusterLevelMetadata),
      kubernetes_service_metadata_(kKubernetesDefaultServiceMetadata),
      instance_id_(kDefaultInstanceId),
      instance_zone_(kDefaultInstanceZone),
      health_check_file_(kDefaultHealthCheckFile) {}

Configuration::Configuration(std::istream& input) : Configuration() {
  ParseConfiguration(input);
}

int Configuration::ParseArguments(int ac, char** av) {
  std::string config_file;
  boost::program_options::options_description flags_desc;
  flags_desc.add_options()
      ("help,h", "Print help message")
      ("version,V", "Print the agent version")
      ("verbose,v", boost::program_options::bool_switch(&verbose_logging_),
           "Enable verbose logging")
      ("option,o",
            boost::program_options::value<std::vector<std::string>>()
                ->composing(),
            "Explicit configuration option, e.g. "
            "-o CredentialsFile=/tmp/token.json "
            "(can be specified multiple times)")
      ;
  boost::program_options::options_description hidden_desc;
  hidden_desc.add_options()
      (kConfigFileFlag,
           boost::program_options::value<std::string>(&config_file)
               ->default_value(""),
           "Configuration file location")
      ;
  boost::program_options::options_description all_desc;
  all_desc.add(flags_desc).add(hidden_desc);
  boost::program_options::positional_options_description positional_desc;
  positional_desc.add(kConfigFileFlag, 1);
  boost::program_options::variables_map flags;
  try {
    boost::program_options::store(
        boost::program_options::command_line_parser(ac, av)
        .options(all_desc).positional(positional_desc).run(), flags);
    boost::program_options::notify(flags);

    if (flags.count("help")) {
      std::cout << flags_desc << std::endl;
      return -1;
    }
    if (flags.count("version")) {
      std::cout << "Stackdriver Metadata Agent v" << STRINGIFY(AGENT_VERSION)
                << std::endl;
      return -1;
    }
    ParseConfigFile(config_file);

    // Command line options override the options provided in the config file.
    if (flags.count("option")) {
      std::stringstream option_stream;
      const std::vector<std::string> options =
          flags["option"].as<std::vector<std::string>>();
      for (const std::string& option : options) {
        std::size_t separator_pos = option.find("=");
        if (separator_pos == std::string::npos) {
          std::cerr << "Invalid option " << option;
          return 1;
        }
        const std::string key = option.substr(0, separator_pos);
        const std::string value =
            option.substr(separator_pos + 1, std::string::npos);
        option_stream << key << ": " << value << "\n";
      }

#ifdef VERBOSE
      LOG(DEBUG) << "Options:\n" << option_stream.str();
#endif
      ParseConfiguration(option_stream);
    }

    return 0;
  } catch (const boost::program_options::error& arg_error) {
    std::cerr << arg_error.what() << std::endl;
    std::cerr << flags_desc << std::endl;
    return 1;
  }
}

void Configuration::ParseConfigFile(const std::string& filename) {
  if (filename.empty()) return;

  std::ifstream input(filename);
  ParseConfiguration(input);
}

void Configuration::ParseConfiguration(std::istream& input) {
  YAML::Node config = YAML::Load(input);
  std::lock_guard<std::mutex> lock(mutex_);
  project_id_ =
      config["ProjectId"].as<std::string>(project_id_);
  credentials_file_ =
      config["CredentialsFile"].as<std::string>(credentials_file_);
  metadata_api_num_threads_ =
      config["MetadataApiNumThreads"].as<int>(metadata_api_num_threads_);
  metadata_api_port_ =
      config["MetadataApiPort"].as<int>(metadata_api_port_);
  metadata_api_resource_type_separator_ =
      config["MetadataApiResourceTypeSeparator"].as<std::string>(
          metadata_api_resource_type_separator_);
  metadata_reporter_interval_seconds_ =
      config["MetadataReporterIntervalSeconds"].as<int>(
          metadata_reporter_interval_seconds_);
  metadata_reporter_purge_deleted_ =
      config["MetadataReporterPurgeDeleted"].as<bool>(
          metadata_reporter_purge_deleted_);
  metadata_reporter_user_agent_ =
      config["MetadataReporterUserAgent"].as<std::string>(
          metadata_reporter_user_agent_);
  metadata_ingestion_endpoint_format_ =
      config["MetadataIngestionEndpointFormat"].as<std::string>(
          metadata_ingestion_endpoint_format_);
  metadata_ingestion_request_size_limit_bytes_ =
      config["MetadataIngestionRequestSizeLimitBytes"].as<int>(
          metadata_ingestion_request_size_limit_bytes_);
  metadata_ingestion_request_size_limit_count_ =
      config["MetadataIngestionRequestSizeLimitCount"].as<int>(
          metadata_ingestion_request_size_limit_count_);
  metadata_ingestion_raw_content_version_ =
      config["MetadataIngestionRawContentVersion"].as<std::string>(
          metadata_ingestion_raw_content_version_);
  instance_updater_interval_seconds_ =
      config["InstanceUpdaterIntervalSeconds"].as<int>(
          instance_updater_interval_seconds_);
  instance_resource_type_ =
      config["InstanceResourceType"].as<std::string>(instance_resource_type_);
  docker_updater_interval_seconds_ =
      config["DockerUpdaterIntervalSeconds"].as<int>(
          docker_updater_interval_seconds_);
  docker_endpoint_host_ =
      config["DockerEndpointHost"].as<std::string>(docker_endpoint_host_);
  docker_api_version_ =
      config["DockerApiVersion"].as<std::string>(docker_api_version_);
  docker_container_filter_ =
      config["DockerContainerFilter"].as<std::string>(
          docker_container_filter_);
  kubernetes_updater_interval_seconds_ =
      config["KubernetesUpdaterIntervalSeconds"].as<int>(
          kubernetes_updater_interval_seconds_);
  kubernetes_endpoint_host_ =
      config["KubernetesEndpointHost"].as<std::string>(
          kubernetes_endpoint_host_);
  kubernetes_pod_label_selector_ =
      config["KubernetesPodLabelSelector"].as<std::string>(
          kubernetes_pod_label_selector_);
  kubernetes_cluster_name_ =
      config["KubernetesClusterName"].as<std::string>(
          kubernetes_cluster_name_);
  kubernetes_cluster_location_ =
      config["KubernetesClusterLocation"].as<std::string>(
          kubernetes_cluster_location_);
  kubernetes_node_name_ =
      config["KubernetesNodeName"].as<std::string>(kubernetes_node_name_);
  kubernetes_use_watch_ =
      config["KubernetesUseWatch"].as<bool>(kubernetes_use_watch_);
  kubernetes_cluster_level_metadata_ =
      config["KubernetesClusterLevelMetadata"].as<bool>(
          kubernetes_cluster_level_metadata_);
  kubernetes_service_metadata_ =
      config["KubernetesServiceMetadata"].as<bool>(
          kubernetes_service_metadata_);
  instance_id_ =
      config["InstanceId"].as<std::string>(instance_id_);
  instance_zone_ =
      config["InstanceZone"].as<std::string>(instance_zone_);
  health_check_file_ =
      config["HealthCheckFile"].as<std::string>(health_check_file_);
}

}  // google

#undef STRINGIFY
#undef STRINGIFY_H
<commit_msg>In configuration.cc, change LOG(DEBUG) to std::cout. (#144)<commit_after>/*
 * Copyright 2017 Google Inc.
 *
 * 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 "configuration.h"

#include <boost/program_options.hpp>
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>

#include <yaml-cpp/yaml.h>

#ifndef AGENT_VERSION
#define AGENT_VERSION 0.0
#endif

// https://gcc.gnu.org/onlinedocs/gcc-7.3.0/cpp/Stringizing.html
#define STRINGIFY_H(x) #x
#define STRINGIFY(x) STRINGIFY_H(x)

namespace google {

namespace {
constexpr const char kConfigFileFlag[] = "config-file";

constexpr const char kDefaultProjectId[] = "";
constexpr const char kDefaultCredentialsFile[] = "";
constexpr const int kMetadataApiDefaultNumThreads = 3;
constexpr const int kMetadataApiDefaultPort = 8000;
constexpr const char kMetadataApiDefaultResourceTypeSeparator[] = ".";
constexpr const int kMetadataReporterDefaultIntervalSeconds = 60;
constexpr const int kMetadataReporterDefaultPurgeDeleted = false;
constexpr const char kMetadataReporterDefaultUserAgent[] =
    "metadata-agent/" STRINGIFY(AGENT_VERSION);
constexpr const char kMetadataIngestionDefaultEndpointFormat[] =
    "https://stackdriver.googleapis.com/v1beta2/projects/{{project_id}}"
    "/resourceMetadata:batchUpdate";
constexpr const int kMetadataIngestionDefaultRequestSizeLimitBytes =
    8*1024*1024;
constexpr const int kMetadataIngestionDefaultRequestSizeLimitCount = 1000;
constexpr const char kMetadataIngestionDefaultRawContentVersion[] = "0.1";
constexpr const int kInstanceUpdaterDefaultIntervalSeconds = 60*60;
constexpr const char kDefaultInstanceResourceType[] =
    "";  // A blank value means "unspecified; detect via environment".
constexpr const int kDockerUpdaterDefaultIntervalSeconds = 0;
constexpr const char kDockerDefaultEndpointHost[] =
    "unix://%2Fvar%2Frun%2Fdocker.sock/";
constexpr const char kDockerDefaultApiVersion[] = "1.23";
constexpr const char kDockerDefaultContainerFilter[] = "limit=30";
constexpr const int kKubernetesUpdaterDefaultIntervalSeconds = 0;
constexpr const char kKubernetesDefaultEndpointHost[] =
    "https://kubernetes.default.svc";
constexpr const char kKubernetesDefaultPodLabelSelector[] = "";
constexpr const char kKubernetesDefaultClusterName[] = "";
constexpr const char kKubernetesDefaultClusterLocation[] = "";
constexpr const char kKubernetesDefaultNodeName[] = "";
constexpr const bool kKubernetesDefaultUseWatch = false;
constexpr const bool kKubernetesDefaultClusterLevelMetadata = false;
constexpr const bool kKubernetesDefaultServiceMetadata = true;
constexpr const char kDefaultInstanceId[] = "";
constexpr const char kDefaultInstanceZone[] = "";
constexpr const char kDefaultHealthCheckFile[] =
    "/var/run/metadata-agent/health/unhealthy";

}

Configuration::Configuration()
    : project_id_(kDefaultProjectId),
      credentials_file_(kDefaultCredentialsFile),
      verbose_logging_(false),
      metadata_api_num_threads_(kMetadataApiDefaultNumThreads),
      metadata_api_port_(kMetadataApiDefaultPort),
      metadata_api_resource_type_separator_(
          kMetadataApiDefaultResourceTypeSeparator),
      metadata_reporter_interval_seconds_(
          kMetadataReporterDefaultIntervalSeconds),
      metadata_reporter_purge_deleted_(
          kMetadataReporterDefaultPurgeDeleted),
      metadata_reporter_user_agent_(
          kMetadataReporterDefaultUserAgent),
      metadata_ingestion_endpoint_format_(
          kMetadataIngestionDefaultEndpointFormat),
      metadata_ingestion_request_size_limit_bytes_(
          kMetadataIngestionDefaultRequestSizeLimitBytes),
      metadata_ingestion_request_size_limit_count_(
          kMetadataIngestionDefaultRequestSizeLimitCount),
      metadata_ingestion_raw_content_version_(
          kMetadataIngestionDefaultRawContentVersion),
      instance_updater_interval_seconds_(
          kInstanceUpdaterDefaultIntervalSeconds),
      instance_resource_type_(kDefaultInstanceResourceType),
      docker_updater_interval_seconds_(kDockerUpdaterDefaultIntervalSeconds),
      docker_endpoint_host_(kDockerDefaultEndpointHost),
      docker_api_version_(kDockerDefaultApiVersion),
      docker_container_filter_(kDockerDefaultContainerFilter),
      kubernetes_updater_interval_seconds_(
          kKubernetesUpdaterDefaultIntervalSeconds),
      kubernetes_endpoint_host_(kKubernetesDefaultEndpointHost),
      kubernetes_pod_label_selector_(kKubernetesDefaultPodLabelSelector),
      kubernetes_cluster_name_(kKubernetesDefaultClusterName),
      kubernetes_cluster_location_(kKubernetesDefaultClusterLocation),
      kubernetes_node_name_(kKubernetesDefaultNodeName),
      kubernetes_use_watch_(kKubernetesDefaultUseWatch),
      kubernetes_cluster_level_metadata_(
          kKubernetesDefaultClusterLevelMetadata),
      kubernetes_service_metadata_(kKubernetesDefaultServiceMetadata),
      instance_id_(kDefaultInstanceId),
      instance_zone_(kDefaultInstanceZone),
      health_check_file_(kDefaultHealthCheckFile) {}

Configuration::Configuration(std::istream& input) : Configuration() {
  ParseConfiguration(input);
}

int Configuration::ParseArguments(int ac, char** av) {
  std::string config_file;
  boost::program_options::options_description flags_desc;
  flags_desc.add_options()
      ("help,h", "Print help message")
      ("version,V", "Print the agent version")
      ("verbose,v", boost::program_options::bool_switch(&verbose_logging_),
           "Enable verbose logging")
      ("option,o",
            boost::program_options::value<std::vector<std::string>>()
                ->composing(),
            "Explicit configuration option, e.g. "
            "-o CredentialsFile=/tmp/token.json "
            "(can be specified multiple times)")
      ;
  boost::program_options::options_description hidden_desc;
  hidden_desc.add_options()
      (kConfigFileFlag,
           boost::program_options::value<std::string>(&config_file)
               ->default_value(""),
           "Configuration file location")
      ;
  boost::program_options::options_description all_desc;
  all_desc.add(flags_desc).add(hidden_desc);
  boost::program_options::positional_options_description positional_desc;
  positional_desc.add(kConfigFileFlag, 1);
  boost::program_options::variables_map flags;
  try {
    boost::program_options::store(
        boost::program_options::command_line_parser(ac, av)
        .options(all_desc).positional(positional_desc).run(), flags);
    boost::program_options::notify(flags);

    if (flags.count("help")) {
      std::cout << flags_desc << std::endl;
      return -1;
    }
    if (flags.count("version")) {
      std::cout << "Stackdriver Metadata Agent v" << STRINGIFY(AGENT_VERSION)
                << std::endl;
      return -1;
    }
    ParseConfigFile(config_file);

    // Command line options override the options provided in the config file.
    if (flags.count("option")) {
      std::stringstream option_stream;
      const std::vector<std::string> options =
          flags["option"].as<std::vector<std::string>>();
      for (const std::string& option : options) {
        std::size_t separator_pos = option.find("=");
        if (separator_pos == std::string::npos) {
          std::cerr << "Invalid option " << option;
          return 1;
        }
        const std::string key = option.substr(0, separator_pos);
        const std::string value =
            option.substr(separator_pos + 1, std::string::npos);
        option_stream << key << ": " << value << "\n";
      }

#ifdef VERBOSE
      std::cout << "Options:\n" << option_stream.str() << std::endl;
#endif
      ParseConfiguration(option_stream);
    }

    return 0;
  } catch (const boost::program_options::error& arg_error) {
    std::cerr << arg_error.what() << std::endl;
    std::cerr << flags_desc << std::endl;
    return 1;
  }
}

void Configuration::ParseConfigFile(const std::string& filename) {
  if (filename.empty()) return;

  std::ifstream input(filename);
  ParseConfiguration(input);
}

void Configuration::ParseConfiguration(std::istream& input) {
  YAML::Node config = YAML::Load(input);
  std::lock_guard<std::mutex> lock(mutex_);
  project_id_ =
      config["ProjectId"].as<std::string>(project_id_);
  credentials_file_ =
      config["CredentialsFile"].as<std::string>(credentials_file_);
  metadata_api_num_threads_ =
      config["MetadataApiNumThreads"].as<int>(metadata_api_num_threads_);
  metadata_api_port_ =
      config["MetadataApiPort"].as<int>(metadata_api_port_);
  metadata_api_resource_type_separator_ =
      config["MetadataApiResourceTypeSeparator"].as<std::string>(
          metadata_api_resource_type_separator_);
  metadata_reporter_interval_seconds_ =
      config["MetadataReporterIntervalSeconds"].as<int>(
          metadata_reporter_interval_seconds_);
  metadata_reporter_purge_deleted_ =
      config["MetadataReporterPurgeDeleted"].as<bool>(
          metadata_reporter_purge_deleted_);
  metadata_reporter_user_agent_ =
      config["MetadataReporterUserAgent"].as<std::string>(
          metadata_reporter_user_agent_);
  metadata_ingestion_endpoint_format_ =
      config["MetadataIngestionEndpointFormat"].as<std::string>(
          metadata_ingestion_endpoint_format_);
  metadata_ingestion_request_size_limit_bytes_ =
      config["MetadataIngestionRequestSizeLimitBytes"].as<int>(
          metadata_ingestion_request_size_limit_bytes_);
  metadata_ingestion_request_size_limit_count_ =
      config["MetadataIngestionRequestSizeLimitCount"].as<int>(
          metadata_ingestion_request_size_limit_count_);
  metadata_ingestion_raw_content_version_ =
      config["MetadataIngestionRawContentVersion"].as<std::string>(
          metadata_ingestion_raw_content_version_);
  instance_updater_interval_seconds_ =
      config["InstanceUpdaterIntervalSeconds"].as<int>(
          instance_updater_interval_seconds_);
  instance_resource_type_ =
      config["InstanceResourceType"].as<std::string>(instance_resource_type_);
  docker_updater_interval_seconds_ =
      config["DockerUpdaterIntervalSeconds"].as<int>(
          docker_updater_interval_seconds_);
  docker_endpoint_host_ =
      config["DockerEndpointHost"].as<std::string>(docker_endpoint_host_);
  docker_api_version_ =
      config["DockerApiVersion"].as<std::string>(docker_api_version_);
  docker_container_filter_ =
      config["DockerContainerFilter"].as<std::string>(
          docker_container_filter_);
  kubernetes_updater_interval_seconds_ =
      config["KubernetesUpdaterIntervalSeconds"].as<int>(
          kubernetes_updater_interval_seconds_);
  kubernetes_endpoint_host_ =
      config["KubernetesEndpointHost"].as<std::string>(
          kubernetes_endpoint_host_);
  kubernetes_pod_label_selector_ =
      config["KubernetesPodLabelSelector"].as<std::string>(
          kubernetes_pod_label_selector_);
  kubernetes_cluster_name_ =
      config["KubernetesClusterName"].as<std::string>(
          kubernetes_cluster_name_);
  kubernetes_cluster_location_ =
      config["KubernetesClusterLocation"].as<std::string>(
          kubernetes_cluster_location_);
  kubernetes_node_name_ =
      config["KubernetesNodeName"].as<std::string>(kubernetes_node_name_);
  kubernetes_use_watch_ =
      config["KubernetesUseWatch"].as<bool>(kubernetes_use_watch_);
  kubernetes_cluster_level_metadata_ =
      config["KubernetesClusterLevelMetadata"].as<bool>(
          kubernetes_cluster_level_metadata_);
  kubernetes_service_metadata_ =
      config["KubernetesServiceMetadata"].as<bool>(
          kubernetes_service_metadata_);
  instance_id_ =
      config["InstanceId"].as<std::string>(instance_id_);
  instance_zone_ =
      config["InstanceZone"].as<std::string>(instance_zone_);
  health_check_file_ =
      config["HealthCheckFile"].as<std::string>(health_check_file_);
}

}  // google

#undef STRINGIFY
#undef STRINGIFY_H
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015-2017 Nagisa Sekiguchi
 *
 * 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.
 */

#ifndef YDSH_MISC_LEXER_BASE_HPP
#define YDSH_MISC_LEXER_BASE_HPP

#include <cstdio>
#include <cstring>
#include <cassert>
#include <string>
#include <type_traits>
#include <vector>
#include <memory>
#include <algorithm>

#include "unicode.hpp"
#include "noncopyable.h"
#include "token.hpp"

namespace ydsh {
namespace parser_base {

namespace __detail {

/**
 * base lexer for re2c
 */
template<bool T>
class LexerBase {
protected:
    static_assert(T, "not allowed instantiation");

    /**
     * may be null, if input source is string. not closed it.
     * must be binary mode.
     */
    FILE *fp{nullptr};

    unsigned int bufSize{0};

    /**
     * must terminate null character.
     */
    unsigned char *buf{nullptr};

    /**
     * current reading pointer of buf.
     */
    unsigned char *cursor{nullptr};

    /**
     * limit of buf.
     */
    unsigned char *limit{nullptr};

    /**
     * for backtracking.
     */
    unsigned char *marker{nullptr};

    /**
     * for trailing context
     */
    unsigned char *ctxMarker{nullptr};

    /**
     * if fp is null or fp reach EOF, it it true.
     */
    bool endOfFile{false};

    /**
     * if true, reach end of string. nextToken() always return EOS.
     */
    bool endOfString{false};

    static constexpr unsigned int DEFAULT_SIZE = 256;
    static constexpr int DEFAULT_READ_SIZE = 128;

private:
    LexerBase() = default;

public:
    NON_COPYABLE(LexerBase);

    /**
     * FILE must be opened with binary mode.
     * insert newline if not terminated by it.
     */

    /**
     *
     * @param fp
     * must be opened with binary mode.
     * @return
     */
    explicit LexerBase(FILE *fp);

    /**
     *
     * @param src
     * must be null terminated.
     * @return
     */
    explicit LexerBase(const char *src) : LexerBase(src, strlen(src)) {}

    /**
     *
     * @param data
     * @param size
     * @return
     */
    LexerBase(const char *data, unsigned int size);

protected:
    ~LexerBase() {
        delete[] this->buf;
    }

public:
    /**
     * get current reading position.
     */
    unsigned int getPos() const {
        return this->cursor - this->buf;
    }

    /**
     * used size of buf. must be this->getUsedSize() <= this->getBufSize().
     */
    unsigned int getUsedSize() const {
        return this->limit - this->buf + 1;
    }

    bool withinRange(Token token) const {
        return token.pos < this->getUsedSize()
               && token.pos + token.size <= this->getUsedSize();
    }

    /**
     * get text of token.
     */
    std::string toTokenText(Token token) const {
        assert(this->withinRange(token));
        return std::string((char *) (this->buf + token.pos), token.size);
    }

    /**
     * buf size must be equivalent to base.size
     */
    void copyTokenText(Token token, char *buf) const {
        assert(this->withinRange(token));
        memcpy(buf, (char *)this->buf + token.pos, token.size);
    }

    bool startsWith(Token token, char ch) const {
        assert(this->withinRange(token));
        return this->buf[token.pos] == ch;
    }

    bool equals(Token token, const char *str) const {
        assert(this->withinRange(token));
        return strlen(str) == token.size &&
                memcmp(this->buf + token.pos, str, token.size) == 0;
    }

    /**
     * shift EOS token to left.
     * @param token
     * @return
     * if token is EOS, skip redundant white spaces and shift to left.
     * otherwise, return token.
     */
    Token shiftEOS(Token token) const;

    /**
     * get line token which token belongs to.
     */
    Token getLineToken(Token token) const;

    std::string formatLineMarker(Token lineToken, Token token) const;

private:
    /**
     * if this->usedSize + needSize > this->maxSize, expand buf.
     */
    void expandBuf(unsigned int needSize);

    /**
     * swap new buffer and old one, after swapping, update some pointers and bufSize
     */
    void swapBuffer(unsigned char *&newBuf, unsigned int &newSize);

    unsigned int toCodePoint(unsigned int offset, int &code) const {
        return UnicodeUtil::utf8ToCodePoint((char *)(this->buf + offset), this->getUsedSize() - offset, code);
    }

protected:
    /**
     * fill buffer. called from this->nextToken().
     */
    bool fill(int n);
};

// #######################
// ##     LexerBase     ##
// #######################

template<bool T>
LexerBase<T>::LexerBase(FILE *fp) : LexerBase() {
    this->fp = fp;
    this->bufSize = DEFAULT_SIZE;
    this->buf = new unsigned char[this->bufSize];

    this->cursor = this->buf;
    this->limit = this->buf;
}

template<bool T>
LexerBase<T>::LexerBase(const char *data, unsigned int size) : LexerBase() {
    const bool insertingNewline = size == 0 || data[size - 1] != '\n';
    this->bufSize = size + 1 + (insertingNewline ? 1 : 0);

    this->buf = new unsigned char[this->bufSize];
    memcpy(this->buf, data, sizeof(unsigned char) * size);
    if(insertingNewline) {
        this->buf[this->bufSize - 2] = '\n';
    }
    this->buf[this->bufSize - 1] = '\0';

    this->cursor = this->buf;
    this->limit = this->buf + this->bufSize - 1;
    this->endOfFile = true;
}

template <bool T>
Token LexerBase<T>::shiftEOS(Token token) const {
    if(token.size == 0) {
        unsigned int startIndex = token.pos;
        for(; startIndex > 0; startIndex--) {
            char ch = this->buf[startIndex];
            if(ch == ' ' || ch == '\t' || ch == '\n' || ch == '\000') {
                continue;
            }
            if(ch == '\\' && startIndex + 1 < token.pos) {
                char next = this->buf[startIndex + 1];
                if(next == ' ' || next == '\t' || next == '\n') {
                    continue;
                }
            }
            break;
        }
        token.pos = startIndex;
    }
    return token;
}

template <bool T>
Token LexerBase<T>::getLineToken(Token token) const {
    assert(this->withinRange(token));

    // find start index of line.
    long startIndex = token.pos;
    for(; startIndex > -1; startIndex--) {
        if(this->buf[startIndex] == '\n') {
            startIndex += (startIndex == token.pos) ? 0 : 1;
            break;
        }
    }
    if(startIndex == -1) {
        startIndex = 0;
    }

    // find stop index of line
    unsigned int stopIndex = token.pos + token.size;
    if(token.size > 0) {
        for(unsigned int usedSize = this->getUsedSize(); stopIndex < usedSize; stopIndex++) {
            if(this->buf[stopIndex] == '\n') {
                break;
            }
        }
    } else {
        stopIndex++;
    }

    assert(startIndex > -1);
    Token lineToken;
    lineToken.pos = static_cast<unsigned int>(startIndex);
    lineToken.size = stopIndex - static_cast<unsigned int>(startIndex);
    return lineToken;
}

template<bool T>
std::string LexerBase<T>::formatLineMarker(Token lineToken, Token token) const {
    assert(lineToken.pos <= token.pos);

    std::string marker;
    for(unsigned int i = lineToken.pos; i < token.pos;) {
        int code = 0;
        i += this->toCodePoint(i, code);
        if(code < 0) {
            return marker;
        }
        if(code == '\t' || code == '\n') {
            marker += static_cast<char>(code);
            continue;
        }
        int width = UnicodeUtil::localeAwareWidth(code);
        if(width == 1) {
            marker += " ";
        } else if(width == 2) {
            marker += "  ";
        }
    }
    const unsigned int stopPos = token.size + token.pos;
    if(token.size == 0) {
        marker += "  ^";
    }
    for(unsigned int i = token.pos; i < stopPos;) {
        unsigned int prev = i;
        int code = 0;
        i += this->toCodePoint(i, code);
        if(code < 0) {
            return marker;
        }
        if(code == '\t' || code == '\n') {
            marker += static_cast<char>(code);
            continue;
        }
        int width = UnicodeUtil::localeAwareWidth(code);
        if(width == 1) {
            marker += (prev == token.pos ? "^" : "~");
        } else if(width == 2) {
            marker += (prev == token.pos ? "^~" : "~~");
        }
    }
    return marker;
}

template<bool T>
void LexerBase<T>::expandBuf(unsigned int needSize) {
    unsigned int usedSize = this->getUsedSize();
    unsigned int size = usedSize + needSize;
    if(size > this->bufSize) {
        unsigned int newSize = this->bufSize;
        do {
            newSize += (newSize >> 1);
        } while(newSize < size);

        // swap to new buffer
        unsigned char *newBuf = new unsigned char[newSize];
        memcpy(newBuf, this->buf, sizeof(unsigned char) * usedSize);
        this->swapBuffer(newBuf, newSize);
        delete[] newBuf;
    }
}

template <bool T>
void LexerBase<T>::swapBuffer(unsigned char *&newBuf, unsigned int &newSize) {
    // save position
    const unsigned int usedSize = this->getUsedSize();
    const unsigned int pos = this->getPos();
    const unsigned int markerPos = this->marker - this->buf;
    const unsigned int ctxMarkerPos = this->ctxMarker - this->buf;

    // swap
    std::swap(this->buf, newBuf);
    std::swap(this->bufSize, newSize);

    // restore position
    this->cursor = this->buf + pos;
    this->limit = this->buf + usedSize - 1;
    this->marker = this->buf + markerPos;
    this->ctxMarker = this->buf + ctxMarkerPos;
}

template<bool T>
bool LexerBase<T>::fill(int n) {
    if(this->endOfString && this->limit - this->cursor <= 0) {
        return false;
    }

    if(!this->endOfFile) {
        int needSize = n - (this->limit - this->cursor);
        assert(needSize > -1);
        needSize = (needSize > DEFAULT_READ_SIZE) ? needSize : DEFAULT_READ_SIZE;
        this->expandBuf(needSize);
        int readSize = fread(this->limit, sizeof(unsigned char), needSize, this->fp);
        this->limit += readSize;
        *this->limit = '\0';
        if(readSize < needSize) {
            this->endOfFile = true;
            if(*(this->limit - 1) != '\n') {    // terminated newline
                this->expandBuf(1);
                *this->limit = '\n';
                this->limit += 1;
                *this->limit = '\0';
            }
        }
    }
    return true;
}

} // namespace __detail

using LexerBase = __detail::LexerBase<true>;

} // namespace parser_base
} // namespace ydsh

#endif //YDSH_LEXER_BASE_HPP
<commit_msg>LexerBase is move assignable<commit_after>/*
 * Copyright (C) 2015-2017 Nagisa Sekiguchi
 *
 * 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.
 */

#ifndef YDSH_MISC_LEXER_BASE_HPP
#define YDSH_MISC_LEXER_BASE_HPP

#include <cstdio>
#include <cstring>
#include <cassert>
#include <string>
#include <type_traits>
#include <vector>
#include <memory>
#include <algorithm>

#include "unicode.hpp"
#include "noncopyable.h"
#include "token.hpp"

namespace ydsh {
namespace parser_base {

namespace __detail {

/**
 * base lexer for re2c
 */
template<bool T>
class LexerBase {
protected:
    static_assert(T, "not allowed instantiation");

    /**
     * may be null, if input source is string. not closed it.
     * must be binary mode.
     */
    FILE *fp{nullptr};

    unsigned int bufSize{0};

    /**
     * must terminate null character.
     */
    unsigned char *buf{nullptr};

    /**
     * current reading pointer of buf.
     */
    unsigned char *cursor{nullptr};

    /**
     * limit of buf.
     */
    unsigned char *limit{nullptr};

    /**
     * for backtracking.
     */
    unsigned char *marker{nullptr};

    /**
     * for trailing context
     */
    unsigned char *ctxMarker{nullptr};

    /**
     * if fp is null or fp reach EOF, it it true.
     */
    bool endOfFile{false};

    /**
     * if true, reach end of string. nextToken() always return EOS.
     */
    bool endOfString{false};

    static constexpr unsigned int DEFAULT_SIZE = 256;
    static constexpr int DEFAULT_READ_SIZE = 128;

protected:
    LexerBase() = default;

    ~LexerBase() {
        delete[] this->buf;
    }

public:
    NON_COPYABLE(LexerBase);

    LexerBase(LexerBase &&lex) noexcept :
            fp(lex.fp), bufSize(lex.bufSize), buf(lex.buf), cursor(lex.cursor),
            limit(lex.limit), marker(lex.marker), ctxMarker(lex.ctxMarker),
            endOfFile(lex.endOfFile), endOfString(lex.endOfString) {
        lex.buf = nullptr;
    }

    LexerBase &operator=(LexerBase &&lex) {
        auto tmp(std::move(lex));
        this->swap(tmp);
        return *this;
    }

    void swap(LexerBase &lex) {
        std::swap(this->fp, lex.fp);
        std::swap(this->bufSize, lex.bufSize);
        std::swap(this->buf, lex.buf);
        std::swap(this->cursor, lex.cursor);
        std::swap(this->limit, lex.limit);
        std::swap(this->marker, lex.marker);
        std::swap(this->ctxMarker, lex.ctxMarker);
        std::swap(this->endOfFile, lex.endOfFile);
        std::swap(this->endOfString, lex.endOfString);
    }

    /**
     * FILE must be opened with binary mode.
     * insert newline if not terminated by it.
     */

    /**
     *
     * @param fp
     * must be opened with binary mode.
     * @return
     */
    explicit LexerBase(FILE *fp);

    /**
     *
     * @param src
     * must be null terminated.
     * @return
     */
    explicit LexerBase(const char *src) : LexerBase(src, strlen(src)) {}

    /**
     *
     * @param data
     * @param size
     * @return
     */
    LexerBase(const char *data, unsigned int size);

    /**
     * get current reading position.
     */
    unsigned int getPos() const {
        return this->cursor - this->buf;
    }

    /**
     * used size of buf. must be this->getUsedSize() <= this->getBufSize().
     */
    unsigned int getUsedSize() const {
        return this->limit - this->buf + 1;
    }

    bool withinRange(Token token) const {
        return token.pos < this->getUsedSize()
               && token.pos + token.size <= this->getUsedSize();
    }

    /**
     * get text of token.
     */
    std::string toTokenText(Token token) const {
        assert(this->withinRange(token));
        return std::string((char *) (this->buf + token.pos), token.size);
    }

    /**
     * buf size must be equivalent to base.size
     */
    void copyTokenText(Token token, char *buf) const {
        assert(this->withinRange(token));
        memcpy(buf, (char *)this->buf + token.pos, token.size);
    }

    bool startsWith(Token token, char ch) const {
        assert(this->withinRange(token));
        return this->buf[token.pos] == ch;
    }

    bool equals(Token token, const char *str) const {
        assert(this->withinRange(token));
        return strlen(str) == token.size &&
                memcmp(this->buf + token.pos, str, token.size) == 0;
    }

    /**
     * shift EOS token to left.
     * @param token
     * @return
     * if token is EOS, skip redundant white spaces and shift to left.
     * otherwise, return token.
     */
    Token shiftEOS(Token token) const;

    /**
     * get line token which token belongs to.
     */
    Token getLineToken(Token token) const;

    std::string formatLineMarker(Token lineToken, Token token) const;

private:
    /**
     * if this->usedSize + needSize > this->maxSize, expand buf.
     */
    void expandBuf(unsigned int needSize);

    /**
     * swap new buffer and old one, after swapping, update some pointers and bufSize
     */
    void swapBuffer(unsigned char *&newBuf, unsigned int &newSize);

    unsigned int toCodePoint(unsigned int offset, int &code) const {
        return UnicodeUtil::utf8ToCodePoint((char *)(this->buf + offset), this->getUsedSize() - offset, code);
    }

protected:
    /**
     * fill buffer. called from this->nextToken().
     */
    bool fill(int n);
};

// #######################
// ##     LexerBase     ##
// #######################

template<bool T>
LexerBase<T>::LexerBase(FILE *fp) : LexerBase() {
    this->fp = fp;
    this->bufSize = DEFAULT_SIZE;
    this->buf = new unsigned char[this->bufSize];

    this->cursor = this->buf;
    this->limit = this->buf;
}

template<bool T>
LexerBase<T>::LexerBase(const char *data, unsigned int size) : LexerBase() {
    const bool insertingNewline = size == 0 || data[size - 1] != '\n';
    this->bufSize = size + 1 + (insertingNewline ? 1 : 0);

    this->buf = new unsigned char[this->bufSize];
    memcpy(this->buf, data, sizeof(unsigned char) * size);
    if(insertingNewline) {
        this->buf[this->bufSize - 2] = '\n';
    }
    this->buf[this->bufSize - 1] = '\0';

    this->cursor = this->buf;
    this->limit = this->buf + this->bufSize - 1;
    this->endOfFile = true;
}

template <bool T>
Token LexerBase<T>::shiftEOS(Token token) const {
    if(token.size == 0) {
        unsigned int startIndex = token.pos;
        for(; startIndex > 0; startIndex--) {
            char ch = this->buf[startIndex];
            if(ch == ' ' || ch == '\t' || ch == '\n' || ch == '\000') {
                continue;
            }
            if(ch == '\\' && startIndex + 1 < token.pos) {
                char next = this->buf[startIndex + 1];
                if(next == ' ' || next == '\t' || next == '\n') {
                    continue;
                }
            }
            break;
        }
        token.pos = startIndex;
    }
    return token;
}

template <bool T>
Token LexerBase<T>::getLineToken(Token token) const {
    assert(this->withinRange(token));

    // find start index of line.
    long startIndex = token.pos;
    for(; startIndex > -1; startIndex--) {
        if(this->buf[startIndex] == '\n') {
            startIndex += (startIndex == token.pos) ? 0 : 1;
            break;
        }
    }
    if(startIndex == -1) {
        startIndex = 0;
    }

    // find stop index of line
    unsigned int stopIndex = token.pos + token.size;
    if(token.size > 0) {
        for(unsigned int usedSize = this->getUsedSize(); stopIndex < usedSize; stopIndex++) {
            if(this->buf[stopIndex] == '\n') {
                break;
            }
        }
    } else {
        stopIndex++;
    }

    assert(startIndex > -1);
    Token lineToken;
    lineToken.pos = static_cast<unsigned int>(startIndex);
    lineToken.size = stopIndex - static_cast<unsigned int>(startIndex);
    return lineToken;
}

template<bool T>
std::string LexerBase<T>::formatLineMarker(Token lineToken, Token token) const {
    assert(lineToken.pos <= token.pos);

    std::string marker;
    for(unsigned int i = lineToken.pos; i < token.pos;) {
        int code = 0;
        i += this->toCodePoint(i, code);
        if(code < 0) {
            return marker;
        }
        if(code == '\t' || code == '\n') {
            marker += static_cast<char>(code);
            continue;
        }
        int width = UnicodeUtil::localeAwareWidth(code);
        if(width == 1) {
            marker += " ";
        } else if(width == 2) {
            marker += "  ";
        }
    }
    const unsigned int stopPos = token.size + token.pos;
    if(token.size == 0) {
        marker += "  ^";
    }
    for(unsigned int i = token.pos; i < stopPos;) {
        unsigned int prev = i;
        int code = 0;
        i += this->toCodePoint(i, code);
        if(code < 0) {
            return marker;
        }
        if(code == '\t' || code == '\n') {
            marker += static_cast<char>(code);
            continue;
        }
        int width = UnicodeUtil::localeAwareWidth(code);
        if(width == 1) {
            marker += (prev == token.pos ? "^" : "~");
        } else if(width == 2) {
            marker += (prev == token.pos ? "^~" : "~~");
        }
    }
    return marker;
}

template<bool T>
void LexerBase<T>::expandBuf(unsigned int needSize) {
    unsigned int usedSize = this->getUsedSize();
    unsigned int size = usedSize + needSize;
    if(size > this->bufSize) {
        unsigned int newSize = this->bufSize;
        do {
            newSize += (newSize >> 1);
        } while(newSize < size);

        // swap to new buffer
        unsigned char *newBuf = new unsigned char[newSize];
        memcpy(newBuf, this->buf, sizeof(unsigned char) * usedSize);
        this->swapBuffer(newBuf, newSize);
        delete[] newBuf;
    }
}

template <bool T>
void LexerBase<T>::swapBuffer(unsigned char *&newBuf, unsigned int &newSize) {
    // save position
    const unsigned int usedSize = this->getUsedSize();
    const unsigned int pos = this->getPos();
    const unsigned int markerPos = this->marker - this->buf;
    const unsigned int ctxMarkerPos = this->ctxMarker - this->buf;

    // swap
    std::swap(this->buf, newBuf);
    std::swap(this->bufSize, newSize);

    // restore position
    this->cursor = this->buf + pos;
    this->limit = this->buf + usedSize - 1;
    this->marker = this->buf + markerPos;
    this->ctxMarker = this->buf + ctxMarkerPos;
}

template<bool T>
bool LexerBase<T>::fill(int n) {
    if(this->endOfString && this->limit - this->cursor <= 0) {
        return false;
    }

    if(!this->endOfFile) {
        int needSize = n - (this->limit - this->cursor);
        assert(needSize > -1);
        needSize = (needSize > DEFAULT_READ_SIZE) ? needSize : DEFAULT_READ_SIZE;
        this->expandBuf(needSize);
        int readSize = fread(this->limit, sizeof(unsigned char), needSize, this->fp);
        this->limit += readSize;
        *this->limit = '\0';
        if(readSize < needSize) {
            this->endOfFile = true;
            if(*(this->limit - 1) != '\n') {    // terminated newline
                this->expandBuf(1);
                *this->limit = '\n';
                this->limit += 1;
                *this->limit = '\0';
            }
        }
    }
    return true;
}

} // namespace __detail

using LexerBase = __detail::LexerBase<true>;

} // namespace parser_base
} // namespace ydsh

#endif //YDSH_LEXER_BASE_HPP
<|endoftext|>


<commit_before>#include "input.h"

InputHandler::InputHandler(sf::Window* Window, Player* Player, Renderer* Renderer) : app(Window), player(Player), renderer(Renderer) {
    app->ShowMouseCursor(false);
}

bool fullscreen = false;
void InputHandler::handleEvent(sf::Event Event) {
    // Close window : exit
    if (Event.Type == sf::Event::Closed)
        app->Close();

    if(Event.Type == sf::Event::KeyPressed) {
        switch(Event.Key.Code) {
            // Escape key : exit
            case sf::Key::Escape:
                app->Close();
                break;
            // Spacebar : jump
            case sf::Key::Space:
                player->Jump();
                break;
            // F5 : regenerate terrain
            case sf::Key::F5:
                renderer->terrain.Regenerate();
                break;
            // F11 : toggle fullscreen
            case sf::Key::F11:
                toggleFullscreen();
                break;
        }
    }

    // Resize event : adjust viewport
    if (Event.Type == sf::Event::Resized)
        glViewport(0, 0, Event.Size.Width, Event.Size.Height);
}

float ElapsedTime;
float mouseDeltaX, mouseDeltaY;

void InputHandler::handleEvents() {
    const sf::Input& Input = app->GetInput();

    // Constant movement speed
    ElapsedTime = Clock.GetElapsedTime();
    Clock.Reset();

    // Handle held keys
    if ((Input.IsKeyDown(sf::Key::S))) player->Forward(-ElapsedTime);
    if ((Input.IsKeyDown(sf::Key::W))) player->Forward( ElapsedTime);
    if ((Input.IsKeyDown(sf::Key::D))) player->Strafe(-ElapsedTime);
    if ((Input.IsKeyDown(sf::Key::A))) player->Strafe( ElapsedTime);
    if ((Input.IsKeyDown(sf::Key::Z))) player->Speed++;
    if ((Input.IsKeyDown(sf::Key::X))) player->Speed--;

    // Handle other events
    sf::Event Event;
    while (app->GetEvent(Event))
    {
        handleEvent(Event);
    }

    // Rotate view based on mouse movement
    mouseDeltaX = Input.GetMouseX() - 100;
    mouseDeltaY = Input.GetMouseY() - 100;
    app->SetCursorPosition(100, 100);

    if (!(mouseDeltaX == -100 && mouseDeltaY == -100) && !(mouseDeltaX == 0 && mouseDeltaY == 0))
        player->ChangeRotation((mouseDeltaY/10), (mouseDeltaX/10));

    player->DoStep(ElapsedTime);
}

void InputHandler::toggleFullscreen() {
    fullscreen = !fullscreen;
    app->Create(sf::VideoMode(800, 600, 32), "MineCube", (fullscreen ? sf::Style::Fullscreen : sf::Style::Resize|sf::Style::Close));

    renderer->InitGraphics();
    app->ShowMouseCursor(false);
}
<commit_msg>Removed unneeded parenthesis<commit_after>#include "input.h"

InputHandler::InputHandler(sf::Window* Window, Player* Player, Renderer* Renderer) : app(Window), player(Player), renderer(Renderer) {
    app->ShowMouseCursor(false);
}

bool fullscreen = false;
void InputHandler::handleEvent(sf::Event Event) {
    // Close window : exit
    if (Event.Type == sf::Event::Closed)
        app->Close();

    if(Event.Type == sf::Event::KeyPressed) {
        switch(Event.Key.Code) {
            // Escape key : exit
            case sf::Key::Escape:
                app->Close();
                break;
            // Spacebar : jump
            case sf::Key::Space:
                player->Jump();
                break;
            // F5 : regenerate terrain
            case sf::Key::F5:
                renderer->terrain.Regenerate();
                break;
            // F11 : toggle fullscreen
            case sf::Key::F11:
                toggleFullscreen();
                break;
        }
    }

    // Resize event : adjust viewport
    if (Event.Type == sf::Event::Resized)
        glViewport(0, 0, Event.Size.Width, Event.Size.Height);
}

float ElapsedTime;
float mouseDeltaX, mouseDeltaY;

void InputHandler::handleEvents() {
    const sf::Input& Input = app->GetInput();

    // Constant movement speed
    ElapsedTime = Clock.GetElapsedTime();
    Clock.Reset();

    // Handle held keys
    if (Input.IsKeyDown(sf::Key::S)) player->Forward(-ElapsedTime);
    if (Input.IsKeyDown(sf::Key::W)) player->Forward( ElapsedTime);
    if (Input.IsKeyDown(sf::Key::D)) player->Strafe(-ElapsedTime);
    if (Input.IsKeyDown(sf::Key::A)) player->Strafe( ElapsedTime);
    if (Input.IsKeyDown(sf::Key::Z)) player->Speed++;
    if (Input.IsKeyDown(sf::Key::X)) player->Speed--;

    // Handle other events
    sf::Event Event;
    while (app->GetEvent(Event))
    {
        handleEvent(Event);
    }

    // Rotate view based on mouse movement
    mouseDeltaX = Input.GetMouseX() - 100;
    mouseDeltaY = Input.GetMouseY() - 100;
    app->SetCursorPosition(100, 100);

    if (!(mouseDeltaX == -100 && mouseDeltaY == -100) && !(mouseDeltaX == 0 && mouseDeltaY == 0))
        player->ChangeRotation((mouseDeltaY/10), (mouseDeltaX/10));

    player->DoStep(ElapsedTime);
}

void InputHandler::toggleFullscreen() {
    fullscreen = !fullscreen;
    app->Create(sf::VideoMode(800, 600, 32), "MineCube", (fullscreen ? sf::Style::Fullscreen : sf::Style::Resize|sf::Style::Close));

    renderer->InitGraphics();
    app->ShowMouseCursor(false);
}
<|endoftext|>


<commit_before>#include "window.h"
#include "exception.h"
#include "sprite.h"

#if MW_OPENGLES2
#include "shader.h"
#include "matrix.h"
#endif // MW_OPENGLES2

#include <SDL_image.h>

namespace mw {

	int Window::nbrCurrentInstance = 0;

	void Window::initOpenGl() {
		if (nbrCurrentInstance < 1) {
			SDL_GL_SetSwapInterval(1);
			SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
#ifdef MW_OPENGLES2
			//SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
			SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
			SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
			SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
			if (SDL_GL_LoadLibrary(0) != 0) {
				std::printf("\n Failed to load OpenGl ES 2\n");
				std::exit(1);
			}
#endif
		}
	}

	Window::Window(int x, int y, int width, int height, bool resizeable, std::string title, std::string icon, bool borderless) {
		// Create an application window with the following settings:
		Uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL;
		if (resizeable) {
			flags |= SDL_WINDOW_RESIZABLE;
		}
		borderless_ = borderless;
		if (borderless) {
			flags |= SDL_WINDOW_BORDERLESS;
		}

		initOpenGl();

		if (x < 0) {
			x = SDL_WINDOWPOS_UNDEFINED;
		}
		if (y < 0) {
			y = SDL_WINDOWPOS_UNDEFINED;
		}

		window_ = SDL_CreateWindow(
			title.c_str(),
			x,
			y,
			width,
			height,
			flags);

		if (window_ == 0) {
			throw Exception(SDL_GetError());
		}

		SDL_Surface* surface = IMG_Load(icon.c_str());
		SDL_SetWindowIcon(window_, surface);
		SDL_FreeSurface(surface);

		quit_ = false;
		time_ = 0;
		width_ = width;
		height_ = height;

		setupOpenGlContext();
		++nbrCurrentInstance;
	}

	void Window::setupOpenGlContext() {
		glContext_ = SDL_GL_CreateContext(window_);
#ifdef MW_OPENGLES2
		initGLES2();
		mw::Shader shader;
		shader.bindAttribute(SHADER_A_VEC4_POSITION);
		shader.bindAttribute(SHADER_A_VEC2_TEXCOORD);
		shader.loadAndLink(SHADER_VER, SHADER_FRAG);
		Shader::setDefaultShader(shader);
#endif //MW_OPENGLES2
		if (nbrCurrentInstance < 1) {
			std::printf("\nGL_VERSION: %s", reinterpret_cast<const char *>(glGetString(GL_VERSION)));
			std::printf("\nGL_SHADING_LANGUAGE_VERSION: %s\n\n", reinterpret_cast<const char *>(glGetString(GL_SHADING_LANGUAGE_VERSION)));
		}
		glClear(GL_COLOR_BUFFER_BIT);
		checkGlError();
	}

	Window::~Window() {
		if (window_ != nullptr) {
			// In order to signal the the current gl context is not active.
			++nbrCurrentInstance;

#ifdef MW_OPENGLES2
			// Clean up the shader.
			Shader::setDefaultShader(mw::Shader());
#endif //MW_OPENGLES2

			// Clean up Gl context and the window.
			SDL_GL_DeleteContext(glContext_);
			SDL_DestroyWindow(window_);
		}
	}

	void Window::startLoop(Uint32 delta) {
		if (!quit_) {
			SDL_GL_MakeCurrent(window_, glContext_);
		}
		while (!quit_) {
			SDL_Event eventSDL;
			while (SDL_PollEvent(&eventSDL)) {
				eventUpdate(eventSDL);
			}

			Uint32 currentTime = SDL_GetTicks();
			Uint32 deltaTime = currentTime - time_;
			if (deltaTime >= delta) {
				// Only update the screen at choosen intervall.
				// Solve the problem where the SDL_GetTicks is imprecise
				// under some delta on some plattforms.
				time_ = currentTime;
				update(deltaTime);
			}

			SDL_GL_SwapWindow(window_);
		}
	}

	SDL_Window* Window::getSdlWindow() const {
		return window_;
	}

	void Window::setFullScreen(bool fullScreen) {
		if (isFullScreen()) {
			SDL_SetWindowFullscreen(window_, 0);
			SDL_SetWindowSize(window_, width_, height_);
			if (borderless_) {
				SDL_SetWindowBordered(window_, SDL_bool::SDL_FALSE);
			}
		} else {
			SDL_GetWindowSize(window_, &width_, &height_);
			SDL_SetWindowFullscreen(window_, SDL_WINDOW_FULLSCREEN_DESKTOP);
		}
	}

	bool Window::isFullScreen() const {
		return (SDL_GetWindowFlags(window_) & SDL_WINDOW_FULLSCREEN_DESKTOP) > 0;
	}

	int Window::getWidth() const {
		int w, h;
		SDL_GetWindowSize(window_, &w, &h);
		return w;
	}

	int Window::getHeight() const {
		int w, h;
		SDL_GetWindowSize(window_, &w, &h);
		return h;
	}

	void Window::getSize(int& width, int& height) {
		SDL_GetWindowSize(window_, &width, &height);
	}

	void Window::quit() {
		quit_ = true;
	}

	Uint32  Window::getId() const {
		return SDL_GetWindowID(window_);
	}

	void Window::update(Uint32 deltaTime) {
	}

	void Window::eventUpdate(const SDL_Event& windowEvent) {
	}

} // Namespace mw.
<commit_msg>BUg fix! Crashes on nvidia cards.<commit_after>#include "window.h"
#include "exception.h"
#include "sprite.h"

#if MW_OPENGLES2
#include "shader.h"
#include "matrix.h"
#endif // MW_OPENGLES2

#include <SDL_image.h>

namespace mw {

	int Window::nbrCurrentInstance = 0;

	void Window::initOpenGl() {
		if (nbrCurrentInstance < 1) {
			SDL_GL_SetSwapInterval(1);
			SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
#ifdef MW_OPENGLES2
			SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
			SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
			SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
			if (SDL_GL_LoadLibrary(0) != 0) {
				std::printf("\n Failed to load OpenGl ES 2\n");
				std::exit(1);
			}
#endif
		}
	}

	Window::Window(int x, int y, int width, int height, bool resizeable, std::string title, std::string icon, bool borderless) {
		// Create an application window with the following settings:
		Uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL;
		if (resizeable) {
			flags |= SDL_WINDOW_RESIZABLE;
		}
		borderless_ = borderless;
		if (borderless) {
			flags |= SDL_WINDOW_BORDERLESS;
		}

		initOpenGl();

		if (x < 0) {
			x = SDL_WINDOWPOS_UNDEFINED;
		}
		if (y < 0) {
			y = SDL_WINDOWPOS_UNDEFINED;
		}

		window_ = SDL_CreateWindow(
			title.c_str(),
			x,
			y,
			width,
			height,
			flags);

		if (window_ == 0) {
			throw Exception(SDL_GetError());
		}

		SDL_Surface* surface = IMG_Load(icon.c_str());
		SDL_SetWindowIcon(window_, surface);
		SDL_FreeSurface(surface);

		quit_ = false;
		time_ = 0;
		width_ = width;
		height_ = height;

		setupOpenGlContext();
		++nbrCurrentInstance;
	}

	void Window::setupOpenGlContext() {
		glContext_ = SDL_GL_CreateContext(window_);
#ifdef MW_OPENGLES2
		initGLES2();
		mw::Shader shader;
		shader.bindAttribute(SHADER_A_VEC4_POSITION);
		shader.bindAttribute(SHADER_A_VEC2_TEXCOORD);
		shader.loadAndLink(SHADER_VER, SHADER_FRAG);
		Shader::setDefaultShader(shader);
#endif //MW_OPENGLES2
		if (nbrCurrentInstance < 1) {
			std::printf("\nGL_VERSION: %s", reinterpret_cast<const char *>(glGetString(GL_VERSION)));
			std::printf("\nGL_SHADING_LANGUAGE_VERSION: %s\n\n", reinterpret_cast<const char *>(glGetString(GL_SHADING_LANGUAGE_VERSION)));
		}
		glClear(GL_COLOR_BUFFER_BIT);
		checkGlError();
	}

	Window::~Window() {
		if (window_ != nullptr) {
			// In order to signal the the current gl context is not active.
			++nbrCurrentInstance;

#ifdef MW_OPENGLES2
			// Clean up the shader.
			Shader::setDefaultShader(mw::Shader());
#endif //MW_OPENGLES2

			// Clean up Gl context and the window.
			SDL_GL_DeleteContext(glContext_);
			SDL_DestroyWindow(window_);
		}
	}

	void Window::startLoop(Uint32 delta) {
		if (!quit_) {
			SDL_GL_MakeCurrent(window_, glContext_);
		}
		while (!quit_) {
			SDL_Event eventSDL;
			while (SDL_PollEvent(&eventSDL)) {
				eventUpdate(eventSDL);
			}

			Uint32 currentTime = SDL_GetTicks();
			Uint32 deltaTime = currentTime - time_;
			if (deltaTime >= delta) {
				// Only update the screen at choosen intervall.
				// Solve the problem where the SDL_GetTicks is imprecise
				// under some delta on some plattforms.
				time_ = currentTime;
				update(deltaTime);
			}

			SDL_GL_SwapWindow(window_);
		}
	}

	SDL_Window* Window::getSdlWindow() const {
		return window_;
	}

	void Window::setFullScreen(bool fullScreen) {
		if (isFullScreen()) {
			SDL_SetWindowFullscreen(window_, 0);
			SDL_SetWindowSize(window_, width_, height_);
			if (borderless_) {
				SDL_SetWindowBordered(window_, SDL_bool::SDL_FALSE);
			}
		} else {
			SDL_GetWindowSize(window_, &width_, &height_);
			SDL_SetWindowFullscreen(window_, SDL_WINDOW_FULLSCREEN_DESKTOP);
		}
	}

	bool Window::isFullScreen() const {
		return (SDL_GetWindowFlags(window_) & SDL_WINDOW_FULLSCREEN_DESKTOP) > 0;
	}

	int Window::getWidth() const {
		int w, h;
		SDL_GetWindowSize(window_, &w, &h);
		return w;
	}

	int Window::getHeight() const {
		int w, h;
		SDL_GetWindowSize(window_, &w, &h);
		return h;
	}

	void Window::getSize(int& width, int& height) {
		SDL_GetWindowSize(window_, &width, &height);
	}

	void Window::quit() {
		quit_ = true;
	}

	Uint32  Window::getId() const {
		return SDL_GetWindowID(window_);
	}

	void Window::update(Uint32 deltaTime) {
	}

	void Window::eventUpdate(const SDL_Event& windowEvent) {
	}

} // Namespace mw.
<|endoftext|>


<commit_before>// vim:ts=2:sw=2:expandtab:autoindent:filetype=cpp:
/*
The MIT License

Copyright (c) 2008, 2009 Aristid Breitkreuz, Ash Berlin, Ruediger Sonderfeld

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "flusspferd/modules.hpp"
#include "flusspferd/create.hpp"
#include "flusspferd/string.hpp"
#include "flusspferd/tracer.hpp"
#include "flusspferd/security.hpp"
#include "flusspferd/evaluate.hpp"
#include "flusspferd/value_io.hpp"
#include <boost/filesystem.hpp>
#include <boost/unordered_map.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/spirit/home/phoenix/core.hpp>
#include <boost/spirit/home/phoenix/operator.hpp>
#include <boost/utility.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <list>
#ifdef WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif

#define DIRSEP1 '/'
#define DIRSEP2 '\0'
#define SHLIBPREFIX "lib"

using namespace flusspferd;

namespace algo = boost::algorithm;
namespace phoenix = boost::phoenix;
namespace args = phoenix::arg_names;

static void require(call_context &);

void flusspferd::load_require_function(object container) {
  function imp = create_native_function(container, "require", &require, 1);

  imp.define_property("preload", create_object(), permanent_property);
  imp.define_property("paths", create_array(), permanent_property);
  imp.define_property("alias", create_object(), permanent_property);
  imp.define_property("module_cache", create_object(),
                      permanent_property);
  imp.define_property("id", flusspferd::string(),
                      permanent_property);
}

// Take 'foo/bar' as a flusspferd::string, check no path sep in it, and
// return '/foo/bar.js' or '/foo/libbar.so', etc. as a std::string
static std::string process_name(
    std::string name,
    std::string module,
    std::string const &prefix,
    std::string const &suffix,
    char out_dirsep)
{
  if ((DIRSEP1 != '/' && name.find(DIRSEP1) != std::string::npos) &&
      (DIRSEP2 != '/' && DIRSEP2 && name.find(DIRSEP2) != std::string::npos))
  {
    throw exception("Invalid module name");
  }

  typedef std::list<std::string> container;

  module.erase(
    std::find(module.rbegin(), module.rend(), '/').base(),
    module.end());;

  if (algo::starts_with(name, "./") || algo::starts_with(name, "../"))
    name = module + "/" + name;

  container elements;
  algo::split(elements, name, args::arg1 == '/', algo::token_compress_on);

  if (elements.empty())
    throw exception("Invalid module name");

  elements.remove(".");

  for (container::iterator it = ++elements.begin(); it != elements.end();) {
    if (*it == ".." && it != elements.begin())
      it = elements.erase(boost::prior(it), boost::next(it));
    else
      ++it;
  }
  std::string result;

  container::iterator last = boost::prior(elements.end());

  for (container::iterator it = elements.begin(); it != last; ++it) {
    result.append(*it);
    result += out_dirsep;
  }

  result += prefix;
  result += *last;
  result += suffix;

  return result;
}

void require(call_context &x) {
  security &sec = security::get();

  value paths_v = x.function.get_property_object("paths");
  if (!paths_v.is_object() || paths_v.is_null())
    throw exception("Unable to get search paths or it is not an object");

  array paths = paths_v.get_object();
  size_t len = paths.length();

  bool found = false;

  std::string name = flusspferd::string(x.arg[0]).to_string();

  std::string module =
      x.function.get_property("id").to_std_string();

  std::string key = process_name(name, module, "", "", '/');

  object module_cache;

  try {
    x.function.set_property("id", flusspferd::string(key));

    value alias_v = x.function.get_property("alias");

    if (alias_v.is_object() && !alias_v.is_null()) {
      object alias = alias_v.get_object();
      if (alias.has_own_property(key)) {
        name = alias.get_property(key).to_std_string();
        key = process_name(name, "", "", "", '/');
      }
    }

    module_cache = x.function.get_property_object("module_cache");
    if (module_cache.is_null())
      throw exception("No valid module cache");

    if (module_cache.has_own_property(key)) {
      x.result = module_cache.get_property(key);
      return;
    }

    object classes_object = flusspferd::global().prototype();
    object ctx = flusspferd::create_object(classes_object);
    ctx.set_parent(classes_object);

    object exports = flusspferd::create_object();
    ctx.define_property(
      "exports",
      exports,
      read_only_property | permanent_property);

    module_cache.set_property(key, exports);
    x.result = exports;

    value preload = x.function.get_property("preload");

    if (preload.is_object() && !preload.is_null()) {
      value loader = preload.get_object().get_property(key);
      if (loader.is_object()) {
        if (!loader.is_null()) {
          local_root_scope scope;
          object o = loader.get_object();
          o.call(ctx);
        }
        return;
      }
    }

    std::string so_name, js_name;
    so_name = "/" + process_name(key, "", SHLIBPREFIX, FLUSSPFERD_MODULE_SUFFIX, DIRSEP1);
    js_name = "/" + process_name(key, "", "", ".js", DIRSEP1);

    for (size_t i = 0; i < len; i++) {
      std::string path = paths.get_element(i).to_std_string();
      std::string fullpath = path + so_name;

      if (sec.check_path(fullpath, security::READ) &&
          boost::filesystem::exists(fullpath))
      {
#ifdef WIN32
        HMODULE module = LoadLibrary(fullpath.c_str());

        if (!module)
          throw exception(("Unable to load library '" +fullpath+"'").c_str());

        FARPROC symbol = GetProcAddress(module, "flusspferd_load");

        if (!symbol)
          throw exception(("Unable to load library '" + fullpath + "': symbol "
                          "not found").c_str());
#else
        // Load the .so
        void *module = dlopen(fullpath.c_str(), RTLD_LAZY);
        if (!module) {
          std::stringstream ss;
          ss << "Unable to load library '" << fullpath.c_str()
             << "': " << dlerror();
          throw exception(ss.str().c_str());
        }

        dlerror(); // clear error state

        void *symbol = dlsym(module, "flusspferd_load");

        char const *const error_string = dlerror();

        if (error_string) {
          std::stringstream ss;
          ss << "Unable to load library '" << fullpath.c_str()
             << "': " << error_string;
          throw exception(ss.str().c_str());
        }
#endif

        flusspferd_load_t func = *(flusspferd_load_t*) &symbol;

        func(exports, ctx);

        // The exports reference might have been changed.
        module_cache.set_property(key, exports);
        x.result = exports;

        found = true;
        break;
      }
    }

    for (size_t i = 0; i < len; i++) {
      std::string path = paths.get_element(i).to_std_string();
      std::string fullpath = path + js_name;

      if (sec.check_path(fullpath, security::READ) &&
          boost::filesystem::exists(fullpath))
      {
        value val = flusspferd::execute(fullpath.c_str(), ctx);
        found = true;
        break;
      }
    }

    if (!found) {
      std::stringstream ss;
      ss << "Unable to find library '" << key << "' in [" << paths_v << "]";
      throw exception(ss.str().c_str());
    }
  } catch (...) {
    if (!module_cache.is_null())
      module_cache.delete_property(key);

    x.function.set_property("id", flusspferd::string(module));
    throw;
  }

  x.function.set_property("id", flusspferd::string(module));
}

<commit_msg>First (hacky) draft at sorting out JS module scope problem<commit_after>// vim:ts=2:sw=2:expandtab:autoindent:filetype=cpp:
/*
The MIT License

Copyright (c) 2008, 2009 Aristid Breitkreuz, Ash Berlin, Ruediger Sonderfeld

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "flusspferd/modules.hpp"
#include "flusspferd/create.hpp"
#include "flusspferd/string.hpp"
#include "flusspferd/tracer.hpp"
#include "flusspferd/security.hpp"
#include "flusspferd/evaluate.hpp"
#include "flusspferd/value_io.hpp"
#include <boost/filesystem.hpp>
#include <boost/unordered_map.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/spirit/home/phoenix/core.hpp>
#include <boost/spirit/home/phoenix/operator.hpp>
#include <boost/utility.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <cerrno>
#include <list>
#ifdef WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif

#define DIRSEP1 '/'
#define DIRSEP2 '\0'
#define SHLIBPREFIX "lib"

using namespace flusspferd;

namespace algo = boost::algorithm;
namespace phoenix = boost::phoenix;
namespace args = phoenix::arg_names;

static void require(call_context &);

void flusspferd::load_require_function(object container) {
  function imp = create_native_function(container, "require", &require, 1);

  imp.define_property("preload", create_object(), permanent_property);
  imp.define_property("paths", create_array(), permanent_property);
  imp.define_property("alias", create_object(), permanent_property);
  imp.define_property("module_cache", create_object(),
                      permanent_property);
  imp.define_property("id", flusspferd::string(),
                      permanent_property);
}

// Take 'foo/bar' as a flusspferd::string, check no path sep in it, and
// return '/foo/bar.js' or '/foo/libbar.so', etc. as a std::string
static std::string process_name(
    std::string name,
    std::string module,
    std::string const &prefix,
    std::string const &suffix,
    char out_dirsep)
{
  if ((DIRSEP1 != '/' && name.find(DIRSEP1) != std::string::npos) &&
      (DIRSEP2 != '/' && DIRSEP2 && name.find(DIRSEP2) != std::string::npos))
  {
    throw exception("Invalid module name");
  }

  typedef std::list<std::string> container;

  module.erase(
    std::find(module.rbegin(), module.rend(), '/').base(),
    module.end());;

  if (algo::starts_with(name, "./") || algo::starts_with(name, "../"))
    name = module + "/" + name;

  container elements;
  algo::split(elements, name, args::arg1 == '/', algo::token_compress_on);

  if (elements.empty())
    throw exception("Invalid module name");

  elements.remove(".");

  for (container::iterator it = ++elements.begin(); it != elements.end();) {
    if (*it == ".." && it != elements.begin())
      it = elements.erase(boost::prior(it), boost::next(it));
    else
      ++it;
  }
  std::string result;

  container::iterator last = boost::prior(elements.end());

  for (container::iterator it = elements.begin(); it != last; ++it) {
    result.append(*it);
    result += out_dirsep;
  }

  result += prefix;
  result += *last;
  result += suffix;

  return result;
}

void require_js(object require, char const *filename, object exports) {
  std::ifstream f(filename);
  std::stringstream ss;
  std::string l;
  if (!f) {
    unsigned int err = errno;
    ss << "io.File: Could not open file '" << filename << "' - " << err;
    throw exception(ss.str().c_str());
  }

  ss << "function(exports,require,module) { ";

  while( getline(f, l) ) {
    ss << l << '\n';
  }
  ss << ";}";

  f.close();

  std::string js = ss.str();
  printf("%s\n", js.c_str());

  object module = evaluate(js.c_str(), js.size(), filename, 1ul).to_object();

  // TODO: Create a new require object so we have a different ID
  module.call(module, exports, require);
}

void require(call_context &x) {
  security &sec = security::get();

  value paths_v = x.function.get_property_object("paths");
  if (!paths_v.is_object() || paths_v.is_null())
    throw exception("Unable to get search paths or it is not an object");

  array paths = paths_v.get_object();
  size_t len = paths.length();

  bool found = false;

  std::string name = flusspferd::string(x.arg[0]).to_string();

  std::string module =
      x.function.get_property("id").to_std_string();

  std::string key = process_name(name, module, "", "", '/');

  object module_cache;

  try {
    x.function.set_property("id", flusspferd::string(key));

    value alias_v = x.function.get_property("alias");

    if (alias_v.is_object() && !alias_v.is_null()) {
      object alias = alias_v.get_object();
      if (alias.has_own_property(key)) {
        name = alias.get_property(key).to_std_string();
        key = process_name(name, "", "", "", '/');
      }
    }

    module_cache = x.function.get_property_object("module_cache");
    if (module_cache.is_null())
      throw exception("No valid module cache");

    if (module_cache.has_own_property(key)) {
      x.result = module_cache.get_property(key);
      return;
    }

    object classes_object = flusspferd::global().prototype();
    object ctx = flusspferd::create_object(classes_object);
    ctx.set_parent(classes_object);

    object exports = flusspferd::create_object();
    ctx.define_property(
      "exports",
      exports,
      read_only_property | permanent_property);

    module_cache.set_property(key, exports);
    x.result = exports;

    value preload = x.function.get_property("preload");

    if (preload.is_object() && !preload.is_null()) {
      value loader = preload.get_object().get_property(key);
      if (loader.is_object()) {
        if (!loader.is_null()) {
          local_root_scope scope;
          object o = loader.get_object();
          o.call(ctx);
        }
        return;
      }
    }

    std::string so_name, js_name;
    so_name = "/" + process_name(key, "", SHLIBPREFIX, FLUSSPFERD_MODULE_SUFFIX, DIRSEP1);
    js_name = "/" + process_name(key, "", "", ".js", DIRSEP1);

    for (size_t i = 0; i < len; i++) {
      std::string path = paths.get_element(i).to_std_string();
      std::string fullpath = path + so_name;

      if (sec.check_path(fullpath, security::READ) &&
          boost::filesystem::exists(fullpath))
      {
#ifdef WIN32
        HMODULE module = LoadLibrary(fullpath.c_str());

        if (!module)
          throw exception(("Unable to load library '" +fullpath+"'").c_str());

        FARPROC symbol = GetProcAddress(module, "flusspferd_load");

        if (!symbol)
          throw exception(("Unable to load library '" + fullpath + "': symbol "
                          "not found").c_str());
#else
        // Load the .so
        void *module = dlopen(fullpath.c_str(), RTLD_LAZY);
        if (!module) {
          std::stringstream ss;
          ss << "Unable to load library '" << fullpath.c_str()
             << "': " << dlerror();
          throw exception(ss.str().c_str());
        }

        dlerror(); // clear error state

        void *symbol = dlsym(module, "flusspferd_load");

        char const *const error_string = dlerror();

        if (error_string) {
          std::stringstream ss;
          ss << "Unable to load library '" << fullpath.c_str()
             << "': " << error_string;
          throw exception(ss.str().c_str());
        }
#endif

        flusspferd_load_t func = *(flusspferd_load_t*) &symbol;

        func(exports, ctx);

        // The exports reference might have been changed.
        module_cache.set_property(key, exports);
        x.result = exports;

        found = true;
        break;
      }
    }

    for (size_t i = 0; i < len; i++) {
      std::string path = paths.get_element(i).to_std_string();
      std::string fullpath = path + js_name;

      if (sec.check_path(fullpath, security::READ) &&
          boost::filesystem::exists(fullpath))
      {
        //value val = flusspferd::execute(fullpath.c_str(), ctx);
        require_js(x.function, fullpath.c_str(), exports);
        found = true;
        break;
      }
    }

    if (!found) {
      std::stringstream ss;
      ss << "Unable to find library '" << key << "' in [" << paths_v << "]";
      throw exception(ss.str().c_str());
    }
  } catch (...) {
    if (!module_cache.is_null())
      module_cache.delete_property(key);

    x.function.set_property("id", flusspferd::string(module));
    throw;
  }

  x.function.set_property("id", flusspferd::string(module));
}

<|endoftext|>