<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 _GNU_SOURCE
#define _GNU_SOURCE // needed for getopt_long
#endif

#include <thrift/thrift-config.h>

#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <getopt.h>
#include <boost/test/unit_test.hpp>

#include <thrift/transport/TFileTransport.h>

using namespace apache::thrift::transport;

/**************************************************************************
 * Global state
 **************************************************************************/

static const char* tmp_dir = "/tmp";

class FsyncLog;
FsyncLog* fsync_log;

/**************************************************************************
 * Helper code
 **************************************************************************/

// Provide BOOST_CHECK_LT() and BOOST_CHECK_GT(), in case we're compiled
// with an older version of boost
#ifndef BOOST_CHECK_LT
#define BOOST_CHECK_CMP(a, b, op, check_fn) \
  check_fn((a) op (b), \
           "check " BOOST_STRINGIZE(a) " " BOOST_STRINGIZE(op) " " \
           BOOST_STRINGIZE(b) " failed: " BOOST_STRINGIZE(a) "=" << (a) << \
           " " BOOST_STRINGIZE(b) "=" << (b))

#define BOOST_CHECK_LT(a, b) BOOST_CHECK_CMP(a, b, <, BOOST_CHECK_MESSAGE)
#define BOOST_CHECK_GT(a, b) BOOST_CHECK_CMP(a, b, >, BOOST_CHECK_MESSAGE)
#define BOOST_REQUIRE_LT(a, b) BOOST_CHECK_CMP(a, b, <, BOOST_REQUIRE_MESSAGE)
#endif // BOOST_CHECK_LT

/**
 * Class to record calls to fsync
 */
class FsyncLog {
 public:
  struct FsyncCall {
    struct timeval time;
    int fd;
  };
  typedef std::list<FsyncCall> CallList;

  FsyncLog() {}

  void fsync(int fd) {
    (void) fd;
    FsyncCall call;
    gettimeofday(&call.time, NULL);
    calls_.push_back(call);
  }

  const CallList* getCalls() const {
    return &calls_;
  }

 private:
  CallList calls_;
};

/**
 * Helper class to clean up temporary files
 */
class TempFile {
 public:
  TempFile(const char* directory, const char* prefix) {
    size_t path_len = strlen(directory) + strlen(prefix) + 8;
    path_ = new char[path_len];
    snprintf(path_, path_len, "%s/%sXXXXXX", directory, prefix);

    fd_ = mkstemp(path_);
    if (fd_ < 0) {
      throw apache::thrift::TException("mkstemp() failed");
    }
  }

  ~TempFile() {
    unlink();
    close();
  }

  const char* getPath() const {
    return path_;
  }

  int getFD() const {
    return fd_;
  }

  void unlink() {
    if (path_) {
      ::unlink(path_);
      delete[] path_;
      path_ = NULL;
    }
  }

  void close() {
    if (fd_ < 0) {
      return;
    }

    ::close(fd_);
    fd_ = -1;
  }

 private:
  char* path_;
  int fd_;
};

// Use our own version of fsync() for testing.
// This returns immediately, so timing in test_destructor() isn't affected by
// waiting on the actual filesystem.
extern "C"
int fsync(int fd) {
  if (fsync_log) {
    fsync_log->fsync(fd);
  }
  return 0;
}

int time_diff(const struct timeval* t1, const struct timeval* t2) {
  return (t2->tv_usec - t1->tv_usec) + (t2->tv_sec - t1->tv_sec) * 1000000;
}

/**************************************************************************
 * Test cases
 **************************************************************************/

/**
 * Make sure the TFileTransport destructor exits "quickly".
 *
 * Previous versions had a bug causing the writer thread not to exit
 * right away.
 *
 * It's kind of lame that we just check to see how long the destructor takes in
 * wall-clock time.  This could result in false failures on slower systems, or
 * on heavily loaded machines.
 */
BOOST_AUTO_TEST_CASE(test_destructor) {
  TempFile f(tmp_dir, "thrift.TFileTransportTest.");

  unsigned int const NUM_ITERATIONS = 1000;

  unsigned int num_over = 0;
  for (unsigned int n = 0; n < NUM_ITERATIONS; ++n) {
    ftruncate(f.getFD(), 0);

    TFileTransport* transport = new TFileTransport(f.getPath());

    // write something so that the writer thread gets started
    transport->write(reinterpret_cast<const uint8_t*>("foo"), 3);

    // Every other iteration, also call flush(), just in case that potentially
    // has any effect on how the writer thread wakes up.
    if (n & 0x1) {
      transport->flush();
    }

    /*
     * Time the call to the destructor
     */
    struct timeval start;
    struct timeval end;

    gettimeofday(&start, NULL);
    delete transport;
    gettimeofday(&end, NULL);

    int delta = time_diff(&start, &end);

    // If any attempt takes more than 500ms, treat that as a failure.
    // Treat this as a fatal failure, so we'll return now instead of
    // looping over a very slow operation.
    BOOST_REQUIRE_LT(delta, 500000);

    // Normally, it takes less than 100ms on my dev box.
    // However, if the box is heavily loaded, some of the test runs
    // take longer, since we're just waiting for our turn on the CPU.
    if (delta > 100000) {
      ++num_over;
    }
  }

  // Make sure fewer than 10% of the runs took longer than 1000us
  BOOST_CHECK(num_over < (NUM_ITERATIONS / 10));
}

/**
 * Make sure setFlushMaxUs() is honored.
 */
void test_flush_max_us_impl(uint32_t flush_us, uint32_t write_us,
                            uint32_t test_us) {
  // TFileTransport only calls fsync() if data has been written,
  // so make sure the write interval is smaller than the flush interval.
  BOOST_REQUIRE(write_us < flush_us);

  TempFile f(tmp_dir, "thrift.TFileTransportTest.");

  // Record calls to fsync()
  FsyncLog log;
  fsync_log = &log;

  TFileTransport* transport = new TFileTransport(f.getPath());
  // Don't flush because of # of bytes written
  transport->setFlushMaxBytes(0xffffffff);
  uint8_t buf[] = "a";
  uint32_t buflen = sizeof(buf);

  // Set the flush interval
  transport->setFlushMaxUs(flush_us);

  // Make one call to write, to start the writer thread now.
  // (If we just let the thread get created during our test loop,
  // the thread creation sometimes takes long enough to make the first
  // fsync interval fail the check.)
  transport->write(buf, buflen);

  // Add one entry to the fsync log, just to mark the start time
  log.fsync(-1);

  // Loop doing write(), sleep(), ...
  uint32_t total_time = 0;
  while (true) {
    transport->write(buf, buflen);
    if (total_time > test_us) {
      break;
    }
    usleep(write_us);
    total_time += write_us;
  }

  delete transport;

  // Stop logging new fsync() calls
  fsync_log = NULL;

  // Examine the fsync() log
  //
  // TFileTransport uses pthread_cond_timedwait(), which only has millisecond
  // resolution.  In my testing, it normally wakes up about 1 millisecond late.
  // However, sometimes it takes a bit longer.  Allow 5ms leeway.
  int max_allowed_delta = flush_us + 5000;

  const FsyncLog::CallList* calls = log.getCalls();
  // We added 1 fsync call above.
  // Make sure TFileTransport called fsync at least once
  BOOST_CHECK_GE(calls->size(),
                 static_cast<FsyncLog::CallList::size_type>(1));

  const struct timeval* prev_time = NULL;
  for (FsyncLog::CallList::const_iterator it = calls->begin();
       it != calls->end();
       ++it) {
    if (prev_time) {
      int delta = time_diff(prev_time, &it->time);
      BOOST_CHECK_LT(delta, max_allowed_delta);
    }
    prev_time = &it->time;
  }
}

BOOST_AUTO_TEST_CASE(test_flush_max_us1) {
  // fsync every 10ms, write every 5ms, for 500ms
  test_flush_max_us_impl(10000, 5000, 500000);
}

BOOST_AUTO_TEST_CASE(test_flush_max_us2) {
  // fsync every 50ms, write every 20ms, for 500ms
  test_flush_max_us_impl(50000, 20000, 500000);
}

BOOST_AUTO_TEST_CASE(test_flush_max_us3) {
  // fsync every 400ms, write every 300ms, for 1s
  test_flush_max_us_impl(400000, 300000, 1000000);
}

/**
 * Make sure flush() is fast when there is nothing to do.
 *
 * TFileTransport used to have a bug where flush() would wait for the fsync
 * timeout to expire.
 */
BOOST_AUTO_TEST_CASE(test_noop_flush) {
  TempFile f(tmp_dir, "thrift.TFileTransportTest.");
  TFileTransport transport(f.getPath());

  // Write something to start the writer thread.
  uint8_t buf[] = "a";
  transport.write(buf, 1);

  struct timeval start;
  gettimeofday(&start, NULL);

  for (unsigned int n = 0; n < 10; ++n) {
    transport.flush();

    struct timeval now;
    gettimeofday(&now, NULL);

    // Fail if at any point we've been running for longer than half a second.
    // (With the buggy code, TFileTransport used to take 3 seconds per flush())
    //
    // Use a fatal fail so we break out early, rather than continuing to make
    // many more slow flush() calls.
    int delta = time_diff(&start, &now);
    BOOST_REQUIRE_LT(delta, 2000000);
  }
}

/**************************************************************************
 * General Initialization
 **************************************************************************/

void print_usage(FILE* f, const char* argv0) {
  fprintf(f, "Usage: %s [boost_options] [options]\n", argv0);
  fprintf(f, "Options:\n");
  fprintf(f, "  --tmp-dir=DIR, -t DIR\n");
  fprintf(f, "  --help\n");
}

void parse_args(int argc, char* argv[]) {
  struct option long_opts[] = {
    { "help", false, NULL, 'h' },
    { "tmp-dir", true, NULL, 't' },
    { NULL, 0, NULL, 0 }
  };

  while (true) {
    optopt = 1;
    int optchar = getopt_long(argc, argv, "ht:", long_opts, NULL);
    if (optchar == -1) {
      break;
    }

    switch (optchar) {
      case 't':
        tmp_dir = optarg;
        break;
      case 'h':
        print_usage(stdout, argv[0]);
        exit(0);
      case '?':
        exit(1);
      default:
        // Only happens if someone adds another option to the optarg string,
        // but doesn't update the switch statement to handle it.
        fprintf(stderr, "unknown option \"-%c\"\n", optchar);
        exit(1);
    }
  }
}

boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
  boost::unit_test::framework::master_test_suite().p_name.value =
    "TFileTransportTest";

  // Parse arguments
  parse_args(argc, argv);
  return NULL;
}
<commit_msg>THRIFT-2316 cpp: TFileTransportTest is to restrictive<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 _GNU_SOURCE
#define _GNU_SOURCE // needed for getopt_long
#endif

#include <thrift/thrift-config.h>

#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <getopt.h>
#include <boost/test/unit_test.hpp>

#include <thrift/transport/TFileTransport.h>

using namespace apache::thrift::transport;

/**************************************************************************
 * Global state
 **************************************************************************/

static const char* tmp_dir = "/tmp";

class FsyncLog;
FsyncLog* fsync_log;

/**************************************************************************
 * Helper code
 **************************************************************************/

// Provide BOOST_WARN_LT() and BOOST_WARN_GT(), in case we're compiled
// with an older version of boost
#ifndef BOOST_WARN_LT
#define BOOST_WARN_CMP(a, b, op, check_fn) \
  check_fn((a) op (b), \
           "check " BOOST_STRINGIZE(a) " " BOOST_STRINGIZE(op) " " \
           BOOST_STRINGIZE(b) " failed: " BOOST_STRINGIZE(a) "=" << (a) << \
           " " BOOST_STRINGIZE(b) "=" << (b))

#define BOOST_WARN_LT(a, b) BOOST_WARN_CMP(a, b, <, BOOST_WARN_MESSAGE)
#define BOOST_WARN_GT(a, b) BOOST_WARN_CMP(a, b, >, BOOST_WARN_MESSAGE)
#define BOOST_WARN_LT(a, b) BOOST_WARN_CMP(a, b, <, BOOST_WARN_MESSAGE)
#endif // BOOST_WARN_LT

/**
 * Class to record calls to fsync
 */
class FsyncLog {
 public:
  struct FsyncCall {
    struct timeval time;
    int fd;
  };
  typedef std::list<FsyncCall> CallList;

  FsyncLog() {}

  void fsync(int fd) {
    (void) fd;
    FsyncCall call;
    gettimeofday(&call.time, NULL);
    calls_.push_back(call);
  }

  const CallList* getCalls() const {
    return &calls_;
  }

 private:
  CallList calls_;
};

/**
 * Helper class to clean up temporary files
 */
class TempFile {
 public:
  TempFile(const char* directory, const char* prefix) {
    size_t path_len = strlen(directory) + strlen(prefix) + 8;
    path_ = new char[path_len];
    snprintf(path_, path_len, "%s/%sXXXXXX", directory, prefix);

    fd_ = mkstemp(path_);
    if (fd_ < 0) {
      throw apache::thrift::TException("mkstemp() failed");
    }
  }

  ~TempFile() {
    unlink();
    close();
  }

  const char* getPath() const {
    return path_;
  }

  int getFD() const {
    return fd_;
  }

  void unlink() {
    if (path_) {
      ::unlink(path_);
      delete[] path_;
      path_ = NULL;
    }
  }

  void close() {
    if (fd_ < 0) {
      return;
    }

    ::close(fd_);
    fd_ = -1;
  }

 private:
  char* path_;
  int fd_;
};

// Use our own version of fsync() for testing.
// This returns immediately, so timing in test_destructor() isn't affected by
// waiting on the actual filesystem.
extern "C"
int fsync(int fd) {
  if (fsync_log) {
    fsync_log->fsync(fd);
  }
  return 0;
}

int time_diff(const struct timeval* t1, const struct timeval* t2) {
  return (t2->tv_usec - t1->tv_usec) + (t2->tv_sec - t1->tv_sec) * 1000000;
}

/**************************************************************************
 * Test cases
 **************************************************************************/

/**
 * Make sure the TFileTransport destructor exits "quickly".
 *
 * Previous versions had a bug causing the writer thread not to exit
 * right away.
 *
 * It's kind of lame that we just check to see how long the destructor takes in
 * wall-clock time.  This could result in false failures on slower systems, or
 * on heavily loaded machines.
 */
BOOST_AUTO_TEST_CASE(test_destructor) {
  TempFile f(tmp_dir, "thrift.TFileTransportTest.");

  unsigned int const NUM_ITERATIONS = 1000;

  unsigned int num_over = 0;
  for (unsigned int n = 0; n < NUM_ITERATIONS; ++n) {
    ftruncate(f.getFD(), 0);

    TFileTransport* transport = new TFileTransport(f.getPath());

    // write something so that the writer thread gets started
    transport->write(reinterpret_cast<const uint8_t*>("foo"), 3);

    // Every other iteration, also call flush(), just in case that potentially
    // has any effect on how the writer thread wakes up.
    if (n & 0x1) {
      transport->flush();
    }

    /*
     * Time the call to the destructor
     */
    struct timeval start;
    struct timeval end;

    gettimeofday(&start, NULL);
    delete transport;
    gettimeofday(&end, NULL);

    int delta = time_diff(&start, &end);

    // If any attempt takes more than 500ms, treat that as a failure.
    // Treat this as a fatal failure, so we'll return now instead of
    // looping over a very slow operation.
    BOOST_WARN_LT(delta, 500000);

    // Normally, it takes less than 100ms on my dev box.
    // However, if the box is heavily loaded, some of the test runs
    // take longer, since we're just waiting for our turn on the CPU.
    if (delta > 100000) {
      ++num_over;
    }
  }

  // Make sure fewer than 10% of the runs took longer than 1000us
  BOOST_WARN(num_over < (NUM_ITERATIONS / 10));
}

/**
 * Make sure setFlushMaxUs() is honored.
 */
void test_flush_max_us_impl(uint32_t flush_us, uint32_t write_us,
                            uint32_t test_us) {
  // TFileTransport only calls fsync() if data has been written,
  // so make sure the write interval is smaller than the flush interval.
  BOOST_WARN(write_us < flush_us);

  TempFile f(tmp_dir, "thrift.TFileTransportTest.");

  // Record calls to fsync()
  FsyncLog log;
  fsync_log = &log;

  TFileTransport* transport = new TFileTransport(f.getPath());
  // Don't flush because of # of bytes written
  transport->setFlushMaxBytes(0xffffffff);
  uint8_t buf[] = "a";
  uint32_t buflen = sizeof(buf);

  // Set the flush interval
  transport->setFlushMaxUs(flush_us);

  // Make one call to write, to start the writer thread now.
  // (If we just let the thread get created during our test loop,
  // the thread creation sometimes takes long enough to make the first
  // fsync interval fail the check.)
  transport->write(buf, buflen);

  // Add one entry to the fsync log, just to mark the start time
  log.fsync(-1);

  // Loop doing write(), sleep(), ...
  uint32_t total_time = 0;
  while (true) {
    transport->write(buf, buflen);
    if (total_time > test_us) {
      break;
    }
    usleep(write_us);
    total_time += write_us;
  }

  delete transport;

  // Stop logging new fsync() calls
  fsync_log = NULL;

  // Examine the fsync() log
  //
  // TFileTransport uses pthread_cond_timedwait(), which only has millisecond
  // resolution.  In my testing, it normally wakes up about 1 millisecond late.
  // However, sometimes it takes a bit longer.  Allow 5ms leeway.
  int max_allowed_delta = flush_us + 5000;

  const FsyncLog::CallList* calls = log.getCalls();
  // We added 1 fsync call above.
  // Make sure TFileTransport called fsync at least once
  BOOST_WARN_GE(calls->size(),
                 static_cast<FsyncLog::CallList::size_type>(1));

  const struct timeval* prev_time = NULL;
  for (FsyncLog::CallList::const_iterator it = calls->begin();
       it != calls->end();
       ++it) {
    if (prev_time) {
      int delta = time_diff(prev_time, &it->time);
      BOOST_WARN_LT(delta, max_allowed_delta);
    }
    prev_time = &it->time;
  }
}

BOOST_AUTO_TEST_CASE(test_flush_max_us1) {
  // fsync every 10ms, write every 5ms, for 500ms
  test_flush_max_us_impl(10000, 5000, 500000);
}

BOOST_AUTO_TEST_CASE(test_flush_max_us2) {
  // fsync every 50ms, write every 20ms, for 500ms
  test_flush_max_us_impl(50000, 20000, 500000);
}

BOOST_AUTO_TEST_CASE(test_flush_max_us3) {
  // fsync every 400ms, write every 300ms, for 1s
  test_flush_max_us_impl(400000, 300000, 1000000);
}

/**
 * Make sure flush() is fast when there is nothing to do.
 *
 * TFileTransport used to have a bug where flush() would wait for the fsync
 * timeout to expire.
 */
BOOST_AUTO_TEST_CASE(test_noop_flush) {
  TempFile f(tmp_dir, "thrift.TFileTransportTest.");
  TFileTransport transport(f.getPath());

  // Write something to start the writer thread.
  uint8_t buf[] = "a";
  transport.write(buf, 1);

  struct timeval start;
  gettimeofday(&start, NULL);

  for (unsigned int n = 0; n < 10; ++n) {
    transport.flush();

    struct timeval now;
    gettimeofday(&now, NULL);

    // Fail if at any point we've been running for longer than half a second.
    // (With the buggy code, TFileTransport used to take 3 seconds per flush())
    //
    // Use a fatal fail so we break out early, rather than continuing to make
    // many more slow flush() calls.
    int delta = time_diff(&start, &now);
    BOOST_WARN_LT(delta, 2000000);
  }
}

/**************************************************************************
 * General Initialization
 **************************************************************************/

void print_usage(FILE* f, const char* argv0) {
  fprintf(f, "Usage: %s [boost_options] [options]\n", argv0);
  fprintf(f, "Options:\n");
  fprintf(f, "  --tmp-dir=DIR, -t DIR\n");
  fprintf(f, "  --help\n");
}

void parse_args(int argc, char* argv[]) {
  struct option long_opts[] = {
    { "help", false, NULL, 'h' },
    { "tmp-dir", true, NULL, 't' },
    { NULL, 0, NULL, 0 }
  };

  while (true) {
    optopt = 1;
    int optchar = getopt_long(argc, argv, "ht:", long_opts, NULL);
    if (optchar == -1) {
      break;
    }

    switch (optchar) {
      case 't':
        tmp_dir = optarg;
        break;
      case 'h':
        print_usage(stdout, argv[0]);
        exit(0);
      case '?':
        exit(1);
      default:
        // Only happens if someone adds another option to the optarg string,
        // but doesn't update the switch statement to handle it.
        fprintf(stderr, "unknown option \"-%c\"\n", optchar);
        exit(1);
    }
  }
}

boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
  boost::unit_test::framework::master_test_suite().p_name.value =
    "TFileTransportTest";

  // Parse arguments
  parse_args(argc, argv);
  return NULL;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017 Incognito
 *
 * 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 "../precompiled.h"

#include "amx.h"
#include "../core.h"
#include "../main.h"

using namespace Utility;

cell AMX_NATIVE_CALL Utility::hookedNative(AMX *amx, cell *params)
{
	return 1;
}

int Utility::checkInterfaceAndRegisterNatives(AMX *amx, AMX_NATIVE_INFO *amxNativeList)
{
	AMX_HEADER *amxHeader = reinterpret_cast<AMX_HEADER*>(amx->base);
	AMX_FUNCSTUBNT *amxNativeTable = reinterpret_cast<AMX_FUNCSTUBNT*>(amx->base + amxHeader->natives);
	int amxRegisterResult = amx_Register(amx, amxNativeList, -1);
	bool foundNatives = false;
	bool hookedNatives = false;
	int numberOfNatives = 0;
	amx_NumNatives(amx, &numberOfNatives);
	for (int i = 0; i < numberOfNatives; ++i)
	{
		char *name = reinterpret_cast<char*>(amx->base + amxNativeTable[i].nameofs);
		if (std::string(name).find("Streamer_") != std::string::npos)
		{
			foundNatives = true;
			if (!amxNativeTable[i].address)
			{
				Utility::logError("Obsolete or invalid native \"%s\" found (script might need to be recompiled with the correct include file).", name);
				amxNativeTable[i].address = reinterpret_cast<cell>(hookedNative);
				hookedNatives = true;
			}
		}
	}
	if (foundNatives)
	{
		cell amxAddr = 0;
		int includeFileValue = 0;
		if (!amx_FindPubVar(amx, "Streamer_IncludeFileVersion", &amxAddr))
		{
			cell *amxPhysAddr = NULL;
			if (!amx_GetAddr(amx, amxAddr, &amxPhysAddr))
			{
				includeFileValue = static_cast<int>(*amxPhysAddr);
			}
		}
		if (includeFileValue != INCLUDE_FILE_VERSION)
		{
			std::ostringstream includeFileVersion;
			if (includeFileValue <= 0)
			{
				includeFileVersion << "unknown version";
			}
			else
			{
				includeFileVersion << std::hex << std::showbase << includeFileValue;
			}
			Utility::logError("Include file version (%s) does not match plugin version (%#x) (script might need to be recompiled with the correct include file).", includeFileVersion.str().c_str(), INCLUDE_FILE_VERSION);
		}
	}
	if (hookedNatives)
	{
		amxRegisterResult = amx_Register(amx, amxNativeList, -1);
	}
	return amxRegisterResult;
}

void Utility::destroyAllItemsInInterface(AMX *amx)
{
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.begin();
	while (o != core->getData()->objects.end())
	{
		if (o->second->amx == amx)
		{
			o = destroyObject(o);
		}
		else
		{
			++o;
		}
	}
	boost::unordered_map<int, Item::SharedPickup>::iterator p = core->getData()->pickups.begin();
	while (p != core->getData()->pickups.end())
	{
		if (p->second->amx == amx)
		{
			p = destroyPickup(p);
		}
		else
		{
			++p;
		}
	}
	boost::unordered_map<int, Item::SharedCheckpoint>::iterator c = core->getData()->checkpoints.begin();
	while (c != core->getData()->checkpoints.end())
	{
		if (c->second->amx == amx)
		{
			c = destroyCheckpoint(c);
		}
		else
		{
			++c;
		}
	}
	boost::unordered_map<int, Item::SharedRaceCheckpoint>::iterator r = core->getData()->raceCheckpoints.begin();
	while (r != core->getData()->raceCheckpoints.end())
	{
		if (r->second->amx == amx)
		{
			r = destroyRaceCheckpoint(r);
		}
		else
		{
			++r;
		}
	}
	boost::unordered_map<int, Item::SharedMapIcon>::iterator m = core->getData()->mapIcons.begin();
	while (m != core->getData()->mapIcons.end())
	{
		if (m->second->amx == amx)
		{
			m = destroyMapIcon(m);
		}
		else
		{
			++m;
		}
	}
	boost::unordered_map<int, Item::SharedTextLabel>::iterator t = core->getData()->textLabels.begin();
	while (t != core->getData()->textLabels.end())
	{
		if (t->second->amx == amx)
		{
			t = destroyTextLabel(t);
		}
		else
		{
			++t;
		}
	}
	Utility::executeFinalAreaCallbacksForAllAreas(amx, false);
	boost::unordered_map<int, Item::SharedArea>::iterator a = core->getData()->areas.begin();
	while (a != core->getData()->areas.end())
	{
		if (a->second->amx == amx)
		{
			a = destroyArea(a);
		}
		else
		{
			++a;
		}
	}
	boost::unordered_map<int, Item::SharedActor>::iterator b = core->getData()->actors.begin();
	while (b != core->getData()->actors.end())
	{
		if (b->second->amx == amx)
		{
			b = destroyActor(b);
		}
		else
		{
			++b;
		}
	}
}

void Utility::executeFinalAreaCallbacks(int areaid)
{
	std::vector<boost::tuple<int, int> > callbacks;
	boost::unordered_map<int, Item::SharedArea>::iterator a = core->getData()->areas.find(areaid);
	if (a != core->getData()->areas.end())
	{
		for (boost::unordered_map<int, Player>::iterator p = core->getData()->players.begin(); p != core->getData()->players.end(); ++p)
		{
			boost::unordered_set<int>::iterator i = p->second.internalAreas.find(a->first);
			if (i != p->second.internalAreas.end())
			{
				callbacks.push_back(boost::make_tuple(a->first, p->first));
			}
		}
	}
	for (std::vector<boost::tuple<int, int> >::const_iterator c = callbacks.begin(); c != callbacks.end(); ++c)
	{
		for (std::set<AMX*>::iterator amx = core->getData()->interfaces.begin(); amx != core->getData()->interfaces.end(); ++amx)
		{
			int amxIndex = 0;
			if (!amx_FindPublic(*amx, "OnPlayerLeaveDynamicArea", &amxIndex))
			{
				amx_Push(*amx, static_cast<cell>(c->get<0>()));
				amx_Push(*amx, static_cast<cell>(c->get<1>()));
				amx_Exec(*amx, NULL, amxIndex);
			}
		}
	}
}

void Utility::executeFinalAreaCallbacksForAllAreas(AMX *amx, bool ignoreInterface)
{
	std::vector<boost::tuple<int, int> > callbacks;
	for (boost::unordered_map<int, Item::SharedArea>::iterator a = core->getData()->areas.begin(); a != core->getData()->areas.end(); ++a)
	{
		if (ignoreInterface || a->second->amx == amx)
		{
			for (boost::unordered_map<int, Player>::iterator p = core->getData()->players.begin(); p != core->getData()->players.end(); ++p)
			{
				boost::unordered_set<int>::iterator i = p->second.internalAreas.find(a->first);
				if (i != p->second.internalAreas.end())
				{
					callbacks.push_back(boost::make_tuple(a->first, p->first));
				}
			}
		}
	}
	for (std::vector<boost::tuple<int, int> >::const_iterator c = callbacks.begin(); c != callbacks.end(); ++c)
	{
		for (std::set<AMX*>::iterator a = core->getData()->interfaces.begin(); a != core->getData()->interfaces.end(); ++a)
		{
			int amxIndex = 0;
			if (!amx_FindPublic(*a, "OnPlayerLeaveDynamicArea", &amxIndex))
			{
				amx_Push(*a, static_cast<cell>(c->get<0>()));
				amx_Push(*a, static_cast<cell>(c->get<1>()));
				amx_Exec(*a, NULL, amxIndex);
			}
		}
	}
}

void Utility::logError(const char *format, ...)
{
	va_list args;
	va_start(args, format);
	char buffer[MAX_BUFFER];
	vsnprintf(buffer, sizeof(buffer), format, args);
	buffer[sizeof(buffer) - 1] = '\0';
	va_end(args);
	if (core->getData()->errorCallbackEnabled)
	{
		for (std::set<AMX*>::iterator a = core->getData()->interfaces.begin(); a != core->getData()->interfaces.end(); ++a)
		{
			cell amxAddress = 0;
			int amxIndex = 0;
			if (!amx_FindPublic(*a, "Streamer_OnPluginError", &amxIndex))
			{
				amx_PushString(*a, &amxAddress, NULL, buffer, 0, 0);
				amx_Exec(*a, NULL, amxIndex);
				amx_Release(*a, amxAddress);
			}
		}
	}
	else
	{
		static std::string lastErrorMessage;
		if (lastErrorMessage != buffer)
		{
			sampgdk::logprintf("*** Streamer Plugin: %s", buffer);
		}
		lastErrorMessage = buffer;
	}
}

void Utility::convertArrayToPolygon(AMX *amx, cell input, cell size, Polygon2d &polygon)
{
	cell *array = NULL;
	std::vector<Eigen::Vector2f> points;
	amx_GetAddr(amx, input, &array);
	for (std::size_t i = 0; i < static_cast<std::size_t>(size); i += 2)
	{
		points.push_back(Eigen::Vector2f(amx_ctof(array[i]), amx_ctof(array[i + 1])));
	}
	boost::geometry::assign_points(polygon, points);
	boost::geometry::correct(polygon);
}

bool Utility::convertPolygonToArray(AMX *amx, cell output, cell size, Polygon2d &polygon)
{
	cell *array = NULL;
	std::size_t i = 0;
	amx_GetAddr(amx, output, &array);
	for (std::vector<Eigen::Vector2f>::iterator p = polygon.outer().begin(); p != polygon.outer().end(); ++p)
	{
		if ((i + 1) >= static_cast<std::size_t>(size))
		{
			return false;
		}
		array[i++] = amx_ftoc(p->data()[0]);
		array[i++] = amx_ftoc(p->data()[1]);
	}
	return true;
}

std::string Utility::convertNativeStringToString(AMX *amx, cell input)
{
	char *string = NULL;
	amx_StrParam(amx, input, string);
	return string ? string : "";
}

void Utility::convertStringToNativeString(AMX *amx, cell output, cell size, std::string string)
{
	cell *address = NULL;
	amx_GetAddr(amx, output, &address);
	amx_SetString(address, string.c_str(), 0, 0, static_cast<size_t>(size));
}

void Utility::storeFloatInNative(AMX *amx, cell output, float value)
{
	cell *address;
	amx_GetAddr(amx, output, &address);
	*address = amx_ftoc(value);
}

void Utility::storeIntegerInNative(AMX *amx, cell output, int value)
{
	cell *address;
	amx_GetAddr(amx, output, &address);
	*address = value;
}
<commit_msg>Improve version check<commit_after>/*
 * Copyright (C) 2017 Incognito
 *
 * 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 "../precompiled.h"

#include "amx.h"
#include "../core.h"
#include "../main.h"

using namespace Utility;

cell AMX_NATIVE_CALL Utility::hookedNative(AMX *amx, cell *params)
{
	return 1;
}

int Utility::checkInterfaceAndRegisterNatives(AMX *amx, AMX_NATIVE_INFO *amxNativeList)
{
	AMX_HEADER *amxHeader = reinterpret_cast<AMX_HEADER*>(amx->base);
	AMX_FUNCSTUBNT *amxNativeTable = reinterpret_cast<AMX_FUNCSTUBNT*>(amx->base + amxHeader->natives);
	int amxRegisterResult = amx_Register(amx, amxNativeList, -1);
	bool foundNatives = false;
	bool hookedNatives = false;
	int numberOfNatives = 0;
	amx_NumNatives(amx, &numberOfNatives);
	for (int i = 0; i < numberOfNatives; ++i)
	{
		char *name = reinterpret_cast<char*>(amx->base + amxNativeTable[i].nameofs);
		if (std::string(name).find("Streamer_") != std::string::npos)
		{
			foundNatives = true;
			if (!amxNativeTable[i].address)
			{
				amxNativeTable[i].address = reinterpret_cast<cell>(hookedNative);
				hookedNatives = true;
			}
		}
	}
	if (foundNatives)
	{
		cell amxAddr = 0;
		int includeFileVersion = 0;
		if (!amx_FindPubVar(amx, "Streamer_IncludeFileVersion", &amxAddr))
		{
			cell *amxPhysAddr = NULL;
			if (!amx_GetAddr(amx, amxAddr, &amxPhysAddr))
			{
				includeFileVersion = static_cast<int>(*amxPhysAddr);
			}
		}
		std::ostringstream includeFileVersionStream;
		if (includeFileVersion <= 0)
		{
			includeFileVersionStream << "unknown version";
		}
		else
		{
			includeFileVersionStream << std::hex << std::showbase << includeFileVersion;
			std::istringstream(includeFileVersionStream.str().substr(0, std::to_string(INCLUDE_FILE_VERSION).length() + 2)) >> std::hex >> includeFileVersion;
		}
		if (includeFileVersion < INCLUDE_FILE_VERSION)
		{
			Utility::logError("The include file version (%s) for this script is older than the plugin version (%#x). The script might need to be recompiled with the latest include file.", includeFileVersionStream.str().c_str(), INCLUDE_FILE_VERSION);
		}
		else if (includeFileVersion > INCLUDE_FILE_VERSION)
		{
			Utility::logError("The plugin version (%#x) is older than the include file version (%s) for this script. The plugin might need to be updated to the latest version.", INCLUDE_FILE_VERSION, includeFileVersionStream.str().c_str());
		}
	}
	if (hookedNatives)
	{
		amxRegisterResult = amx_Register(amx, amxNativeList, -1);
	}
	return amxRegisterResult;
}

void Utility::destroyAllItemsInInterface(AMX *amx)
{
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.begin();
	while (o != core->getData()->objects.end())
	{
		if (o->second->amx == amx)
		{
			o = destroyObject(o);
		}
		else
		{
			++o;
		}
	}
	boost::unordered_map<int, Item::SharedPickup>::iterator p = core->getData()->pickups.begin();
	while (p != core->getData()->pickups.end())
	{
		if (p->second->amx == amx)
		{
			p = destroyPickup(p);
		}
		else
		{
			++p;
		}
	}
	boost::unordered_map<int, Item::SharedCheckpoint>::iterator c = core->getData()->checkpoints.begin();
	while (c != core->getData()->checkpoints.end())
	{
		if (c->second->amx == amx)
		{
			c = destroyCheckpoint(c);
		}
		else
		{
			++c;
		}
	}
	boost::unordered_map<int, Item::SharedRaceCheckpoint>::iterator r = core->getData()->raceCheckpoints.begin();
	while (r != core->getData()->raceCheckpoints.end())
	{
		if (r->second->amx == amx)
		{
			r = destroyRaceCheckpoint(r);
		}
		else
		{
			++r;
		}
	}
	boost::unordered_map<int, Item::SharedMapIcon>::iterator m = core->getData()->mapIcons.begin();
	while (m != core->getData()->mapIcons.end())
	{
		if (m->second->amx == amx)
		{
			m = destroyMapIcon(m);
		}
		else
		{
			++m;
		}
	}
	boost::unordered_map<int, Item::SharedTextLabel>::iterator t = core->getData()->textLabels.begin();
	while (t != core->getData()->textLabels.end())
	{
		if (t->second->amx == amx)
		{
			t = destroyTextLabel(t);
		}
		else
		{
			++t;
		}
	}
	Utility::executeFinalAreaCallbacksForAllAreas(amx, false);
	boost::unordered_map<int, Item::SharedArea>::iterator a = core->getData()->areas.begin();
	while (a != core->getData()->areas.end())
	{
		if (a->second->amx == amx)
		{
			a = destroyArea(a);
		}
		else
		{
			++a;
		}
	}
	boost::unordered_map<int, Item::SharedActor>::iterator b = core->getData()->actors.begin();
	while (b != core->getData()->actors.end())
	{
		if (b->second->amx == amx)
		{
			b = destroyActor(b);
		}
		else
		{
			++b;
		}
	}
}

void Utility::executeFinalAreaCallbacks(int areaid)
{
	std::vector<boost::tuple<int, int> > callbacks;
	boost::unordered_map<int, Item::SharedArea>::iterator a = core->getData()->areas.find(areaid);
	if (a != core->getData()->areas.end())
	{
		for (boost::unordered_map<int, Player>::iterator p = core->getData()->players.begin(); p != core->getData()->players.end(); ++p)
		{
			boost::unordered_set<int>::iterator i = p->second.internalAreas.find(a->first);
			if (i != p->second.internalAreas.end())
			{
				callbacks.push_back(boost::make_tuple(a->first, p->first));
			}
		}
	}
	for (std::vector<boost::tuple<int, int> >::const_iterator c = callbacks.begin(); c != callbacks.end(); ++c)
	{
		for (std::set<AMX*>::iterator amx = core->getData()->interfaces.begin(); amx != core->getData()->interfaces.end(); ++amx)
		{
			int amxIndex = 0;
			if (!amx_FindPublic(*amx, "OnPlayerLeaveDynamicArea", &amxIndex))
			{
				amx_Push(*amx, static_cast<cell>(c->get<0>()));
				amx_Push(*amx, static_cast<cell>(c->get<1>()));
				amx_Exec(*amx, NULL, amxIndex);
			}
		}
	}
}

void Utility::executeFinalAreaCallbacksForAllAreas(AMX *amx, bool ignoreInterface)
{
	std::vector<boost::tuple<int, int> > callbacks;
	for (boost::unordered_map<int, Item::SharedArea>::iterator a = core->getData()->areas.begin(); a != core->getData()->areas.end(); ++a)
	{
		if (ignoreInterface || a->second->amx == amx)
		{
			for (boost::unordered_map<int, Player>::iterator p = core->getData()->players.begin(); p != core->getData()->players.end(); ++p)
			{
				boost::unordered_set<int>::iterator i = p->second.internalAreas.find(a->first);
				if (i != p->second.internalAreas.end())
				{
					callbacks.push_back(boost::make_tuple(a->first, p->first));
				}
			}
		}
	}
	for (std::vector<boost::tuple<int, int> >::const_iterator c = callbacks.begin(); c != callbacks.end(); ++c)
	{
		for (std::set<AMX*>::iterator a = core->getData()->interfaces.begin(); a != core->getData()->interfaces.end(); ++a)
		{
			int amxIndex = 0;
			if (!amx_FindPublic(*a, "OnPlayerLeaveDynamicArea", &amxIndex))
			{
				amx_Push(*a, static_cast<cell>(c->get<0>()));
				amx_Push(*a, static_cast<cell>(c->get<1>()));
				amx_Exec(*a, NULL, amxIndex);
			}
		}
	}
}

void Utility::logError(const char *format, ...)
{
	va_list args;
	va_start(args, format);
	char buffer[MAX_BUFFER];
	vsnprintf(buffer, sizeof(buffer), format, args);
	buffer[sizeof(buffer) - 1] = '\0';
	va_end(args);
	if (core->getData()->errorCallbackEnabled)
	{
		for (std::set<AMX*>::iterator a = core->getData()->interfaces.begin(); a != core->getData()->interfaces.end(); ++a)
		{
			cell amxAddress = 0;
			int amxIndex = 0;
			if (!amx_FindPublic(*a, "Streamer_OnPluginError", &amxIndex))
			{
				amx_PushString(*a, &amxAddress, NULL, buffer, 0, 0);
				amx_Exec(*a, NULL, amxIndex);
				amx_Release(*a, amxAddress);
			}
		}
	}
	else
	{
		static std::string lastErrorMessage;
		if (lastErrorMessage != buffer)
		{
			sampgdk::logprintf("*** Streamer Plugin: %s", buffer);
		}
		lastErrorMessage = buffer;
	}
}

void Utility::convertArrayToPolygon(AMX *amx, cell input, cell size, Polygon2d &polygon)
{
	cell *array = NULL;
	std::vector<Eigen::Vector2f> points;
	amx_GetAddr(amx, input, &array);
	for (std::size_t i = 0; i < static_cast<std::size_t>(size); i += 2)
	{
		points.push_back(Eigen::Vector2f(amx_ctof(array[i]), amx_ctof(array[i + 1])));
	}
	boost::geometry::assign_points(polygon, points);
	boost::geometry::correct(polygon);
}

bool Utility::convertPolygonToArray(AMX *amx, cell output, cell size, Polygon2d &polygon)
{
	cell *array = NULL;
	std::size_t i = 0;
	amx_GetAddr(amx, output, &array);
	for (std::vector<Eigen::Vector2f>::iterator p = polygon.outer().begin(); p != polygon.outer().end(); ++p)
	{
		if ((i + 1) >= static_cast<std::size_t>(size))
		{
			return false;
		}
		array[i++] = amx_ftoc(p->data()[0]);
		array[i++] = amx_ftoc(p->data()[1]);
	}
	return true;
}

std::string Utility::convertNativeStringToString(AMX *amx, cell input)
{
	char *string = NULL;
	amx_StrParam(amx, input, string);
	return string ? string : "";
}

void Utility::convertStringToNativeString(AMX *amx, cell output, cell size, std::string string)
{
	cell *address = NULL;
	amx_GetAddr(amx, output, &address);
	amx_SetString(address, string.c_str(), 0, 0, static_cast<size_t>(size));
}

void Utility::storeFloatInNative(AMX *amx, cell output, float value)
{
	cell *address;
	amx_GetAddr(amx, output, &address);
	*address = amx_ftoc(value);
}

void Utility::storeIntegerInNative(AMX *amx, cell output, int value)
{
	cell *address;
	amx_GetAddr(amx, output, &address);
	*address = value;
}
<|endoftext|>


<commit_before>/*
 * ModSecurity, http://www.modsecurity.org/
 * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/)
 *
 * 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
 *
 * If any of the files related to licensing are missing or if you have any
 * other questions related to licensing please contact Trustwave Holdings, Inc.
 * directly using the email address security@modsecurity.org.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <wordexp.h>
#include <stdint.h>
#include <inttypes.h>

#include <ctime>
#include <iostream>
#include <string>
#include <vector>

#if defined _MSC_VER
#include <direct.h>
#elif defined __GNUC__
#include <sys/types.h>
#include <sys/stat.h>
#endif

#include "src/utils/system.h"
#include "src/config.h"

namespace modsecurity {
namespace utils {

double cpu_seconds(void) {
    /*
     * FIXME: Temporary hack to fix build on MacOS X.  Very issuficient way, but
     *      works.  Worth reimplementing using mach_absolute_time().
     */
#ifndef MACOSX
    struct timespec t;
    if (!clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t))
        return static_cast<double>(t.tv_sec)
            + static_cast<double>(t.tv_nsec / 1000000000.0);
    else
        return static_cast<double>(clock()) /
            static_cast<double>(CLOCKS_PER_SEC);
#endif
        return 0;
}

std::string find_resource(const std::string& resource,
    const std::string& config, std::string *err) {
    std::ifstream *iss;

    err->assign("Looking at: ");
    // Trying absolute or relative to the current dir.
    iss = new std::ifstream(resource, std::ios::in);
    if (iss->is_open()) {
        iss->close();
        delete iss;
        return resource;
    } else {
        err->append("'" + resource + "', ");
    }
    delete iss;

    // What about `*' ?
    if (utils::expandEnv(resource, 0).size() > 1) {
        return resource;
    } else {
        err->append("'" + resource + "', ");
    }

    // Trying the same path of the configuration file.
    std::string f = get_path(config) + "/" + resource;
    iss = new std::ifstream(f, std::ios::in);
    if (iss->is_open()) {
        iss->close();
        delete iss;
        return f;
    } else {
        err->append("'" + f + "', ");
    }
    delete iss;

    // What about `*' ?
    if (utils::expandEnv(f, 0).size() > 1) {
        return f;
    } else {
        err->append("'" + f + "'.");
    }

    return std::string("");
}

std::string get_path(const std::string& file) {
    size_t found;

    found = file.find_last_of("/\\");
    if (found > 0) {
        return file.substr(0, found);
    }

    return std::string("");
}

std::list<std::string> expandEnv(const std::string& var, int flags) {
    std::list<std::string> vars;

    wordexp_t p;
    if (wordexp(var.c_str(), &p, flags) == false) {
        if (p.we_wordc) {
            for (char** exp = p.we_wordv; *exp; ++exp) {
                vars.push_back(exp[0]);
            }
        }
        wordfree(&p);
    }
    return vars;
}

bool createDir(std::string dir, int mode, std::string *error) {
    int ret = mkdir(dir.data(), mode);
    if (ret != 0 && errno != EEXIST) {
        error->assign("Not able to create directory: " + dir + ": " \
            + strerror(errno) + ".");
        return false;
    }

    return true;
}

bool isFile(std::string f) {
    struct stat fileInfo;
    FILE *fp = fopen(f.c_str(), "r");
    if (fp == NULL) {
        return false;
    }
    fstat(fileno(fp), &fileInfo);
    if (!S_ISREG(fileInfo.st_mode)) {
        fclose(fp);
        return false;
    }
    fclose(fp);

    return true;
}

}  // namespace utils
}  // namespace modsecurity
<commit_msg>Fix "include /foo/*.conf" for single matched object in directory<commit_after>/*
 * ModSecurity, http://www.modsecurity.org/
 * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/)
 *
 * 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
 *
 * If any of the files related to licensing are missing or if you have any
 * other questions related to licensing please contact Trustwave Holdings, Inc.
 * directly using the email address security@modsecurity.org.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <wordexp.h>
#include <stdint.h>
#include <inttypes.h>

#include <ctime>
#include <iostream>
#include <string>
#include <vector>

#if defined _MSC_VER
#include <direct.h>
#elif defined __GNUC__
#include <sys/types.h>
#include <sys/stat.h>
#endif

#include "src/utils/system.h"
#include "src/config.h"

namespace modsecurity {
namespace utils {

double cpu_seconds(void) {
    /*
     * FIXME: Temporary hack to fix build on MacOS X.  Very issuficient way, but
     *      works.  Worth reimplementing using mach_absolute_time().
     */
#ifndef MACOSX
    struct timespec t;
    if (!clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t))
        return static_cast<double>(t.tv_sec)
            + static_cast<double>(t.tv_nsec / 1000000000.0);
    else
        return static_cast<double>(clock()) /
            static_cast<double>(CLOCKS_PER_SEC);
#endif
        return 0;
}

std::string find_resource(const std::string& resource,
    const std::string& config, std::string *err) {
    std::ifstream *iss;

    err->assign("Looking at: ");
    // Trying absolute or relative to the current dir.
    iss = new std::ifstream(resource, std::ios::in);
    if (iss->is_open()) {
        iss->close();
        delete iss;
        return resource;
    } else {
        err->append("'" + resource + "', ");
    }
    delete iss;

    // What about `*' ?
    if (utils::expandEnv(resource, 0).size() > 0) {
        return resource;
    } else {
        err->append("'" + resource + "', ");
    }

    // Trying the same path of the configuration file.
    std::string f = get_path(config) + "/" + resource;
    iss = new std::ifstream(f, std::ios::in);
    if (iss->is_open()) {
        iss->close();
        delete iss;
        return f;
    } else {
        err->append("'" + f + "', ");
    }
    delete iss;

    // What about `*' ?
    if (utils::expandEnv(f, 0).size() > 0) {
        return f;
    } else {
        err->append("'" + f + "'.");
    }

    return std::string("");
}

std::string get_path(const std::string& file) {
    size_t found;

    found = file.find_last_of("/\\");
    if (found > 0) {
        return file.substr(0, found);
    }

    return std::string("");
}

std::list<std::string> expandEnv(const std::string& var, int flags) {
    std::list<std::string> vars;

    wordexp_t p;
    if (wordexp(var.c_str(), &p, flags) == false) {
        if (p.we_wordc) {
            for (char** exp = p.we_wordv; *exp; ++exp) {
                vars.push_back(exp[0]);
            }
        }
        wordfree(&p);
    }
    return vars;
}

bool createDir(std::string dir, int mode, std::string *error) {
    int ret = mkdir(dir.data(), mode);
    if (ret != 0 && errno != EEXIST) {
        error->assign("Not able to create directory: " + dir + ": " \
            + strerror(errno) + ".");
        return false;
    }

    return true;
}

bool isFile(std::string f) {
    struct stat fileInfo;
    FILE *fp = fopen(f.c_str(), "r");
    if (fp == NULL) {
        return false;
    }
    fstat(fileno(fp), &fileInfo);
    if (!S_ISREG(fileInfo.st_mode)) {
        fclose(fp);
        return false;
    }
    fclose(fp);

    return true;
}

}  // namespace utils
}  // namespace modsecurity
<|endoftext|>


<commit_before>#pragma once

//====================================================|
//            C++ standard                            |
//                                                    |

#if __cplusplus > 201703L
#	define M_CPP                                     20
#elif __cplusplus > 201402L
#	define M_CPP                                     17
#elif __cplusplus > 201103L
#	define M_CPP                                     14
#elif __cplusplus > 199711L
#	define M_CPP                                     11
#else
#	define M_CPP                                      3
#endif

//====================================================|
//            Compiler definitions                    |
//                                                    |

#define M_COMPILER_UNKNOWN                            0
#define M_COMPILER_GCC                                1
#define M_COMPILER_MSVC                               2
#define M_COMPILER_CLANG                              3

#if defined(__GNUC__) || defined(__GNUG__)
#	define M_COMPILER M_COMPILER_GCC
#elif defined(_MSC_VER)
#	define M_COMPILER M_COMPILER_MSVC
#elif defined(__clang__)
#	define M_COMPILER M_COMPILER_CLANG
#else
#	define M_COMPILER M_COMPILER_UNKNOWN
#endif

//====================================================|
//            CPU architecture definitions            |
//                                                    |
#define M_CPU_UNKNOWN                                 0
#define M_CPU_X86                                     1
#define M_CPU_X86_64                                  2
#define M_CPU_ARM                                     3

#if M_COMPILER == M_COMPILER_GCC
#	if defined(__i386__) // __i386__ is defined for any x86 processor
#		define M_CPU M_CPU_X86

#		if defined(__i686__)
#			define M_CPU_VERSION 6
#		elif defined(__i586__)
#			define M_CPU_VERSION 5
#		elif defined(__i486__)
#			define M_CPU_VERSION 4
#		else
#			define M_CPU_VERSION 3
#		endif

#	elif defined(__x86_64__)
#		define M_CPU M_CPU_X86_64

#		define M_CPU_VERSION 0

#	elif defined(__arm__)
#		define M_CPU M_CPU_ARM

#		if defined(__thumb2__) // this macro is defined when targeting only thumb-2
#			define M_CPU_ARM_THUMB_VERSION 2
#		elif defined(__thumb__) // this macro is defined when targeting any, thumb-1 or thumb-2
#			define M_CPU_ARM_THUMB_VERSION 1
#		endif

#		if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__)
#			define M_CPU_VERSION 7
#		elif defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6__)
#			define M_CPU_VERSION 6
#		elif defined(__ARM_ARCH_5TEJ__) || defined(__ARM_ARCH_5TE__) || defined(__ARM_ARCH_5T__) || defined(__ARM_ARCH_5E__) || defined(__ARM_ARCH_5__)
#			define M_CPU_VERSION 5
#		elif defined(__ARM_ARCH_4T__) || defined(__ARM_ARCH_4__)
#			define M_CPU_VERSION 4
#		else
#			define M_CPU_VERSION 0
#		endif

#	else
#		define M_CPU M_CPU_UNKNOWN
#		define M_CPU_VERSION 0
#	endif

#elif M_COMPILER == M_COMPILER_MSVC
#	if defined(_M_IX86)
#		define M_CPU M_CPU_X86

#		if _M_IX86 == 600
#			define M_CPU_VERSION 6
#		elif _M_IX86 == 500
#			define M_CPU_VERSION 5
#		elif _M_IX86 == 400
#			define M_CPU_VERSION 4
#		else
#			define M_CPU_VERSION 3
#		endif

#	elif defined(_M_AMD64) || defined(_M_X64)
#		define M_CPU M_CPU_X86_64
#		define M_CPU_VERSION 0

#	else
#		define M_CPU M_CPU_UNKNOWN
#		define M_CPU_VERSION 0
#	endif
#else
#	define M_CPU M_CPU_UNKNOWN
#	define M_CPU_VERSION 0
#endif

//======================================|
//            OS definitions            |
//                                      |

// OS family
#define M_OS_UNKNOWN                    0
#define M_OS_LINUX                      1
#define M_OS_WINDOWS                    2
#define M_OS_MACOSX                     3
#define M_OS_UNIX                       4

// OS name
#define M_OS_NAME_UNKNOWN               0
#define M_OS_NAME_MACOSX                1
#define M_OS_NAME_IOS                   2
#define M_OS_NAME_ANDROID               3
#define M_OS_NAME_SOLARIS               4

#if defined(__linux__)
#	define M_OS M_OS_LINUX
#	if defined(__ANDROID__)
#		define M_OS_NAME M_OS_NAME_ANDROID
#	else
#		define M_OS_NAME M_OS_NAME_UNKNOWN
#	endif

// _WIN32 macro is defined for both win32 and win64. _WIN32 is the correct one, WIN32 not always defined.
#elif defined(_WIN32) || defined(WIN32)
#	define M_OS M_OS_WINDOWS
#	define M_OS_NAME M_OS_NAME_UNKNOWN
#elif defined(__APPLE__)
#	define M_OS M_OS_MACOSX
#	include <TargetConditionals.h>
#	if TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1 // iOS
#		define M_OS_NAME M_OS_NAME_IOS
#	else
#		define M_OS_NAME M_OS_NAME_MACOSX
#	endif

#elif defined(__unix) || defined(__unix__) // check for UNIX should go after check for Linux, because on Linux the __unix macro is also defined
#	define M_OS M_OS_UNIX
#	if defined(sun) || defined(__sun)
#		define M_OS_NAME M_OS_NAME_SOLARIS
#	else
#		define M_OS_NAME M_OS_NAME_UNKNOWN
#	endif
#else
#	define M_OS M_OS_UNKNOWN
#	define M_OS_NAME M_OS_NAME_UNKNOWN
#endif

#if M_OS == M_OS_WINDOWS
#	if M_COMPILER == M_COMPILER_MSVC
#		ifdef _USRDLL
#			define DLLEXPORT __declspec(dllexport)
#		else
#			define DLLEXPORT __declspec(dllimport)
#		endif
#		pragma warning(disable : 4251) // member variable needs to have dll-interface to be used by clients of class
#		pragma warning(disable : 4275) // non dll-interface class used as base for dll-interface class
#		pragma warning(disable : 4250) // method inherited via dominance
#	else
#		define DLLEXPORT
#	endif
#else
#	define DLLEXPORT
#endif
<commit_msg>add M_CPU_BITS config macro<commit_after>#pragma once

//====================================================|
//            C++ standard                            |
//                                                    |

#if __cplusplus > 201703L
#	define M_CPP                                     20
#elif __cplusplus > 201402L
#	define M_CPP                                     17
#elif __cplusplus > 201103L
#	define M_CPP                                     14
#elif __cplusplus > 199711L
#	define M_CPP                                     11
#else
#	define M_CPP                                      3
#endif

//====================================================|
//            Compiler definitions                    |
//                                                    |

#define M_COMPILER_UNKNOWN                            0
#define M_COMPILER_GCC                                1
#define M_COMPILER_MSVC                               2
#define M_COMPILER_CLANG                              3

#if defined(__GNUC__) || defined(__GNUG__)
#	define M_COMPILER M_COMPILER_GCC
#elif defined(_MSC_VER)
#	define M_COMPILER M_COMPILER_MSVC
#elif defined(__clang__)
#	define M_COMPILER M_COMPILER_CLANG
#else
#	define M_COMPILER M_COMPILER_UNKNOWN
#endif

//====================================================|
//            CPU architecture definitions            |
//                                                    |
#define M_CPU_UNKNOWN                                 0
#define M_CPU_X86                                     1
#define M_CPU_X86_64                                  2
#define M_CPU_ARM                                     3

#if M_COMPILER == M_COMPILER_GCC
#	if defined(__i386__) // __i386__ is defined for any x86 processor
#		define M_CPU M_CPU_X86
#		define M_CPU_BITS 32

#		if defined(__i686__)
#			define M_CPU_VERSION 6
#		elif defined(__i586__)
#			define M_CPU_VERSION 5
#		elif defined(__i486__)
#			define M_CPU_VERSION 4
#		else
#			define M_CPU_VERSION 3
#		endif

#	elif defined(__x86_64__)
#		define M_CPU M_CPU_X86_64
#		define M_CPU_BITS 64

#		define M_CPU_VERSION 0

#	elif defined(__arm__)
#		define M_CPU M_CPU_ARM
#		define M_CPU_BITS 32

#		if defined(__thumb2__) // this macro is defined when targeting only thumb-2
#			define M_CPU_ARM_THUMB_VERSION 2
#		elif defined(__thumb__) // this macro is defined when targeting any, thumb-1 or thumb-2
#			define M_CPU_ARM_THUMB_VERSION 1
#		endif

#		if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__)
#			define M_CPU_VERSION 7
#		elif defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6__)
#			define M_CPU_VERSION 6
#		elif defined(__ARM_ARCH_5TEJ__) || defined(__ARM_ARCH_5TE__) || defined(__ARM_ARCH_5T__) || defined(__ARM_ARCH_5E__) || defined(__ARM_ARCH_5__)
#			define M_CPU_VERSION 5
#		elif defined(__ARM_ARCH_4T__) || defined(__ARM_ARCH_4__)
#			define M_CPU_VERSION 4
#		else
#			define M_CPU_VERSION 0
#		endif

#	else
#		define M_CPU M_CPU_UNKNOWN
#		define M_CPU_BITS 0
#		define M_CPU_VERSION 0
#	endif

#elif M_COMPILER == M_COMPILER_MSVC
#	if defined(_M_IX86)
#		define M_CPU M_CPU_X86
#		define M_CPU_BITS 32

#		if _M_IX86 == 600
#			define M_CPU_VERSION 6
#		elif _M_IX86 == 500
#			define M_CPU_VERSION 5
#		elif _M_IX86 == 400
#			define M_CPU_VERSION 4
#		else
#			define M_CPU_VERSION 3
#		endif

#	elif defined(_M_AMD64) || defined(_M_X64)
#		define M_CPU M_CPU_X86_64
#		define M_CPU_BITS 64
#		define M_CPU_VERSION 0

#	else
#		define M_CPU M_CPU_UNKNOWN
#		define M_CPU_BITS 0
#		define M_CPU_VERSION 0
#	endif
#else
#	define M_CPU M_CPU_UNKNOWN
#	define M_CPU_BITS 0
#	define M_CPU_VERSION 0
#endif

//======================================|
//            OS definitions            |
//                                      |

// OS family
#define M_OS_UNKNOWN                    0
#define M_OS_LINUX                      1
#define M_OS_WINDOWS                    2
#define M_OS_MACOSX                     3
#define M_OS_UNIX                       4

// OS name
#define M_OS_NAME_UNKNOWN               0
#define M_OS_NAME_MACOSX                1
#define M_OS_NAME_IOS                   2
#define M_OS_NAME_ANDROID               3
#define M_OS_NAME_SOLARIS               4

#if defined(__linux__)
#	define M_OS M_OS_LINUX
#	if defined(__ANDROID__)
#		define M_OS_NAME M_OS_NAME_ANDROID
#	else
#		define M_OS_NAME M_OS_NAME_UNKNOWN
#	endif

// _WIN32 macro is defined for both win32 and win64. _WIN32 is the correct one, WIN32 not always defined.
#elif defined(_WIN32) || defined(WIN32)
#	define M_OS M_OS_WINDOWS
#	define M_OS_NAME M_OS_NAME_UNKNOWN
#elif defined(__APPLE__)
#	define M_OS M_OS_MACOSX
#	include <TargetConditionals.h>
#	if TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1 // iOS
#		define M_OS_NAME M_OS_NAME_IOS
#	else
#		define M_OS_NAME M_OS_NAME_MACOSX
#	endif

#elif defined(__unix) || defined(__unix__) // check for UNIX should go after check for Linux, because on Linux the __unix macro is also defined
#	define M_OS M_OS_UNIX
#	if defined(sun) || defined(__sun)
#		define M_OS_NAME M_OS_NAME_SOLARIS
#	else
#		define M_OS_NAME M_OS_NAME_UNKNOWN
#	endif
#else
#	define M_OS M_OS_UNKNOWN
#	define M_OS_NAME M_OS_NAME_UNKNOWN
#endif

#if M_OS == M_OS_WINDOWS
#	if M_COMPILER == M_COMPILER_MSVC
#		ifdef _USRDLL
#			define DLLEXPORT __declspec(dllexport)
#		else
#			define DLLEXPORT __declspec(dllimport)
#		endif
#		pragma warning(disable : 4251) // member variable needs to have dll-interface to be used by clients of class
#		pragma warning(disable : 4275) // non dll-interface class used as base for dll-interface class
#		pragma warning(disable : 4250) // method inherited via dominance
#	else
#		define DLLEXPORT
#	endif
#else
#	define DLLEXPORT
#endif
<|endoftext|>


<commit_before>#pragma once

#include <algorithm>
#include <utility>
#include <memory>
#include <string>
#include <vector>
#include <uv.h>
#include "handle.hpp"
#include "stream.hpp"
#include "util.hpp"
#include "loop.hpp"

namespace uvw {

namespace details {

enum class UVProcessFlags: std::underlying_type_t<uv_process_flags> {
    SETUID = UV_PROCESS_SETUID,
    SETGID = UV_PROCESS_SETGID,
    WINDOWS_VERBATIM_ARGUMENTS = UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS,
    DETACHED = UV_PROCESS_DETACHED,
    WINDOWS_HIDE = UV_PROCESS_WINDOWS_HIDE
};

enum class UVStdIOFlags: std::underlying_type_t<uv_stdio_flags> {
    IGNORE_STREAM = UV_IGNORE,
    CREATE_PIPE = UV_CREATE_PIPE,
    INHERIT_FD = UV_INHERIT_FD,
    INHERIT_STREAM = UV_INHERIT_STREAM,
    READABLE_PIPE = UV_READABLE_PIPE,
    WRITABLE_PIPE = UV_WRITABLE_PIPE,
    OVERLAPPED_PIPE = UV_OVERLAPPED_PIPE
};

}

/**
 * @brief ExitEvent event.
 *
 * It will be emitted by ProcessHandle according with its functionalities.
 */
struct ExitEvent {
    explicit ExitEvent(int64_t code, int sig) noexcept
        : status{code}, signal{sig}
    {}

    int64_t status; /*!< The exit status. */
    int signal; /*!< The signal that caused the process to terminate, if any. */
};

/**
 * @brief The ProcessHandle handle.
 *
 * Process handles will spawn a new process and allow the user to control it and
 * establish communication channels with it using streams.
 */
class ProcessHandle final: public Handle<ProcessHandle, uv_process_t> {
    static void exitCallback(uv_process_t *handle, int64_t exitStatus, int termSignal) {
        ProcessHandle &process = *(static_cast<ProcessHandle*>(handle->data));
        process.publish(ExitEvent{exitStatus, termSignal});
    }

public:
    using Process = details::UVProcessFlags;
    using StdIO = details::UVStdIOFlags;

    ProcessHandle(ConstructorAccess ca, std::shared_ptr<Loop> ref)
        : Handle{ca, std::move(ref)}, poFdStdio{1}
    {
        // stdin container default initialization
        poFdStdio[0].flags = static_cast<uv_stdio_flags>(StdIO::IGNORE_STREAM);
    }

    /**
     * @brief Disables inheritance for file descriptors/handles.
     *
     * Disables inheritance for file descriptors/handles that this process
     * inherited from its parent. The effect is that child processes spawned by
     * this process don’t accidentally inherit these handles.

     * It is recommended to call this function as early in your program as
     * possible, before the inherited file descriptors can be closed or
     * duplicated.
     *
     * See the official
     * [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_disable_stdio_inheritance)
     * for further details.
     */
    static void disableStdIOInheritance() noexcept {
        uv_disable_stdio_inheritance();
    }

    /**
     * @brief kill Sends the specified signal to the given PID.
     * @param pid A valid process id.
     * @param signum A valid signal identifier.
     * @return True in case of success, false otherwise.
     */
    static bool kill(int pid, int signum) noexcept {
        return (0 == uv_kill(pid, signum));
    }

    /**
     * @brief Initializes the handle.
     * @return True in case of success, false otherwise.
     */
    bool init() {
        // deferred initialization: libuv initializes process handles only when
        // uv_spawn is invoked and uvw stays true to the underlying library
        return true;
    }

    /**
     * @brief spawn Starts the process.
     *
     * If the process isn't successfully spawned, an ErrorEvent event will be
     * emitted by the handle.
     *
     * See the official
     * [documentation](http://docs.libuv.org/en/v1.x/process.html)
     * for further details.
     *
     * @param file Path pointing to the program to be executed.
     * @param args Command line arguments.
     * @param env Optional environment for the new process.
     */
    void spawn(const char *file, char **args, char **env = nullptr) {
        uv_process_options_t po;

        po.exit_cb = &exitCallback;

        po.file = file;
        po.args = args;
        po.env = env;
        po.cwd = poCwd.empty() ? nullptr : poCwd.data();
        po.flags = poFlags;
        po.uid = poUid;
        po.gid = poGid;

        /**
         * See the constructor, poFdStdio[0] is stdin. It must be poStdio[0] by
         * convention. From the official documentation:
         *
         * > The convention is that stdio[0] points to stdin, fd 1 is used
         * > for stdout, and fd 2 is stderr.
         */
        std::vector<uv_stdio_container_t> poStdio{poFdStdio.size() + poStreamStdio.size()};
        poStdio.insert(poStdio.begin(), poStreamStdio.cbegin(), poStreamStdio.cend());
        poStdio.insert(poStdio.begin(), poFdStdio.cbegin(), poFdStdio.cend());
        po.stdio_count = static_cast<decltype(po.stdio_count)>(poStdio.size());
        po.stdio = poStdio.data();

        // fake initialization so as to have leak invoked
        // see init member function for more details
        initialize([](auto...){ return 0; });

        invoke(&uv_spawn, parent(), get(), &po);
    }

    /**
     * @brief Sends the specified signal to the internal process handle.
     * @param signum A valid signal identifier.
     */
    void kill(int signum) {
        invoke(&uv_process_kill, get(), signum);
    }

    /**
     * @brief Gets the PID of the spawned process.
     *
     * It’s set after calling `spawn()`.
     *
     * @return The PID of the spawned process.
     */
    int pid() noexcept {
        return get()->pid;
    }

    /**
     * @brief Sets the current working directory for the subprocess.
     * @param path The working directory to be used when `spawn()` is invoked.
     * @return A reference to this process handle.
     */
    ProcessHandle & cwd(std::string path) noexcept {
        poCwd = path;
        return *this;
    }

    /**
     * @brief Sets flags that control how `spawn()` behaves.
     *
     * Available flags are:
     *
     * * `ProcessHandle::Process::SETUID`
     * * `ProcessHandle::Process::SETGID`
     * * `ProcessHandle::Process::WINDOWS_VERBATIM_ARGUMENTS`
     * * `ProcessHandle::Process::DETACHED`
     * * `ProcessHandle::Process::WINDOWS_HIDE`
     *
     * See the official
     * [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_process_flags)
     * for further details.
     *
     * @param flags A valid set of flags.
     * @return A reference to this process handle.
     */
    ProcessHandle & flags(Flags<Process> flags) noexcept {
        poFlags = flags;
        return *this;
    }

    /**
     * @brief Makes a `stdio` handle available to the child process.
     *
     * Available flags are:
     *
     * * `ProcessHandle::StdIO::IGNORE_STREAM`
     * * `ProcessHandle::StdIO::CREATE_PIPE`
     * * `ProcessHandle::StdIO::INHERIT_FD`
     * * `ProcessHandle::StdIO::INHERIT_STREAM`
     * * `ProcessHandle::StdIO::READABLE_PIPE`
     * * `ProcessHandle::StdIO::WRITABLE_PIPE`
     * * `ProcessHandle::StdIO::OVERLAPPED_PIPE`
     *
     * See the official
     * [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_stdio_flags)
     * for further details.
     *
     * @param stream A valid `stdio` handle.
     * @param flags A valid set of flags.
     * @return A reference to this process handle.
     */
    template<typename T, typename U>
    ProcessHandle & stdio(StreamHandle<T, U> &stream, Flags<StdIO> flags) {
        uv_stdio_container_t container;
        Flags<StdIO>::Type fgs = flags;
        container.flags = static_cast<uv_stdio_flags>(fgs);
        container.data.stream = get<uv_stream_t>(stream);
        poStreamStdio.push_back(std::move(container));
        return *this;
    }

    /**
     * @brief Makes a file descriptor available to the child process.
     *
     * Available flags are:
     *
     * * `ProcessHandle::StdIO::IGNORE_STREAM`
     * * `ProcessHandle::StdIO::CREATE_PIPE`
     * * `ProcessHandle::StdIO::INHERIT_FD`
     * * `ProcessHandle::StdIO::INHERIT_STREAM`
     * * `ProcessHandle::StdIO::READABLE_PIPE`
     * * `ProcessHandle::StdIO::WRITABLE_PIPE`
     * * `ProcessHandle::StdIO::OVERLAPPED_PIPE`
     *
     * Default file descriptors are:
     *     * `uvw::StdIN` for `stdin`
     *     * `uvw::StdOUT` for `stdout`
     *     * `uvw::StdERR` for `stderr`
     *
     * See the official
     * [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_stdio_flags)
     * for further details.
     *
     * @param fd A valid file descriptor.
     * @param flags A valid set of flags.
     * @return A reference to this process handle.
     */
    ProcessHandle & stdio(FileHandle fd, Flags<StdIO> flags) {
        auto fgs = static_cast<uv_stdio_flags>(Flags<StdIO>::Type{flags});

        auto actual = FileHandle::Type{fd};

        if(actual == FileHandle::Type{StdIN}) {
            poFdStdio[0].flags = fgs;
        } else {
            auto it = std::find_if(poFdStdio.begin(), poFdStdio.end(), [actual](auto &&container){
                return container.data.fd == actual;
            });

            if(it == poFdStdio.cend()) {
                uv_stdio_container_t container;
                container.flags = fgs;
                container.data.fd = actual;
                poFdStdio.push_back(std::move(container));
            } else {
                it->flags = fgs;
                it->data.fd = actual;
            }
        }

        return *this;
    }

    /**
     * @brief Sets the child process' user id.
     * @param id A valid user id to be used.
     * @return A reference to this process handle.
     */
    ProcessHandle & uid(Uid id) {
        poUid = id;
        return *this;
    }

    /**
     * @brief Sets the child process' group id.
     * @param id A valid group id to be used.
     * @return A reference to this process handle.
     */
    ProcessHandle & gid(Gid id) {
        poGid = id;
        return *this;
    }

private:
    std::string poCwd;
    Flags<Process> poFlags;
    std::vector<uv_stdio_container_t> poFdStdio;
    std::vector<uv_stdio_container_t> poStreamStdio;
    Uid poUid;
    Gid poGid;
};

}
<commit_msg>A fix to process I/O initialization. Closes #119. (#120)<commit_after>#pragma once

#include <algorithm>
#include <utility>
#include <memory>
#include <string>
#include <vector>
#include <uv.h>
#include "handle.hpp"
#include "stream.hpp"
#include "util.hpp"
#include "loop.hpp"

namespace uvw {

namespace details {

enum class UVProcessFlags: std::underlying_type_t<uv_process_flags> {
    SETUID = UV_PROCESS_SETUID,
    SETGID = UV_PROCESS_SETGID,
    WINDOWS_VERBATIM_ARGUMENTS = UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS,
    DETACHED = UV_PROCESS_DETACHED,
    WINDOWS_HIDE = UV_PROCESS_WINDOWS_HIDE
};

enum class UVStdIOFlags: std::underlying_type_t<uv_stdio_flags> {
    IGNORE_STREAM = UV_IGNORE,
    CREATE_PIPE = UV_CREATE_PIPE,
    INHERIT_FD = UV_INHERIT_FD,
    INHERIT_STREAM = UV_INHERIT_STREAM,
    READABLE_PIPE = UV_READABLE_PIPE,
    WRITABLE_PIPE = UV_WRITABLE_PIPE,
    OVERLAPPED_PIPE = UV_OVERLAPPED_PIPE
};

}

/**
 * @brief ExitEvent event.
 *
 * It will be emitted by ProcessHandle according with its functionalities.
 */
struct ExitEvent {
    explicit ExitEvent(int64_t code, int sig) noexcept
        : status{code}, signal{sig}
    {}

    int64_t status; /*!< The exit status. */
    int signal; /*!< The signal that caused the process to terminate, if any. */
};

/**
 * @brief The ProcessHandle handle.
 *
 * Process handles will spawn a new process and allow the user to control it and
 * establish communication channels with it using streams.
 */
class ProcessHandle final: public Handle<ProcessHandle, uv_process_t> {
    static void exitCallback(uv_process_t *handle, int64_t exitStatus, int termSignal) {
        ProcessHandle &process = *(static_cast<ProcessHandle*>(handle->data));
        process.publish(ExitEvent{exitStatus, termSignal});
    }

public:
    using Process = details::UVProcessFlags;
    using StdIO = details::UVStdIOFlags;

    ProcessHandle(ConstructorAccess ca, std::shared_ptr<Loop> ref)
        : Handle{ca, std::move(ref)}, poFdStdio{1}
    {
        // stdin container default initialization
        poFdStdio[0].flags = static_cast<uv_stdio_flags>(StdIO::IGNORE_STREAM);
    }

    /**
     * @brief Disables inheritance for file descriptors/handles.
     *
     * Disables inheritance for file descriptors/handles that this process
     * inherited from its parent. The effect is that child processes spawned by
     * this process don’t accidentally inherit these handles.

     * It is recommended to call this function as early in your program as
     * possible, before the inherited file descriptors can be closed or
     * duplicated.
     *
     * See the official
     * [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_disable_stdio_inheritance)
     * for further details.
     */
    static void disableStdIOInheritance() noexcept {
        uv_disable_stdio_inheritance();
    }

    /**
     * @brief kill Sends the specified signal to the given PID.
     * @param pid A valid process id.
     * @param signum A valid signal identifier.
     * @return True in case of success, false otherwise.
     */
    static bool kill(int pid, int signum) noexcept {
        return (0 == uv_kill(pid, signum));
    }

    /**
     * @brief Initializes the handle.
     * @return True in case of success, false otherwise.
     */
    bool init() {
        // deferred initialization: libuv initializes process handles only when
        // uv_spawn is invoked and uvw stays true to the underlying library
        return true;
    }

    /**
     * @brief spawn Starts the process.
     *
     * If the process isn't successfully spawned, an ErrorEvent event will be
     * emitted by the handle.
     *
     * See the official
     * [documentation](http://docs.libuv.org/en/v1.x/process.html)
     * for further details.
     *
     * @param file Path pointing to the program to be executed.
     * @param args Command line arguments.
     * @param env Optional environment for the new process.
     */
    void spawn(const char *file, char **args, char **env = nullptr) {
        uv_process_options_t po;

        po.exit_cb = &exitCallback;

        po.file = file;
        po.args = args;
        po.env = env;
        po.cwd = poCwd.empty() ? nullptr : poCwd.data();
        po.flags = poFlags;
        po.uid = poUid;
        po.gid = poGid;

        /**
         * See the constructor, poFdStdio[0] is stdin. It must be poStdio[0] by
         * convention. From the official documentation:
         *
         * > The convention is that stdio[0] points to stdin, fd 1 is used
         * > for stdout, and fd 2 is stderr.
         */
        std::vector<uv_stdio_container_t> poStdio;
        poStdio.reserve(poFdStdio.size() + poStreamStdio.size());
        poStdio.insert(poStdio.begin(), poFdStdio.cbegin(), poFdStdio.cend());
        poStdio.insert(poStdio.end(), poStreamStdio.cbegin(), poStreamStdio.cend());

        po.stdio_count = static_cast<decltype(po.stdio_count)>(poStdio.size());
        po.stdio = poStdio.data();

        // fake initialization so as to have leak invoked
        // see init member function for more details
        initialize([](auto...){ return 0; });

        invoke(&uv_spawn, parent(), get(), &po);
    }

    /**
     * @brief Sends the specified signal to the internal process handle.
     * @param signum A valid signal identifier.
     */
    void kill(int signum) {
        invoke(&uv_process_kill, get(), signum);
    }

    /**
     * @brief Gets the PID of the spawned process.
     *
     * It’s set after calling `spawn()`.
     *
     * @return The PID of the spawned process.
     */
    int pid() noexcept {
        return get()->pid;
    }

    /**
     * @brief Sets the current working directory for the subprocess.
     * @param path The working directory to be used when `spawn()` is invoked.
     * @return A reference to this process handle.
     */
    ProcessHandle & cwd(std::string path) noexcept {
        poCwd = path;
        return *this;
    }

    /**
     * @brief Sets flags that control how `spawn()` behaves.
     *
     * Available flags are:
     *
     * * `ProcessHandle::Process::SETUID`
     * * `ProcessHandle::Process::SETGID`
     * * `ProcessHandle::Process::WINDOWS_VERBATIM_ARGUMENTS`
     * * `ProcessHandle::Process::DETACHED`
     * * `ProcessHandle::Process::WINDOWS_HIDE`
     *
     * See the official
     * [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_process_flags)
     * for further details.
     *
     * @param flags A valid set of flags.
     * @return A reference to this process handle.
     */
    ProcessHandle & flags(Flags<Process> flags) noexcept {
        poFlags = flags;
        return *this;
    }

    /**
     * @brief Makes a `stdio` handle available to the child process.
     *
     * Available flags are:
     *
     * * `ProcessHandle::StdIO::IGNORE_STREAM`
     * * `ProcessHandle::StdIO::CREATE_PIPE`
     * * `ProcessHandle::StdIO::INHERIT_FD`
     * * `ProcessHandle::StdIO::INHERIT_STREAM`
     * * `ProcessHandle::StdIO::READABLE_PIPE`
     * * `ProcessHandle::StdIO::WRITABLE_PIPE`
     * * `ProcessHandle::StdIO::OVERLAPPED_PIPE`
     *
     * See the official
     * [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_stdio_flags)
     * for further details.
     *
     * @param stream A valid `stdio` handle.
     * @param flags A valid set of flags.
     * @return A reference to this process handle.
     */
    template<typename T, typename U>
    ProcessHandle & stdio(StreamHandle<T, U> &stream, Flags<StdIO> flags) {
        uv_stdio_container_t container;
        Flags<StdIO>::Type fgs = flags;
        container.flags = static_cast<uv_stdio_flags>(fgs);
        container.data.stream = get<uv_stream_t>(stream);
        poStreamStdio.push_back(std::move(container));
        return *this;
    }

    /**
     * @brief Makes a file descriptor available to the child process.
     *
     * Available flags are:
     *
     * * `ProcessHandle::StdIO::IGNORE_STREAM`
     * * `ProcessHandle::StdIO::CREATE_PIPE`
     * * `ProcessHandle::StdIO::INHERIT_FD`
     * * `ProcessHandle::StdIO::INHERIT_STREAM`
     * * `ProcessHandle::StdIO::READABLE_PIPE`
     * * `ProcessHandle::StdIO::WRITABLE_PIPE`
     * * `ProcessHandle::StdIO::OVERLAPPED_PIPE`
     *
     * Default file descriptors are:
     *     * `uvw::StdIN` for `stdin`
     *     * `uvw::StdOUT` for `stdout`
     *     * `uvw::StdERR` for `stderr`
     *
     * See the official
     * [documentation](http://docs.libuv.org/en/v1.x/process.html#c.uv_stdio_flags)
     * for further details.
     *
     * @param fd A valid file descriptor.
     * @param flags A valid set of flags.
     * @return A reference to this process handle.
     */
    ProcessHandle & stdio(FileHandle fd, Flags<StdIO> flags) {
        auto fgs = static_cast<uv_stdio_flags>(Flags<StdIO>::Type{flags});

        auto actual = FileHandle::Type{fd};

        if(actual == FileHandle::Type{StdIN}) {
            poFdStdio[0].flags = fgs;
        } else {
            auto it = std::find_if(poFdStdio.begin(), poFdStdio.end(), [actual](auto &&container){
                return container.data.fd == actual;
            });

            if(it == poFdStdio.cend()) {
                uv_stdio_container_t container;
                container.flags = fgs;
                container.data.fd = actual;
                poFdStdio.push_back(std::move(container));
            } else {
                it->flags = fgs;
                it->data.fd = actual;
            }
        }

        return *this;
    }

    /**
     * @brief Sets the child process' user id.
     * @param id A valid user id to be used.
     * @return A reference to this process handle.
     */
    ProcessHandle & uid(Uid id) {
        poUid = id;
        return *this;
    }

    /**
     * @brief Sets the child process' group id.
     * @param id A valid group id to be used.
     * @return A reference to this process handle.
     */
    ProcessHandle & gid(Gid id) {
        poGid = id;
        return *this;
    }

private:
    std::string poCwd;
    Flags<Process> poFlags;
    std::vector<uv_stdio_container_t> poFdStdio;
    std::vector<uv_stdio_container_t> poStreamStdio;
    Uid poUid;
    Gid poGid;
};

}
<|endoftext|>


<commit_before>//
// Copyright (C) 2013
// Alessio Sclocco <a.sclocco@vu.nl>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#include <iostream>
#include <string>
#include <vector>
#include <exception>
#include <fstream>
#include <iomanip>
#include <limits>
#include <cmath>
using std::cout;
using std::cerr;
using std::endl;
using std::string;
using std::vector;
using std::exception;
using std::ofstream;
using std::fixed;
using std::setprecision;
using std::setw;
using std::numeric_limits;

#include <ArgumentList.hpp>
using isa::utils::ArgumentList;
#include <InitializeOpenCL.hpp>
using isa::OpenCL::initializeOpenCL;
#include <CLData.hpp>
using isa::OpenCL::CLData;
#include <utils.hpp>
using isa::utils::same;
using isa::utils::pad;
#include <Transpose.hpp>
using isa::OpenCL::Transpose;

typedef float dataType;
const string typeName("float");
const unsigned int padding = 32;
const bool DEBUG = false;

int main(int argc, char *argv[]) {
	unsigned int clPlatformID = 0;
	unsigned int clDeviceID = 0;
	unsigned int nrThreads = 0;
	unsigned int M = 0;
	unsigned int N = 0;
	long long unsigned int wrongValues = 0;
	CLData< dataType > * inputData = new CLData< dataType >("InputData", true);
	CLData< dataType > * transposeData = new CLData< dataType >("TransposeData", true);

	try {
		ArgumentList args(argc, argv);

		clPlatformID = args.getSwitchArgument< unsigned int >("-opencl_platform");
		clDeviceID = args.getSwitchArgument< unsigned int >("-opencl_device");
		nrThreads = args.getSwitchArgument< unsigned int >("-threads");
		M = args.getSwitchArgument< unsigned int >("-M");
		N = args.getSwitchArgument< unsigned int >("-N");

	} catch ( exception &err ) {
		cerr << err.what() << endl;
		return 1;
	}

	cl::Context * clContext = new cl::Context();
	vector< cl::Platform > * clPlatforms = new vector< cl::Platform >();
	vector< cl::Device > * clDevices = new vector< cl::Device >();
	vector< vector< cl::CommandQueue > > * clQueues = new vector< vector < cl::CommandQueue > >();

	initializeOpenCL(clPlatformID, 1, clPlatforms, clContext, clDevices, clQueues);

	// Allocate memory
	inputData->allocateHostData(M * pad(N, padding));
	transposeData->allocateHostData(N * pad(M, padding));

	inputData->setCLContext(clContext);
	inputData->setCLQueue(&((clQueues->at(clDeviceID)).at(0)));
	transposeData->setCLContext(clContext);
	transposeData->setCLQueue(&((clQueues->at(clDeviceID)).at(0)));

	try {
		inputData->setDeviceReadOnly();
		inputData->allocateDeviceData();
		transposeData->setDeviceWriteOnly();
		transposeData->allocateDeviceData();
	} catch ( OpenCLError err ) {
		cerr << err.what() << endl;
		return 1;
	}

	for ( unsigned int m = 0; m < M; m++ ) {
		for ( unsigned int n = 0; n < N; n++ ) {
			inputData->setHostDataItem((m * pad(N, padding)) + n, n);
			if ( DEBUG ) {
				cout << setw(3) << inputData->getHostDataItem((m * pad(N, padding)) + n) << " ";
			}
		}
		if ( DEBUG ) {
			cout << endl;
		}
	}
	if ( DEBUG ) {
		cout << endl;
	}

	// Test
	try {
		// Generate kernel
		Transpose< dataType > clTranspose("clTranspose", typeName);
		clTranspose.bindOpenCL(clContext, &(clDevices->at(clDeviceID)), &((clQueues->at(clDeviceID)).at(0)));
		clTranspose.setDimensions(M, N);
		clTranspose.setPaddingFactor(padding);
		clTranspose.setNrThreadsPerBlock(nrThreads);
		clTranspose.generateCode();

		inputData->copyHostToDevice();
		clTranspose(inputData, transposeData);
		transposeData->copyDeviceToHost();
	} catch ( OpenCLError err ) {
		cerr << err.what() << endl;
		return 1;
	}

	// Check
	for ( unsigned int m = 0; m < M; m++ ) {
		for ( unsigned int n = 0; n < N; n++ ) {
			if ( !same(inputData->getHostDataItem((m * pad(N, padding)) + n), transposeData->getHostDataItem((n * pad(M, padding)) + m)) ) {
				wrongValues++;
			}
		}
	}
	if ( DEBUG ) {
		for ( unsigned int n = 0; n < N; n++ ) {
			for ( unsigned int m = 0; m < M; m++ ) {
				cout << cout << setw(3) << transposeData->getHostDataItem((n * pad(M, padding)) + m) << " ";
			}
			cout << endl;
		}
		cout << endl;
	}

	cout << endl;
	cout << "Wrong samples: " << wrongValues << " (" << (wrongValues * 100) / (static_cast< long long unsigned int >(M) * N) << "%)." << endl;
	cout << endl;

	return 0;
}
<commit_msg>Another typo. Works on GPUs so far, fixing it for CPUs.<commit_after>//
// Copyright (C) 2013
// Alessio Sclocco <a.sclocco@vu.nl>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#include <iostream>
#include <string>
#include <vector>
#include <exception>
#include <fstream>
#include <iomanip>
#include <limits>
#include <cmath>
using std::cout;
using std::cerr;
using std::endl;
using std::string;
using std::vector;
using std::exception;
using std::ofstream;
using std::fixed;
using std::setprecision;
using std::setw;
using std::numeric_limits;

#include <ArgumentList.hpp>
using isa::utils::ArgumentList;
#include <InitializeOpenCL.hpp>
using isa::OpenCL::initializeOpenCL;
#include <CLData.hpp>
using isa::OpenCL::CLData;
#include <utils.hpp>
using isa::utils::same;
using isa::utils::pad;
#include <Transpose.hpp>
using isa::OpenCL::Transpose;

typedef float dataType;
const string typeName("float");
const unsigned int padding = 32;
const bool DEBUG = false;

int main(int argc, char *argv[]) {
	unsigned int clPlatformID = 0;
	unsigned int clDeviceID = 0;
	unsigned int nrThreads = 0;
	unsigned int M = 0;
	unsigned int N = 0;
	long long unsigned int wrongValues = 0;
	CLData< dataType > * inputData = new CLData< dataType >("InputData", true);
	CLData< dataType > * transposeData = new CLData< dataType >("TransposeData", true);

	try {
		ArgumentList args(argc, argv);

		clPlatformID = args.getSwitchArgument< unsigned int >("-opencl_platform");
		clDeviceID = args.getSwitchArgument< unsigned int >("-opencl_device");
		nrThreads = args.getSwitchArgument< unsigned int >("-threads");
		M = args.getSwitchArgument< unsigned int >("-M");
		N = args.getSwitchArgument< unsigned int >("-N");

	} catch ( exception &err ) {
		cerr << err.what() << endl;
		return 1;
	}

	cl::Context * clContext = new cl::Context();
	vector< cl::Platform > * clPlatforms = new vector< cl::Platform >();
	vector< cl::Device > * clDevices = new vector< cl::Device >();
	vector< vector< cl::CommandQueue > > * clQueues = new vector< vector < cl::CommandQueue > >();

	initializeOpenCL(clPlatformID, 1, clPlatforms, clContext, clDevices, clQueues);

	// Allocate memory
	inputData->allocateHostData(M * pad(N, padding));
	transposeData->allocateHostData(N * pad(M, padding));

	inputData->setCLContext(clContext);
	inputData->setCLQueue(&((clQueues->at(clDeviceID)).at(0)));
	transposeData->setCLContext(clContext);
	transposeData->setCLQueue(&((clQueues->at(clDeviceID)).at(0)));

	try {
		inputData->setDeviceReadOnly();
		inputData->allocateDeviceData();
		transposeData->setDeviceWriteOnly();
		transposeData->allocateDeviceData();
	} catch ( OpenCLError err ) {
		cerr << err.what() << endl;
		return 1;
	}

	for ( unsigned int m = 0; m < M; m++ ) {
		for ( unsigned int n = 0; n < N; n++ ) {
			inputData->setHostDataItem((m * pad(N, padding)) + n, n);
			if ( DEBUG ) {
				cout << setw(3) << inputData->getHostDataItem((m * pad(N, padding)) + n) << " ";
			}
		}
		if ( DEBUG ) {
			cout << endl;
		}
	}
	if ( DEBUG ) {
		cout << endl;
	}

	// Test
	try {
		// Generate kernel
		Transpose< dataType > clTranspose("clTranspose", typeName);
		clTranspose.bindOpenCL(clContext, &(clDevices->at(clDeviceID)), &((clQueues->at(clDeviceID)).at(0)));
		clTranspose.setDimensions(M, N);
		clTranspose.setPaddingFactor(padding);
		clTranspose.setNrThreadsPerBlock(nrThreads);
		clTranspose.generateCode();

		inputData->copyHostToDevice();
		clTranspose(inputData, transposeData);
		transposeData->copyDeviceToHost();
	} catch ( OpenCLError err ) {
		cerr << err.what() << endl;
		return 1;
	}

	// Check
	for ( unsigned int m = 0; m < M; m++ ) {
		for ( unsigned int n = 0; n < N; n++ ) {
			if ( !same(inputData->getHostDataItem((m * pad(N, padding)) + n), transposeData->getHostDataItem((n * pad(M, padding)) + m)) ) {
				wrongValues++;
			}
		}
	}
	if ( DEBUG ) {
		for ( unsigned int n = 0; n < N; n++ ) {
			for ( unsigned int m = 0; m < M; m++ ) {
				cout << setw(3) << transposeData->getHostDataItem((n * pad(M, padding)) + m) << " ";
			}
			cout << endl;
		}
		cout << endl;
	}

	cout << endl;
	cout << "Wrong samples: " << wrongValues << " (" << (wrongValues * 100) / (static_cast< long long unsigned int >(M) * N) << "%)." << endl;
	cout << endl;

	return 0;
}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- vim:set ts=4 sw=4 sts=4 noet: */
#include "file.h"

#include <cstdio>
#include "utf8path.h"

namespace flint {
namespace file {

bool Txt(const char *filename, Format *format, const boost::filesystem::path &dir)
{
	boost::filesystem::path path = GetPathFromUtf8(filename);
	if (path.empty())
		return false;
	std::string path_s = path.string();
	if (!DetectFormat(path_s.c_str(), format))
		return false;
	FILE *fp;
	if (dir.empty()) {
		fp = std::fopen("file.txt", "w");
	} else {
		auto file_txt = dir / "file.txt";
		fp = std::fopen(file_txt.string().c_str(), "w");
	}
	if (!fp) {
		std::perror(filename);
		return false;
	}
	switch (*format) {
	case file::kCellml:
		fprintf(fp, "cellml\n");
		break;
	case file::kIsml:
		fprintf(fp, "isml\n");
		break;
	case file::kMathml:
		fprintf(fp, "mathml\n");
		break;
	case file::kPhml:
		fprintf(fp, "phml\n");
		break;
	case file::kPhsp:
		fprintf(fp, "phsp\n");
		break;
	case file::kPhz:
		fprintf(fp, "phz\n");
		break;
	case file::kSbml:
		fprintf(fp, "sbml\n");
		break;
	case file::kSedml:
		fprintf(fp, "sedml\n");
		break;
	}
	std::fclose(fp);
	return true;
}

}
}
<commit_msg>Prefer binary output to text mode<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- vim:set ts=4 sw=4 sts=4 noet: */
#include "file.h"

#include <cstdio>
#include "utf8path.h"

namespace flint {
namespace file {

bool Txt(const char *filename, Format *format, const boost::filesystem::path &dir)
{
	boost::filesystem::path path = GetPathFromUtf8(filename);
	if (path.empty())
		return false;
	std::string path_s = path.string();
	if (!DetectFormat(path_s.c_str(), format))
		return false;
	FILE *fp;
	if (dir.empty()) {
		fp = std::fopen("file.txt", "wb");
	} else {
		auto file_txt = dir / "file.txt";
		fp = std::fopen(file_txt.string().c_str(), "wb");
	}
	if (!fp) {
		std::perror(filename);
		return false;
	}
	switch (*format) {
	case file::kCellml:
		fprintf(fp, "cellml\n");
		break;
	case file::kIsml:
		fprintf(fp, "isml\n");
		break;
	case file::kMathml:
		fprintf(fp, "mathml\n");
		break;
	case file::kPhml:
		fprintf(fp, "phml\n");
		break;
	case file::kPhsp:
		fprintf(fp, "phsp\n");
		break;
	case file::kPhz:
		fprintf(fp, "phz\n");
		break;
	case file::kSbml:
		fprintf(fp, "sbml\n");
		break;
	case file::kSedml:
		fprintf(fp, "sedml\n");
		break;
	}
	std::fclose(fp);
	return true;
}

}
}
<|endoftext|>