<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/browsing_data_remover.h"

#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/download_manager.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/session_service.h"
#include "chrome/browser/tab_restore_service.h"
#include "chrome/browser/template_url_model.h"
#include "chrome/browser/user_metrics.h"
#include "chrome/browser/webdata/web_data_service.h"
#include "chrome/common/notification_service.h"
#include "net/base/cookie_monster.h"
#include "net/disk_cache/disk_cache.h"
#include "net/http/http_cache.h"
#include "net/url_request/url_request_context.h"
#include "webkit/glue/password_form.h"

using base::Time;

// Done so that we can use invokeLater on BrowsingDataRemovers and not have
// BrowsingDataRemover implement RefCounted.
void RunnableMethodTraits<BrowsingDataRemover>::RetainCallee(
    BrowsingDataRemover* remover) {
}
void RunnableMethodTraits<BrowsingDataRemover>::ReleaseCallee(
    BrowsingDataRemover* remover) {
}

BrowsingDataRemover::BrowsingDataRemover(Profile* profile, Time delete_begin,
                                         Time delete_end)
    : profile_(profile),
      delete_begin_(delete_begin),
      delete_end_(delete_end),
      removing_(false),
      waiting_for_keywords_(false),
      waiting_for_clear_history_(false),
      waiting_for_clear_cache_(false) {
  DCHECK(profile);
}

BrowsingDataRemover::~BrowsingDataRemover() {
  DCHECK(all_done());
}

void BrowsingDataRemover::Remove(int remove_mask) {
  DCHECK(!removing_);
  removing_ = true;

  if (remove_mask & REMOVE_HISTORY) {
    HistoryService* history_service =
        profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
    if (history_service) {
      UserMetrics::RecordAction(L"ClearBrowsingData_History", profile_);
      waiting_for_clear_history_ = true;
      history_service->ExpireHistoryBetween(delete_begin_, delete_end_,
          &request_consumer_,
          NewCallback(this, &BrowsingDataRemover::OnHistoryDeletionDone));
    }

    // As part of history deletion we also delete the auto-generated keywords.
    TemplateURLModel* keywords_model = profile_->GetTemplateURLModel();
    if (keywords_model && !keywords_model->loaded()) {
      waiting_for_keywords_ = true;
      NotificationService::current()->AddObserver(
          this, TEMPLATE_URL_MODEL_LOADED,
          Source<TemplateURLModel>(keywords_model));
      keywords_model->Load();
    } else if (keywords_model) {
      keywords_model->RemoveAutoGeneratedBetween(delete_begin_, delete_end_);
    }

    // We also delete the list of recently closed tabs. Since these expire,
    // they can't be more than a day old, so we can simply clear them all.
    TabRestoreService* tab_service = profile_->GetTabRestoreService();
    if (tab_service)
      tab_service->ClearHistoricalTabs();

    // We also delete the last session when we delete the history.
    SessionService* session_service = profile_->GetSessionService();
    if (session_service)
      session_service->DeleteSession(false);   // Last session.
  }

  if (remove_mask & REMOVE_DOWNLOADS) {
    UserMetrics::RecordAction(L"ClearBrowsingData_Downloads", profile_);
    DownloadManager* download_manager = profile_->GetDownloadManager();
    download_manager->RemoveDownloadsBetween(delete_begin_, delete_end_);
  }

  if (remove_mask & REMOVE_COOKIES) {
    UserMetrics::RecordAction(L"ClearBrowsingData_Cookies", profile_);
    net::CookieMonster* cookie_monster =
        profile_->GetRequestContext()->cookie_store();
    cookie_monster->DeleteAllCreatedBetween(delete_begin_, delete_end_, true);
  }

  if (remove_mask & REMOVE_PASSWORDS) {
    UserMetrics::RecordAction(L"ClearBrowsingData_Passwords", profile_);
    WebDataService* web_data_service =
        profile_->GetWebDataService(Profile::EXPLICIT_ACCESS);

    web_data_service->RemoveLoginsCreatedBetween(delete_begin_, delete_end_);
  }

  if (remove_mask & REMOVE_FORM_DATA) {
    // TODO(jcampan): bug #3870 hook-up Peterson's code here.
    NOTREACHED() << "to be implemented";
  }

  if (remove_mask & REMOVE_CACHE) {
    // Invoke ClearBrowsingDataView::ClearCache on the IO thread.
    base::Thread* thread = g_browser_process->io_thread();
    if (thread) {
      waiting_for_clear_cache_ = true;
      UserMetrics::RecordAction(L"ClearBrowsingData_Cache", profile_);
      thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
          this,
          &BrowsingDataRemover::ClearCacheOnIOThread,
          delete_begin_,
          delete_end_,
          MessageLoop::current()));
    }
  }

  NotifyAndDeleteIfDone();
}

void BrowsingDataRemover::AddObserver(Observer* observer) {
  observer_list_.AddObserver(observer);
}

void BrowsingDataRemover::RemoveObserver(Observer* observer) {
  observer_list_.RemoveObserver(observer);
}

void BrowsingDataRemover::OnHistoryDeletionDone() {
  waiting_for_clear_history_ = false;
  NotifyAndDeleteIfDone();
}

void BrowsingDataRemover::Observe(NotificationType type,
                                  const NotificationSource& source,
                                  const NotificationDetails& details) {
  // TODO(brettw) bug 1139736: This should also observe session
  // clearing (what about other things such as passwords, etc.?) and wait for
  // them to complete before continuing.
  DCHECK(type == TEMPLATE_URL_MODEL_LOADED);
  TemplateURLModel* model = Source<TemplateURLModel>(source).ptr();
  if (model->profile() == profile_) {
    NotificationService::current()->RemoveObserver(
        this, TEMPLATE_URL_MODEL_LOADED,
        Source<TemplateURLModel>(model));

    model->RemoveAutoGeneratedBetween(delete_begin_, delete_end_);

    waiting_for_keywords_ = false;

    NotifyAndDeleteIfDone();
  }
}

void BrowsingDataRemover::NotifyAndDeleteIfDone() {
  // TODO(brettw) bug 1139736: see TODO in Observe() above.
  if (!all_done())
    return;

  FOR_EACH_OBSERVER(Observer, observer_list_, OnBrowsingDataRemoverDone());

  // History requests aren't happy if you delete yourself from the callback.
  // As such, we do a delete later.
  MessageLoop::current()->DeleteSoon(FROM_HERE, this);
}

void BrowsingDataRemover::ClearedCache() {
  waiting_for_clear_cache_ = false;

  NotifyAndDeleteIfDone();
}

void BrowsingDataRemover::ClearCacheOnIOThread(Time delete_begin,
                                               Time delete_end,
                                               MessageLoop* ui_loop) {
  // This function should be called on the IO thread.
  DCHECK(MessageLoop::current() ==
         ChromeThread::GetMessageLoop(ChromeThread::IO));

  // Get a pointer to the cache.
  net::HttpTransactionFactory* factory =
      profile_->GetRequestContext()->http_transaction_factory();
  disk_cache::Backend* cache = factory->GetCache()->disk_cache();

  // The cache can be empty, for example on startup, in which case |cache| will
  // be null and we don't need to do anything.
  if (cache) {
    if (delete_begin.is_null())
      cache->DoomAllEntries();
    else
      cache->DoomEntriesBetween(delete_begin, delete_end);
  }

  // Notify the UI thread that we are done.
  ui_loop->PostTask(FROM_HERE, NewRunnableMethod(
      this, &BrowsingDataRemover::ClearedCache));
}
<commit_msg>Fix hang in clearing browsing data from incognito mode.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/browsing_data_remover.h"

#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/download_manager.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/session_service.h"
#include "chrome/browser/tab_restore_service.h"
#include "chrome/browser/template_url_model.h"
#include "chrome/browser/user_metrics.h"
#include "chrome/browser/webdata/web_data_service.h"
#include "chrome/common/notification_service.h"
#include "net/base/cookie_monster.h"
#include "net/disk_cache/disk_cache.h"
#include "net/http/http_cache.h"
#include "net/url_request/url_request_context.h"
#include "webkit/glue/password_form.h"

using base::Time;

// Done so that we can use invokeLater on BrowsingDataRemovers and not have
// BrowsingDataRemover implement RefCounted.
void RunnableMethodTraits<BrowsingDataRemover>::RetainCallee(
    BrowsingDataRemover* remover) {
}
void RunnableMethodTraits<BrowsingDataRemover>::ReleaseCallee(
    BrowsingDataRemover* remover) {
}

BrowsingDataRemover::BrowsingDataRemover(Profile* profile, Time delete_begin,
                                         Time delete_end)
    : profile_(profile),
      delete_begin_(delete_begin),
      delete_end_(delete_end),
      removing_(false),
      waiting_for_keywords_(false),
      waiting_for_clear_history_(false),
      waiting_for_clear_cache_(false) {
  DCHECK(profile);
}

BrowsingDataRemover::~BrowsingDataRemover() {
  DCHECK(all_done());
}

void BrowsingDataRemover::Remove(int remove_mask) {
  DCHECK(!removing_);
  removing_ = true;

  if (remove_mask & REMOVE_HISTORY) {
    HistoryService* history_service =
        profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
    if (history_service) {
      UserMetrics::RecordAction(L"ClearBrowsingData_History", profile_);
      waiting_for_clear_history_ = true;
      history_service->ExpireHistoryBetween(delete_begin_, delete_end_,
          &request_consumer_,
          NewCallback(this, &BrowsingDataRemover::OnHistoryDeletionDone));
    }

    // As part of history deletion we also delete the auto-generated keywords.
    TemplateURLModel* keywords_model = profile_->GetTemplateURLModel();
    if (keywords_model && !keywords_model->loaded()) {
      waiting_for_keywords_ = true;
      NotificationService::current()->AddObserver(
          this, TEMPLATE_URL_MODEL_LOADED,
          Source<TemplateURLModel>(keywords_model));
      keywords_model->Load();
    } else if (keywords_model) {
      keywords_model->RemoveAutoGeneratedBetween(delete_begin_, delete_end_);
    }

    // We also delete the list of recently closed tabs. Since these expire,
    // they can't be more than a day old, so we can simply clear them all.
    TabRestoreService* tab_service = profile_->GetTabRestoreService();
    if (tab_service)
      tab_service->ClearHistoricalTabs();

    // We also delete the last session when we delete the history.
    SessionService* session_service = profile_->GetSessionService();
    if (session_service)
      session_service->DeleteSession(false);   // Last session.
  }

  if (remove_mask & REMOVE_DOWNLOADS) {
    UserMetrics::RecordAction(L"ClearBrowsingData_Downloads", profile_);
    DownloadManager* download_manager = profile_->GetDownloadManager();
    download_manager->RemoveDownloadsBetween(delete_begin_, delete_end_);
  }

  if (remove_mask & REMOVE_COOKIES) {
    UserMetrics::RecordAction(L"ClearBrowsingData_Cookies", profile_);
    net::CookieMonster* cookie_monster =
        profile_->GetRequestContext()->cookie_store();
    cookie_monster->DeleteAllCreatedBetween(delete_begin_, delete_end_, true);
  }

  if (remove_mask & REMOVE_PASSWORDS) {
    UserMetrics::RecordAction(L"ClearBrowsingData_Passwords", profile_);
    WebDataService* web_data_service =
        profile_->GetWebDataService(Profile::EXPLICIT_ACCESS);

    web_data_service->RemoveLoginsCreatedBetween(delete_begin_, delete_end_);
  }

  if (remove_mask & REMOVE_FORM_DATA) {
    // TODO(jcampan): bug #3870 hook-up Peterson's code here.
    NOTREACHED() << "to be implemented";
  }

  if (remove_mask & REMOVE_CACHE) {
    // Invoke ClearBrowsingDataView::ClearCache on the IO thread.
    base::Thread* thread = g_browser_process->io_thread();
    if (thread) {
      waiting_for_clear_cache_ = true;
      UserMetrics::RecordAction(L"ClearBrowsingData_Cache", profile_);
      thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
          this,
          &BrowsingDataRemover::ClearCacheOnIOThread,
          delete_begin_,
          delete_end_,
          MessageLoop::current()));
    }
  }

  NotifyAndDeleteIfDone();
}

void BrowsingDataRemover::AddObserver(Observer* observer) {
  observer_list_.AddObserver(observer);
}

void BrowsingDataRemover::RemoveObserver(Observer* observer) {
  observer_list_.RemoveObserver(observer);
}

void BrowsingDataRemover::OnHistoryDeletionDone() {
  waiting_for_clear_history_ = false;
  NotifyAndDeleteIfDone();
}

void BrowsingDataRemover::Observe(NotificationType type,
                                  const NotificationSource& source,
                                  const NotificationDetails& details) {
  // TODO(brettw) bug 1139736: This should also observe session
  // clearing (what about other things such as passwords, etc.?) and wait for
  // them to complete before continuing.
  DCHECK(type == TEMPLATE_URL_MODEL_LOADED);
  TemplateURLModel* model = Source<TemplateURLModel>(source).ptr();
  if (model->profile() == profile_->GetOriginalProfile()) {
    NotificationService::current()->RemoveObserver(
        this, TEMPLATE_URL_MODEL_LOADED,
        Source<TemplateURLModel>(model));

    model->RemoveAutoGeneratedBetween(delete_begin_, delete_end_);

    waiting_for_keywords_ = false;

    NotifyAndDeleteIfDone();
  }
}

void BrowsingDataRemover::NotifyAndDeleteIfDone() {
  // TODO(brettw) bug 1139736: see TODO in Observe() above.
  if (!all_done())
    return;

  FOR_EACH_OBSERVER(Observer, observer_list_, OnBrowsingDataRemoverDone());

  // History requests aren't happy if you delete yourself from the callback.
  // As such, we do a delete later.
  MessageLoop::current()->DeleteSoon(FROM_HERE, this);
}

void BrowsingDataRemover::ClearedCache() {
  waiting_for_clear_cache_ = false;

  NotifyAndDeleteIfDone();
}

void BrowsingDataRemover::ClearCacheOnIOThread(Time delete_begin,
                                               Time delete_end,
                                               MessageLoop* ui_loop) {
  // This function should be called on the IO thread.
  DCHECK(MessageLoop::current() ==
         ChromeThread::GetMessageLoop(ChromeThread::IO));

  // Get a pointer to the cache.
  net::HttpTransactionFactory* factory =
      profile_->GetRequestContext()->http_transaction_factory();
  disk_cache::Backend* cache = factory->GetCache()->disk_cache();

  // The cache can be empty, for example on startup, in which case |cache| will
  // be null and we don't need to do anything.
  if (cache) {
    if (delete_begin.is_null())
      cache->DoomAllEntries();
    else
      cache->DoomEntriesBetween(delete_begin, delete_end);
  }

  // Notify the UI thread that we are done.
  ui_loop->PostTask(FROM_HERE, NewRunnableMethod(
      this, &BrowsingDataRemover::ClearedCache));
}
<|endoftext|>


<commit_before>#ifndef VENTURA_RUN_PROCESS_HPP
#define VENTURA_RUN_PROCESS_HPP

#include <ventura/async_process.hpp>
#include <silicium/write.hpp>
#include <silicium/sink/multi_sink.hpp>
#include <silicium/sink/iterator_sink.hpp>
#include <silicium/sink/copy.hpp>
#include <silicium/source/range_source.hpp>
#include <silicium/source/transforming_source.hpp>
#include <boost/range/algorithm/transform.hpp>

namespace ventura
{
	template <class T>
	bool extract(T &destination, Si::optional<T> &&source)
	{
		if (source)
		{
			destination = std::forward<T>(*source);
			return true;
		}
		return false;
	}
}

#define VENTURA_HAS_RUN_PROCESS                                                                                \
	(SILICIUM_HAS_EXCEPTIONS && VENTURA_HAS_EXPERIMENTAL_READ_FROM_ANONYMOUS_PIPE && VENTURA_HAS_LAUNCH_PROCESS)

#if VENTURA_HAS_RUN_PROCESS
#include <future>

namespace ventura
{
	namespace detail
	{
		inline Si::error_or<Si::pipe> make_pipe() BOOST_NOEXCEPT
		{
#ifdef _WIN32
			SECURITY_ATTRIBUTES security = {};
			security.nLength = sizeof(security);
			security.bInheritHandle = FALSE;
			HANDLE read, write;
			if (!CreatePipe(&read, &write, &security, 0))
			{
				return Si::get_last_error();
			}
			Si::pipe result;
			result.read = Si::file_handle(read);
			result.write = Si::file_handle(write);
			return std::move(result);
#else
			return Si::make_pipe();
#endif
		}

		inline Si::file_handle enable_inheritance(Si::file_handle original)
		{
#ifdef _WIN32
			HANDLE enabled = INVALID_HANDLE_VALUE;
			if (!DuplicateHandle(GetCurrentProcess(), original.release(), GetCurrentProcess(), &enabled, 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
			{
				assert(enabled == INVALID_HANDLE_VALUE);
				Si::throw_last_error();
			}
			return Si::file_handle(enabled);
#else
			return original;
#endif
		}
	}

	inline Si::error_or<int> run_process(process_parameters const &parameters)
	{
		async_process_parameters async_parameters;
		async_parameters.executable = parameters.executable;
		async_parameters.arguments = parameters.arguments;
		async_parameters.current_path = parameters.current_path;
		auto input = detail::make_pipe().move_value();
		auto std_output = detail::make_pipe().move_value();
		auto std_error = detail::make_pipe().move_value();

		Si::file_handle inheritable_stdin_read = detail::enable_inheritance(std::move(input.read));
		Si::file_handle inheritable_stdout_write = detail::enable_inheritance(std::move(std_output.write));
		Si::file_handle inheritable_stderr_write = detail::enable_inheritance(std::move(std_error.write));
		async_process process =
		    launch_process(async_parameters, inheritable_stdin_read.handle, inheritable_stdout_write.handle, inheritable_stderr_write.handle,
		                   parameters.additional_environment, parameters.inheritance)
		        .move_value();

		inheritable_stdin_read.close();
		inheritable_stdout_write.close();
		inheritable_stderr_write.close();

		boost::asio::io_service io;

		boost::promise<void> stop_polling;
		boost::shared_future<void> stopped_polling = stop_polling.get_future().share();

		auto std_output_consumer = Si::make_multi_sink<char, Si::success>(
		    [&parameters]()
		    {
			    return Si::make_iterator_range(&parameters.out, &parameters.out + (parameters.out != nullptr));
			});
		auto stdout_finished = experimental::read_from_anonymous_pipe(io, std_output_consumer, std::move(std_output.read), stopped_polling);

		auto std_error_consumer = Si::make_multi_sink<char, Si::success>(
		    [&parameters]()
		    {
			    return Si::make_iterator_range(&parameters.err, &parameters.err + (parameters.err != nullptr));
			});
		auto stderr_finished = experimental::read_from_anonymous_pipe(io, std_error_consumer, std::move(std_error.read), stopped_polling);

		auto copy_input = std::async(std::launch::async, [&input, &parameters]()
		                             {
			                             if (!parameters.in)
			                             {
				                             return;
			                             }
			                             for (;;)
			                             {
				                             Si::optional<char> const c = Si::get(*parameters.in);
				                             if (!c)
				                             {
					                             break;
				                             }
				                             Si::error_or<size_t> written =
				                                 write(input.write.handle, Si::make_memory_range(&*c, 1));
				                             if (written.is_error())
				                             {
					                             // process must have exited
					                             break;
				                             }
				                             assert(written.get() == 1);
			                             }
#ifdef _WIN32
										 //do not know whether this is necessary
										 BOOL flushed = FlushFileBuffers(input.write.handle);
#endif
			                             input.write.close();
			                         });

#ifdef _WIN32
		io.run();
		copy_input.get();
		stdout_finished.get();
		stderr_finished.get();
		return process.wait_for_exit();
#else
		io.run();
		copy_input.get();
		return process.wait_for_exit();
#endif
	}

#ifdef _WIN32
	SILICIUM_USE_RESULT
	inline Si::error_or<int>
	run_process(absolute_path executable, std::vector<Si::os_string> arguments, absolute_path current_directory,
	            Si::Sink<char, Si::success>::interface &output,
	            std::vector<std::pair<Si::os_char const *, Si::os_char const *>> additional_environment,
	            environment_inheritance inheritance)
	{
		process_parameters parameters;
		parameters.executable = std::move(executable);
		parameters.arguments = std::move(arguments);
		parameters.current_path = std::move(current_directory);
		parameters.out = &output;
		parameters.err = &output;
		parameters.additional_environment = std::move(additional_environment);
		parameters.inheritance = inheritance;
		return run_process(parameters);
	}
#endif

	SILICIUM_USE_RESULT
	inline std::vector<Si::os_string> arguments_to_os_strings(std::vector<Si::noexcept_string> const &arguments)
	{
		std::vector<Si::os_string> new_arguments;
		{
			auto new_arguments_sink = Si::make_container_sink(new_arguments);
			auto arguments_encoder =
			    Si::make_transforming_source(Si::make_range_source(Si::make_contiguous_range(arguments)),
			                                 [](Si::noexcept_string const &in) -> Si::os_string
			                                 {
				                                 // TODO: mvoe the string if possible
				                                 return Si::to_os_string(in);
				                             });
			Si::copy(arguments_encoder, new_arguments_sink);
		}
		return new_arguments;
	}

	SILICIUM_USE_RESULT
	inline Si::error_or<int>
	run_process(absolute_path executable, std::vector<Si::noexcept_string> arguments, absolute_path current_directory,
	            Si::Sink<char, Si::success>::interface &output,
	            std::vector<std::pair<Si::os_char const *, Si::os_char const *>> additional_environment,
	            environment_inheritance inheritance)
	{
		process_parameters parameters;
		parameters.executable = std::move(executable);
		std::vector<Si::os_string> new_arguments = arguments_to_os_strings(arguments);
		parameters.arguments = std::move(new_arguments);
		parameters.current_path = std::move(current_directory);
		parameters.out = &output;
		parameters.err = &output;
		parameters.additional_environment = std::move(additional_environment);
		parameters.inheritance = inheritance;
		return run_process(parameters);
	}
}
#endif

#endif
<commit_msg>remove special Win32 #ifdef<commit_after>#ifndef VENTURA_RUN_PROCESS_HPP
#define VENTURA_RUN_PROCESS_HPP

#include <ventura/async_process.hpp>
#include <silicium/write.hpp>
#include <silicium/sink/multi_sink.hpp>
#include <silicium/sink/iterator_sink.hpp>
#include <silicium/sink/copy.hpp>
#include <silicium/source/range_source.hpp>
#include <silicium/source/transforming_source.hpp>
#include <boost/range/algorithm/transform.hpp>

namespace ventura
{
	template <class T>
	bool extract(T &destination, Si::optional<T> &&source)
	{
		if (source)
		{
			destination = std::forward<T>(*source);
			return true;
		}
		return false;
	}
}

#define VENTURA_HAS_RUN_PROCESS                                                                                \
	(SILICIUM_HAS_EXCEPTIONS && VENTURA_HAS_EXPERIMENTAL_READ_FROM_ANONYMOUS_PIPE && VENTURA_HAS_LAUNCH_PROCESS)

#if VENTURA_HAS_RUN_PROCESS
#include <future>

namespace ventura
{
	namespace detail
	{
		inline Si::error_or<Si::pipe> make_pipe() BOOST_NOEXCEPT
		{
#ifdef _WIN32
			SECURITY_ATTRIBUTES security = {};
			security.nLength = sizeof(security);
			security.bInheritHandle = FALSE;
			HANDLE read, write;
			if (!CreatePipe(&read, &write, &security, 0))
			{
				return Si::get_last_error();
			}
			Si::pipe result;
			result.read = Si::file_handle(read);
			result.write = Si::file_handle(write);
			return std::move(result);
#else
			return Si::make_pipe();
#endif
		}

		inline Si::file_handle enable_inheritance(Si::file_handle original)
		{
#ifdef _WIN32
			HANDLE enabled = INVALID_HANDLE_VALUE;
			if (!DuplicateHandle(GetCurrentProcess(), original.release(), GetCurrentProcess(), &enabled, 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
			{
				assert(enabled == INVALID_HANDLE_VALUE);
				Si::throw_last_error();
			}
			return Si::file_handle(enabled);
#else
			return original;
#endif
		}
	}

	inline Si::error_or<int> run_process(process_parameters const &parameters)
	{
		async_process_parameters async_parameters;
		async_parameters.executable = parameters.executable;
		async_parameters.arguments = parameters.arguments;
		async_parameters.current_path = parameters.current_path;
		auto input = detail::make_pipe().move_value();
		auto std_output = detail::make_pipe().move_value();
		auto std_error = detail::make_pipe().move_value();

		Si::file_handle inheritable_stdin_read = detail::enable_inheritance(std::move(input.read));
		Si::file_handle inheritable_stdout_write = detail::enable_inheritance(std::move(std_output.write));
		Si::file_handle inheritable_stderr_write = detail::enable_inheritance(std::move(std_error.write));
		async_process process =
		    launch_process(async_parameters, inheritable_stdin_read.handle, inheritable_stdout_write.handle, inheritable_stderr_write.handle,
		                   parameters.additional_environment, parameters.inheritance)
		        .move_value();

		inheritable_stdin_read.close();
		inheritable_stdout_write.close();
		inheritable_stderr_write.close();

		boost::asio::io_service io;

		boost::promise<void> stop_polling;
		boost::shared_future<void> stopped_polling = stop_polling.get_future().share();

		auto std_output_consumer = Si::make_multi_sink<char, Si::success>(
		    [&parameters]()
		    {
			    return Si::make_iterator_range(&parameters.out, &parameters.out + (parameters.out != nullptr));
			});
		auto stdout_finished = experimental::read_from_anonymous_pipe(io, std_output_consumer, std::move(std_output.read), stopped_polling);

		auto std_error_consumer = Si::make_multi_sink<char, Si::success>(
		    [&parameters]()
		    {
			    return Si::make_iterator_range(&parameters.err, &parameters.err + (parameters.err != nullptr));
			});
		auto stderr_finished = experimental::read_from_anonymous_pipe(io, std_error_consumer, std::move(std_error.read), stopped_polling);

		auto copy_input = std::async(std::launch::async, [&input, &parameters]()
		                             {
			                             if (!parameters.in)
			                             {
				                             return;
			                             }
			                             for (;;)
			                             {
				                             Si::optional<char> const c = Si::get(*parameters.in);
				                             if (!c)
				                             {
					                             break;
				                             }
				                             Si::error_or<size_t> written =
				                                 write(input.write.handle, Si::make_memory_range(&*c, 1));
				                             if (written.is_error())
				                             {
					                             // process must have exited
					                             break;
				                             }
				                             assert(written.get() == 1);
			                             }
#ifdef _WIN32
										 //do not know whether this is necessary
										 BOOL flushed = FlushFileBuffers(input.write.handle);
#endif
			                             input.write.close();
			                         });

		io.run();
		copy_input.get();
		stdout_finished.get();
		stderr_finished.get();
		return process.wait_for_exit();
	}

#ifdef _WIN32
	SILICIUM_USE_RESULT
	inline Si::error_or<int>
	run_process(absolute_path executable, std::vector<Si::os_string> arguments, absolute_path current_directory,
	            Si::Sink<char, Si::success>::interface &output,
	            std::vector<std::pair<Si::os_char const *, Si::os_char const *>> additional_environment,
	            environment_inheritance inheritance)
	{
		process_parameters parameters;
		parameters.executable = std::move(executable);
		parameters.arguments = std::move(arguments);
		parameters.current_path = std::move(current_directory);
		parameters.out = &output;
		parameters.err = &output;
		parameters.additional_environment = std::move(additional_environment);
		parameters.inheritance = inheritance;
		return run_process(parameters);
	}
#endif

	SILICIUM_USE_RESULT
	inline std::vector<Si::os_string> arguments_to_os_strings(std::vector<Si::noexcept_string> const &arguments)
	{
		std::vector<Si::os_string> new_arguments;
		{
			auto new_arguments_sink = Si::make_container_sink(new_arguments);
			auto arguments_encoder =
			    Si::make_transforming_source(Si::make_range_source(Si::make_contiguous_range(arguments)),
			                                 [](Si::noexcept_string const &in) -> Si::os_string
			                                 {
				                                 // TODO: mvoe the string if possible
				                                 return Si::to_os_string(in);
				                             });
			Si::copy(arguments_encoder, new_arguments_sink);
		}
		return new_arguments;
	}

	SILICIUM_USE_RESULT
	inline Si::error_or<int>
	run_process(absolute_path executable, std::vector<Si::noexcept_string> arguments, absolute_path current_directory,
	            Si::Sink<char, Si::success>::interface &output,
	            std::vector<std::pair<Si::os_char const *, Si::os_char const *>> additional_environment,
	            environment_inheritance inheritance)
	{
		process_parameters parameters;
		parameters.executable = std::move(executable);
		std::vector<Si::os_string> new_arguments = arguments_to_os_strings(arguments);
		parameters.arguments = std::move(new_arguments);
		parameters.current_path = std::move(current_directory);
		parameters.out = &output;
		parameters.err = &output;
		parameters.additional_environment = std::move(additional_environment);
		parameters.inheritance = inheritance;
		return run_process(parameters);
	}
}
#endif

#endif
<|endoftext|>


<commit_before>
#include "time.h"         // au::Format
#include "au/string.h"

#include "Cronometer.h"     // Own interface

NAMESPACE_BEGIN(au)

Cronometer::Cronometer()
{
    if ( gettimeofday(&t,NULL) != 0)
        LM_X(1, ("gettimeofday failed"));
}

void Cronometer::reset()
{
    if ( gettimeofday(&t,NULL) != 0)
        LM_X(1, ("gettimeofday failed"));
}

time_t Cronometer::diffTimeInSeconds()
{
    struct timeval t2;

    if ( gettimeofday(&t2,NULL) != 0)
        LM_X(1, ("gettimeofday failed"));

    return t2.tv_sec - t.tv_sec;
}

double Cronometer::diffTimeAndReset()
{
    struct timeval t2;

    if ( gettimeofday(&t2,NULL) != 0)
        LM_X(1, ("gettimeofday failed"));

    // Reset the value to this new time reference
    t = t2;

    return ( (double) t2.tv_sec ) + ((double)t2.tv_usec / 1000000.0) - ( (double) t.tv_sec ) - ((double)t.tv_usec / 1000000.0);

}

double Cronometer::diffTime()
{
    struct timeval t2;

    if ( gettimeofday(&t2,NULL) != 0)
        LM_X(1, ("gettimeofday failed"));

    double diff = ( (double) t2.tv_sec ) + ((double)t2.tv_usec / 1000000.0) - ( (double) t.tv_sec ) - ((double)t.tv_usec / 1000000.0);

    LM_M(("Cronometer diff %.02f (%d %d --> %d %d )"
          ,diff
          ,t.tv_sec
          ,t.tv_usec
          ,t2.tv_sec
          ,t2.tv_usec
          ));

    return diff;
}

std::string Cronometer::str()
{
    return time_string(diffTimeInSeconds());
}

#pragma mark CronometerSystem

CronometerSystem::CronometerSystem()
{
    running = false;
    seconds = 0;
}

void CronometerSystem::start()
{
    running = true;
    c.reset();
}
void CronometerSystem::stop()
{
    running = false;
    seconds += c.diffTimeInSeconds();
}

// Reset the count to 0
void CronometerSystem::reset()
{
    running = false;
    seconds = 0;
}

// Get a string with the time ellapsed since last reset or creation
std::string CronometerSystem::str()
{
    return time_string( getSeconds() );
}

int CronometerSystem::getSeconds()
{
    if( running )
        return (seconds + c.diffTimeInSeconds() );
    else
        return seconds;
}

NAMESPACE_END
<commit_msg>SAMSON-291 Less traces...<commit_after>
#include "time.h"         // au::Format
#include "au/string.h"

#include "Cronometer.h"     // Own interface

NAMESPACE_BEGIN(au)

Cronometer::Cronometer()
{
    if ( gettimeofday(&t,NULL) != 0)
        LM_X(1, ("gettimeofday failed"));
}

void Cronometer::reset()
{
    if ( gettimeofday(&t,NULL) != 0)
        LM_X(1, ("gettimeofday failed"));
}

time_t Cronometer::diffTimeInSeconds()
{
    struct timeval t2;

    if ( gettimeofday(&t2,NULL) != 0)
        LM_X(1, ("gettimeofday failed"));

    return t2.tv_sec - t.tv_sec;
}

double Cronometer::diffTimeAndReset()
{
    struct timeval t2;

    if ( gettimeofday(&t2,NULL) != 0)
        LM_X(1, ("gettimeofday failed"));

    // Reset the value to this new time reference
    t = t2;

    return ( (double) t2.tv_sec ) + ((double)t2.tv_usec / 1000000.0) - ( (double) t.tv_sec ) - ((double)t.tv_usec / 1000000.0);

}

double Cronometer::diffTime()
{
    struct timeval t2;

    if ( gettimeofday(&t2,NULL) != 0)
        LM_X(1, ("gettimeofday failed"));

    double diff = ( (double) t2.tv_sec ) + ((double)t2.tv_usec / 1000000.0) - ( (double) t.tv_sec ) - ((double)t.tv_usec / 1000000.0);

/*
    {
        LM_M(("Cronometer diff %.02f (%d %d --> %d %d )"
              ,diff
              ,t.tv_sec
              ,t.tv_usec
              ,t2.tv_sec
              ,t2.tv_usec
              ));
    }
*/
    return diff;
}

std::string Cronometer::str()
{
    return time_string(diffTimeInSeconds());
}

#pragma mark CronometerSystem

CronometerSystem::CronometerSystem()
{
    running = false;
    seconds = 0;
}

void CronometerSystem::start()
{
    running = true;
    c.reset();
}
void CronometerSystem::stop()
{
    running = false;
    seconds += c.diffTimeInSeconds();
}

// Reset the count to 0
void CronometerSystem::reset()
{
    running = false;
    seconds = 0;
}

// Get a string with the time ellapsed since last reset or creation
std::string CronometerSystem::str()
{
    return time_string( getSeconds() );
}

int CronometerSystem::getSeconds()
{
    if( running )
        return (seconds + c.diffTimeInSeconds() );
    else
        return seconds;
}

NAMESPACE_END
<|endoftext|>


<commit_before>/*
    Copyright (C) 1999 by Denis Dmitriev
    Antialiased polygon splitting

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

    This library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <math.h>

#define SYSDEF_ALLOCA
#include "sysdef.h"
#include "qint.h"
#include "csgeom/vector2.h"
#include "csgeom/polyaa.h"

static float __calc_area (int n, csVector2 *p)
{
  float area = 0;

  for (int i = 0; i < n; i++)
  {
    int j = (i != n - 1) ? i + 1 : 0;
    area += (p [i].y + p [j].y) * (p [i].x - p [j].x);
  }

  return fabs (area / 2.0);
}

#define EPS   0.0001

// No doubt static vars are !!!EVIL!!! :-) but for
// the sake of performance's we still use them ...
static csAAPFCBPixel PutPixel;
static csAAPFCBBox DrawBox;
static csRect Grid;
static void *Arg;

void __poly_fill (csVector2 *iVertices, int iVertexCount)
{
  // Calculate the complete are of visible rectangle
  int height = Grid.Height ();
  int width = Grid.Width ();
  int visarea = width * height;

  // Sanity check (prevents infinite looping)
  if (visarea <= 0)
    return;

  // Calculate the complete area of the polygon
  float a = __calc_area (iVertexCount, iVertices);

  // Check if polygon is hollow
  if (a < EPS)
    // this area is hollow
    return;

  // Check if current rectangle equals a single grid cell
  if (height == 1 && width == 1)
  {
    PutPixel (Grid.xmin, Grid.ymin, a, Arg);
    return;
  }

  // Check if polygon surface equals the visible rectangle surface
  if (fabs (a - visarea) < EPS)
  {
    // this area is completely covered
    if (DrawBox)
      DrawBox (Grid.xmin, Grid.ymin, width, height, Arg);
    else
      for (int i = 0 ; i < height; i++)
        for (int j = 0 ; j < width; j++)
          PutPixel (Grid.xmin + j, Grid.ymin + i, 1.0, Arg);
    return;
  }

  // Allocate space for both polygons that result after split
  int n2 [2] = { 0, 0 };
  csVector2 *p2 [2];

  p2 [0] = (csVector2 *)alloca (sizeof (csVector2) * (iVertexCount + 1));
  p2 [1] = (csVector2 *)alloca (sizeof (csVector2) * (iVertexCount + 1));

  if (width > height)
  {
    // Split the polygon vertically by the line "x = sub_x"
    // (p2 [0] -- left poly, p2 [1] -- right poly)

    int sub_x = Grid.xmin + width / 2;
    int where_are_we = iVertices [0].x > sub_x;
    p2 [where_are_we] [n2 [where_are_we]++] = iVertices [0];
    for (int v = 1, prev = 0; v <= iVertexCount; v++)
    {
      // Check whenever current vertex is on left or right side of divider
      int cur = (v == iVertexCount) ? 0 : v;
      int now_we_are = iVertices [cur].x > sub_x;

      if (now_we_are == where_are_we)
      {
        // Do not add the first point since it will be added at the end
        if (cur) p2 [where_are_we] [n2 [where_are_we]++] = iVertices [cur];
      }
      else
      {
        // The most complex case: find the Y at intersection point
        float y = iVertices [prev].y + (iVertices [cur].y - iVertices [prev].y) *
          (sub_x - iVertices [prev].x) / (iVertices [cur].x - iVertices [prev].x);

        // Add the intersection point to both polygons
  	p2 [0] [n2 [0]++] = p2 [1] [n2 [1]++] = csVector2 (sub_x, y);

  	if (cur) p2 [now_we_are] [n2 [now_we_are]++] = iVertices [cur];
      }

      where_are_we = now_we_are;
      prev = cur;
    }

    int tmp = Grid.xmax;
    Grid.xmax = sub_x;
    __poly_fill (p2 [0], n2 [0]);
    Grid.xmax = tmp;

    tmp = Grid.xmin;
    Grid.xmin = sub_x;
    __poly_fill (p2 [1], n2 [1]);
    Grid.xmin = tmp;
  }
  else
  {
    // Split the polygon horizontally by the line "y = sub_y"
    // (p[0] -- top poly, p[1] -- bottom poly)

    int sub_y = Grid.ymin + height / 2;
    int where_are_we = iVertices [0].y > sub_y;
    p2 [where_are_we] [n2 [where_are_we]++] = iVertices [0];
    for (int v = 1, prev = 0; v <= iVertexCount; v++)
    {
      // Check whenever current vertex is on top or down side of divider
      int cur = (v == iVertexCount) ? 0 : v;
      int now_we_are = iVertices [cur].y > sub_y;

      if (now_we_are == where_are_we)
      {
        // Do not add the first point since it will be added at the end
  	if (cur) p2 [where_are_we] [n2 [where_are_we]++] = iVertices [cur];
      }
      else
      {
        // The most complex case: find the X at intersection point
        float x = iVertices [prev].x + (iVertices [cur].x - iVertices [prev].x) *
          (sub_y - iVertices [prev].y) / (iVertices [cur].y - iVertices [prev].y);

        // Add the intersection point to both polygons
  	p2 [0] [n2 [0]++] = p2 [1] [n2 [1]++] = csVector2 (x, sub_y);

  	if (cur) p2 [now_we_are] [n2 [now_we_are]++] = iVertices [cur];
      }

      where_are_we = now_we_are;
      prev = cur;
    }

    int tmp = Grid.ymax;
    Grid.ymax = sub_y;
    __poly_fill (p2 [0], n2 [0]);
    Grid.ymax = tmp;

    tmp = Grid.ymin;
    Grid.ymin = sub_y;
    __poly_fill (p2 [1], n2 [1]);
    Grid.ymin = tmp;
  }
}

void csAntialiasedPolyFill (csVector2 *iVertices, int iVertexCount,
  void *iArg, csAAPFCBPixel iPutPixel, csAAPFCBBox iDrawBox)
{
  PutPixel = iPutPixel;
  DrawBox = iDrawBox;
  Arg = iArg;

  // Find the bounding box first
  Grid.Set (999999, 999999, -999999, -999999);
  for (int i = 0; i < iVertexCount; i++)
  {
    int x = QInt (iVertices [i].x);
    int y = QInt (iVertices [i].y);
    if (Grid.xmin > x) Grid.xmin = x;
    if (Grid.ymin > y) Grid.ymin = y;
    x = QRound (ceil (iVertices [i].x));
    y = QRound (ceil (iVertices [i].y));
    if (Grid.xmax < x) Grid.xmax = x;
    if (Grid.ymax < y) Grid.ymax = y;
  }

  __poly_fill (iVertices, iVertexCount);
}
<commit_msg>more robust.<commit_after>/*
    Copyright (C) 1999 by Denis Dmitriev
    Antialiased polygon splitting

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

    This library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <math.h>

#define SYSDEF_ALLOCA
#include "sysdef.h"
#include "qint.h"
#include "csgeom/vector2.h"
#include "csgeom/polyaa.h"

static float __calc_area (int n, csVector2 *p)
{
  float area = 0;

  for (int i = 0; i < n; i++)
  {
    int j = (i != n - 1) ? i + 1 : 0;
    area += (p [i].y + p [j].y) * (p [i].x - p [j].x);
  }

  return fabs (area / 2.0);
}

#define EPS   0.0001

// No doubt static vars are !!!EVIL!!! :-) but for
// the sake of performance's we still use them ...
static csAAPFCBPixel PutPixel;
static csAAPFCBBox DrawBox;
static csRect Grid;
static void *Arg;

void __poly_fill (csVector2 *iVertices, int iVertexCount)
{
  // Calculate the complete are of visible rectangle
  int height = Grid.Height ();
  int width = Grid.Width ();
  int visarea = width * height;

  // Sanity check (prevents infinite looping)
  if (visarea <= 0)
    return;

  // Calculate the complete area of the polygon
  float a = __calc_area (iVertexCount, iVertices);

  // Check if polygon is hollow
  if (a < EPS)
    // this area is hollow
    return;

  // Check if current rectangle equals a single grid cell
  if (height == 1 && width == 1)
  {
    PutPixel (Grid.xmin, Grid.ymin, a, Arg);
    return;
  }

  // Check if polygon surface equals the visible rectangle surface
  if (fabs (a - visarea) < EPS)
  {
    // this area is completely covered
    if (DrawBox)
      DrawBox (Grid.xmin, Grid.ymin, width, height, Arg);
    else
      for (int i = 0 ; i < height; i++)
        for (int j = 0 ; j < width; j++)
          PutPixel (Grid.xmin + j, Grid.ymin + i, 1.0, Arg);
    return;
  }

  // Allocate space for both polygons that result after split
  int n2 [2] = { 0, 0 };
  csVector2 *p2 [2];

  p2 [0] = (csVector2 *)alloca (sizeof (csVector2) * (iVertexCount + 1));
  p2 [1] = (csVector2 *)alloca (sizeof (csVector2) * (iVertexCount + 1));

  if (width > height)
  {
    // Split the polygon vertically by the line "x = sub_x"
    // (p2 [0] -- left poly, p2 [1] -- right poly)

    int sub_x = Grid.xmin + width / 2;
    int where_are_we = iVertices [0].x > sub_x;
    p2 [where_are_we] [n2 [where_are_we]++] = iVertices [0];
    for (int v = 1, prev = 0; v <= iVertexCount; v++)
    {
      // Check whenever current vertex is on left or right side of divider
      int cur = (v == iVertexCount) ? 0 : v;
      int now_we_are = iVertices [cur].x > sub_x;

      if (now_we_are == where_are_we)
      {
        // Do not add the first point since it will be added at the end
        if (cur) p2 [where_are_we] [n2 [where_are_we]++] = iVertices [cur];
      }
      else
      {
        // The most complex case: find the Y at intersection point
        float y = iVertices [prev].y + (iVertices [cur].y - iVertices [prev].y) *
          (sub_x - iVertices [prev].x) / (iVertices [cur].x - iVertices [prev].x);

        // Add the intersection point to both polygons
  	p2 [0] [n2 [0]++] = p2 [1] [n2 [1]++] = csVector2 (sub_x, y);

  	if (cur) p2 [now_we_are] [n2 [now_we_are]++] = iVertices [cur];
      }

      where_are_we = now_we_are;
      prev = cur;
    }

    int tmp = Grid.xmax;
    Grid.xmax = sub_x;
    __poly_fill (p2 [0], n2 [0]);
    Grid.xmax = tmp;

    tmp = Grid.xmin;
    Grid.xmin = sub_x;
    __poly_fill (p2 [1], n2 [1]);
    Grid.xmin = tmp;
  }
  else
  {
    // Split the polygon horizontally by the line "y = sub_y"
    // (p[0] -- top poly, p[1] -- bottom poly)

    int sub_y = Grid.ymin + height / 2;
    int where_are_we = iVertices [0].y > sub_y;
    p2 [where_are_we] [n2 [where_are_we]++] = iVertices [0];
    for (int v = 1, prev = 0; v <= iVertexCount; v++)
    {
      // Check whenever current vertex is on top or down side of divider
      int cur = (v == iVertexCount) ? 0 : v;
      int now_we_are = iVertices [cur].y > sub_y;

      if (now_we_are == where_are_we)
      {
        // Do not add the first point since it will be added at the end
  	if (cur) p2 [where_are_we] [n2 [where_are_we]++] = iVertices [cur];
      }
      else
      {
        // The most complex case: find the X at intersection point
        float x = iVertices [prev].x + (iVertices [cur].x - iVertices [prev].x) *
          (sub_y - iVertices [prev].y) / (iVertices [cur].y - iVertices [prev].y);

        // Add the intersection point to both polygons
  	p2 [0] [n2 [0]++] = p2 [1] [n2 [1]++] = csVector2 (x, sub_y);

  	if (cur) p2 [now_we_are] [n2 [now_we_are]++] = iVertices [cur];
      }

      where_are_we = now_we_are;
      prev = cur;
    }

    int tmp = Grid.ymax;
    Grid.ymax = sub_y;
    __poly_fill (p2 [0], n2 [0]);
    Grid.ymax = tmp;

    tmp = Grid.ymin;
    Grid.ymin = sub_y;
    __poly_fill (p2 [1], n2 [1]);
    Grid.ymin = tmp;
  }
}

void csAntialiasedPolyFill (csVector2 *iVertices, int iVertexCount,
  void *iArg, csAAPFCBPixel iPutPixel, csAAPFCBBox iDrawBox)
{
  // if nothing to do, exit
  if(iVertexCount <= 0) return;

  PutPixel = iPutPixel;
  DrawBox = iDrawBox;
  Arg = iArg;

  // Find the bounding box first
  Grid.Set (999999, 999999, -999999, -999999);
  for (int i = 0; i < iVertexCount; i++)
  {
    int x = QInt (iVertices [i].x);
    int y = QInt (iVertices [i].y);
    if (Grid.xmin > x) Grid.xmin = x;
    if (Grid.ymin > y) Grid.ymin = y;
    x = QRound (ceil (iVertices [i].x));
    y = QRound (ceil (iVertices [i].y));
    if (Grid.xmax < x) Grid.xmax = x;
    if (Grid.ymax < y) Grid.ymax = y;
  }

  __poly_fill (iVertices, iVertexCount);
}
<|endoftext|>


<commit_before>// Copyright 2017 The Bazel Authors. All rights reserved.
//
// 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.
//
// wrapped_clang.cc: Pass args to 'xcrun clang' and zip dsym files.
//
// wrapped_clang passes its args to clang, but also supports a separate set of
// invocations to generate dSYM files.  If "DSYM_HINT" flags are passed in, they
// are used to construct that separate set of invocations (instead of being
// passed to clang).
// The following "DSYM_HINT" flags control dsym generation.  If any one if these
// are passed in, then they all must be passed in.
// "DSYM_HINT_LINKED_BINARY": Workspace-relative path to binary output of the
//    link action generating the dsym file.
// "DSYM_HINT_DSYM_PATH": Workspace-relative path to dSYM dwarf file.

#include <libgen.h>
#include <spawn.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

#include <cerrno>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <utility>
#include <vector>

extern char **environ;

namespace {

constexpr char kAddASTPathPrefix[] = "-Wl,-add_ast_path,";

// Returns the base name of the given filepath. For example, given
// /foo/bar/baz.txt, returns 'baz.txt'.
const char *Basename(const char *filepath) {
  const char *base = strrchr(filepath, '/');
  return base ? (base + 1) : filepath;
}

// Unescape and unquote an argument read from a line of a response file.
static std::string Unescape(const std::string &arg) {
  std::string result;
  auto length = arg.size();
  for (size_t i = 0; i < length; ++i) {
    auto ch = arg[i];

    // If it's a backslash, consume it and append the character that follows.
    if (ch == '\\' && i + 1 < length) {
      ++i;
      result.push_back(arg[i]);
      continue;
    }

    // If it's a quote, process everything up to the matching quote, unescaping
    // backslashed characters as needed.
    if (ch == '"' || ch == '\'') {
      auto quote = ch;
      ++i;
      while (i != length && arg[i] != quote) {
        if (arg[i] == '\\' && i + 1 < length) {
          ++i;
        }
        result.push_back(arg[i]);
        ++i;
      }
      if (i == length) {
        break;
      }
      continue;
    }

    // It's a regular character.
    result.push_back(ch);
  }

  return result;
}

// Converts an array of string arguments to char *arguments.
// The first arg is reduced to its basename as per execve conventions.
// Note that the lifetime of the char* arguments in the returned array
// are controlled by the lifetime of the strings in args.
std::vector<const char *> ConvertToCArgs(const std::vector<std::string> &args) {
  std::vector<const char *> c_args;
  c_args.push_back(Basename(args[0].c_str()));
  for (int i = 1; i < args.size(); i++) {
    c_args.push_back(args[i].c_str());
  }
  c_args.push_back(nullptr);
  return c_args;
}

// Spawns a subprocess for given arguments args. The first argument is used
// for the executable path.
void RunSubProcess(const std::vector<std::string> &args) {
  std::vector<const char *> exec_argv = ConvertToCArgs(args);
  pid_t pid;
  int status = posix_spawn(&pid, args[0].c_str(), nullptr, nullptr,
                           const_cast<char **>(exec_argv.data()), environ);
  if (status == 0) {
    int wait_status;
    do {
      wait_status = waitpid(pid, &status, 0);
    } while ((wait_status == -1) && (errno == EINTR));
    if (wait_status < 0) {
      std::cerr << "Error waiting on child process '" << args[0] << "'. "
                << strerror(errno) << "\n";
      abort();
    }
    if (WEXITSTATUS(status) != 0) {
      std::cerr << "Error in child process '" << args[0] << "'. "
                << WEXITSTATUS(status) << "\n";
      abort();
    }
  } else {
    std::cerr << "Error forking process '" << args[0] << "'. "
              << strerror(status) << "\n";
    abort();
  }
}

// Finds and replaces all instances of oldsub with newsub, in-place on str.
void FindAndReplace(const std::string &oldsub, const std::string &newsub,
                    std::string *str) {
  int start = 0;
  while ((start = str->find(oldsub, start)) != std::string::npos) {
    str->replace(start, oldsub.length(), newsub);
    start += newsub.length();
  }
}

// If arg is of the classic flag form "foo=bar", and flagname is 'foo', sets
// str to point to a new std::string 'bar' and returns true.
// Otherwise, returns false.
bool SetArgIfFlagPresent(const std::string &arg, const std::string &flagname,
                         std::string *str) {
  std::string prefix_string = flagname + "=";
  if (arg.compare(0, prefix_string.length(), prefix_string) == 0) {
    *str = arg.substr(prefix_string.length());
    return true;
  }
  return false;
}

// Returns the DEVELOPER_DIR environment variable in the current process
// environment. Aborts if this variable is unset.
std::string GetMandatoryEnvVar(const std::string &var_name) {
  char *env_value = getenv(var_name.c_str());
  if (env_value == nullptr) {
    std::cerr << "Error: " << var_name << " not set.\n";
    abort();
  }
  return env_value;
}

// Returns true if `str` starts with the specified `prefix`.
bool StartsWith(const std::string &str, const std::string &prefix) {
  return str.compare(0, prefix.size(), prefix) == 0;
}

// If *`str` begins `prefix`, strip it out and return true.
// Otherwise leave *`str` unchanged and return false.
bool StripPrefixStringIfPresent(std::string *str, const std::string &prefix) {
  if (StartsWith(*str, prefix)) {
    *str = str->substr(prefix.size());
    return true;
  }
  return false;
}

// An RAII temporary file.
class TempFile {
 public:
  // Create a new temporary file using the given path template string (the same
  // form used by `mkstemp`). The file will automatically be deleted when the
  // object goes out of scope.
  static std::unique_ptr<TempFile> Create(const std::string &path_template) {
    const char *tmpDir = getenv("TMPDIR");
    if (!tmpDir) {
      tmpDir = "/tmp";
    }
    size_t size = strlen(tmpDir) + path_template.size() + 2;
    std::unique_ptr<char[]> path(new char[size]);
    snprintf(path.get(), size, "%s/%s", tmpDir, path_template.c_str());

    if (mkstemp(path.get()) == -1) {
      std::cerr << "Failed to create temporary file '" << path.get()
                << "': " << strerror(errno) << "\n";
      return nullptr;
    }
    return std::unique_ptr<TempFile>(new TempFile(path.get()));
  }

  // Explicitly make TempFile non-copyable and movable.
  TempFile(const TempFile &) = delete;
  TempFile &operator=(const TempFile &) = delete;
  TempFile(TempFile &&) = default;
  TempFile &operator=(TempFile &&) = default;

  ~TempFile() { remove(path_.c_str()); }

  // Gets the path to the temporary file.
  std::string GetPath() const { return path_; }

 private:
  explicit TempFile(const std::string &path) : path_(path) {}

  std::string path_;
};

static std::unique_ptr<TempFile> WriteResponseFile(
    const std::vector<std::string> &args) {
  auto response_file = TempFile::Create("wrapped_clang_params.XXXXXX");
  std::ofstream response_file_stream(response_file->GetPath());

  for (const auto &arg : args) {
    // When Clang/Swift write out a response file to communicate from driver to
    // frontend, they just quote every argument to be safe; we duplicate that
    // instead of trying to be "smarter" and only quoting when necessary.
    response_file_stream << '"';
    for (auto ch : arg) {
      if (ch == '"' || ch == '\\') {
        response_file_stream << '\\';
      }
      response_file_stream << ch;
    }
    response_file_stream << "\"\n";
  }

  response_file_stream.close();
  return response_file;
}

void ProcessArgument(const std::string arg, const std::string developer_dir,
                     const std::string sdk_root, const std::string cwd,
                     bool relative_ast_path, std::string &linked_binary,
                     std::string &dsym_path,
                     std::function<void(const std::string &)> consumer);

bool ProcessResponseFile(const std::string arg, const std::string developer_dir,
                         const std::string sdk_root, const std::string cwd,
                         bool relative_ast_path, std::string &linked_binary,
                         std::string &dsym_path,
                         std::function<void(const std::string &)> consumer) {
  auto path = arg.substr(1);
  std::ifstream original_file(path);
  // Ignore non-file args such as '@loader_path/...'
  if (!original_file.good()) {
    return false;
  }

  std::string arg_from_file;
  while (std::getline(original_file, arg_from_file)) {
    // Arguments in response files might be quoted/escaped, so we need to
    // unescape them ourselves.
    ProcessArgument(Unescape(arg_from_file), developer_dir, sdk_root, cwd,
                    relative_ast_path, linked_binary, dsym_path, consumer);
  }

  return true;
}

std::string GetCurrentDirectory() {
  // Passing null,0 causes getcwd to allocate the buffer of the correct size.
  char *buffer = getcwd(nullptr, 0);
  std::string cwd(buffer);
  free(buffer);
  return cwd;
}

void ProcessArgument(const std::string arg, const std::string developer_dir,
                     const std::string sdk_root, const std::string cwd,
                     bool relative_ast_path, std::string &linked_binary,
                     std::string &dsym_path,
                     std::function<void(const std::string &)> consumer) {
  auto new_arg = arg;
  if (arg[0] == '@') {
    if (ProcessResponseFile(arg, developer_dir, sdk_root, cwd,
                            relative_ast_path, linked_binary, dsym_path,
                            consumer)) {
      return;
    }
  }

  if (SetArgIfFlagPresent(arg, "DSYM_HINT_LINKED_BINARY", &linked_binary)) {
    return;
  }
  if (SetArgIfFlagPresent(arg, "DSYM_HINT_DSYM_PATH", &dsym_path)) {
    return;
  }

  std::string dest_dir, bitcode_symbol_map;
  if (SetArgIfFlagPresent(arg, "DEBUG_PREFIX_MAP_PWD", &dest_dir)) {
    new_arg = "-fdebug-prefix-map=" + cwd + "=" + dest_dir;
  }
  if (arg.compare("OSO_PREFIX_MAP_PWD") == 0) {
    new_arg = "-Wl,-oso_prefix," + cwd + "/";
  }

  FindAndReplace("__BAZEL_XCODE_DEVELOPER_DIR__", developer_dir, &new_arg);
  FindAndReplace("__BAZEL_XCODE_SDKROOT__", sdk_root, &new_arg);

  // Make the `add_ast_path` options used to embed Swift module references
  // absolute to enable Swift debugging without dSYMs: see
  // https://forums.swift.org/t/improving-swift-lldb-support-for-path-remappings/22694
  if (!relative_ast_path &&
      StripPrefixStringIfPresent(&new_arg, kAddASTPathPrefix)) {
    // Only modify relative paths.
    if (!StartsWith(arg, "/")) {
      new_arg = std::string(kAddASTPathPrefix) + cwd + "/" + new_arg;
    } else {
      new_arg = std::string(kAddASTPathPrefix) + new_arg;
    }
  }

  consumer(new_arg);
}

}  // namespace

int main(int argc, char *argv[]) {
  std::string tool_name;

  std::string binary_name = Basename(argv[0]);
  if (binary_name == "wrapped_clang_pp") {
    tool_name = "clang++";
  } else if (binary_name == "wrapped_clang") {
    tool_name = "clang";
  } else {
    std::cerr << "Binary must either be named 'wrapped_clang' or "
                 "'wrapped_clang_pp', not "
              << binary_name << "\n";
    abort();
  }

  std::string developer_dir = GetMandatoryEnvVar("DEVELOPER_DIR");
  std::string sdk_root = GetMandatoryEnvVar("SDKROOT");
  std::string linked_binary, dsym_path;

  const std::string cwd = GetCurrentDirectory();
  std::vector<std::string> invocation_args = {"/usr/bin/xcrun", tool_name};
  std::vector<std::string> processed_args = {};

  bool relative_ast_path = getenv("RELATIVE_AST_PATH") != nullptr;
  auto consumer = [&](const std::string &arg) {
    processed_args.push_back(arg);
  };
  for (int i = 1; i < argc; i++) {
    std::string arg(argv[i]);

    ProcessArgument(arg, developer_dir, sdk_root, cwd, relative_ast_path,
                    linked_binary, dsym_path, consumer);
  }

  // Special mode that only prints the command. Used for testing.
  if (getenv("__WRAPPED_CLANG_LOG_ONLY")) {
    for (const std::string &arg : invocation_args) std::cout << arg << ' ';
    for (const std::string &arg : processed_args) std::cout << arg << ' ';
    std::cout << "\n";
    return 0;
  }

  auto response_file = WriteResponseFile(processed_args);
  invocation_args.push_back("@" + response_file->GetPath());

  // Check to see if we should postprocess with dsymutil.
  bool postprocess = false;
  if ((!linked_binary.empty()) || (!dsym_path.empty())) {
    if ((linked_binary.empty()) || (dsym_path.empty())) {
      const char *missing_dsym_flag;
      if (linked_binary.empty()) {
        missing_dsym_flag = "DSYM_HINT_LINKED_BINARY";
      } else {
        missing_dsym_flag = "DSYM_HINT_DSYM_PATH";
      }
      std::cerr << "Error in clang wrapper: If any dsym "
                   "hint is defined, then "
                << missing_dsym_flag << " must be defined\n";
      abort();
    } else {
      postprocess = true;
    }
  }

  RunSubProcess(invocation_args);
  if (!postprocess) {
    return 0;
  }

  std::vector<std::string> dsymutil_args = {
      "/usr/bin/xcrun", "dsymutil", linked_binary, "-o", dsym_path, "--flat"};
  RunSubProcess(dsymutil_args);
  return 0;
}
<commit_msg>Silence swiftmodule timestamp warnings<commit_after>// Copyright 2017 The Bazel Authors. All rights reserved.
//
// 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.
//
// wrapped_clang.cc: Pass args to 'xcrun clang' and zip dsym files.
//
// wrapped_clang passes its args to clang, but also supports a separate set of
// invocations to generate dSYM files.  If "DSYM_HINT" flags are passed in, they
// are used to construct that separate set of invocations (instead of being
// passed to clang).
// The following "DSYM_HINT" flags control dsym generation.  If any one if these
// are passed in, then they all must be passed in.
// "DSYM_HINT_LINKED_BINARY": Workspace-relative path to binary output of the
//    link action generating the dsym file.
// "DSYM_HINT_DSYM_PATH": Workspace-relative path to dSYM dwarf file.

#include <libgen.h>
#include <spawn.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

#include <cerrno>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <utility>
#include <vector>

extern char **environ;

namespace {

constexpr char kAddASTPathPrefix[] = "-Wl,-add_ast_path,";

// Returns the base name of the given filepath. For example, given
// /foo/bar/baz.txt, returns 'baz.txt'.
const char *Basename(const char *filepath) {
  const char *base = strrchr(filepath, '/');
  return base ? (base + 1) : filepath;
}

// Unescape and unquote an argument read from a line of a response file.
static std::string Unescape(const std::string &arg) {
  std::string result;
  auto length = arg.size();
  for (size_t i = 0; i < length; ++i) {
    auto ch = arg[i];

    // If it's a backslash, consume it and append the character that follows.
    if (ch == '\\' && i + 1 < length) {
      ++i;
      result.push_back(arg[i]);
      continue;
    }

    // If it's a quote, process everything up to the matching quote, unescaping
    // backslashed characters as needed.
    if (ch == '"' || ch == '\'') {
      auto quote = ch;
      ++i;
      while (i != length && arg[i] != quote) {
        if (arg[i] == '\\' && i + 1 < length) {
          ++i;
        }
        result.push_back(arg[i]);
        ++i;
      }
      if (i == length) {
        break;
      }
      continue;
    }

    // It's a regular character.
    result.push_back(ch);
  }

  return result;
}

// Converts an array of string arguments to char *arguments.
// The first arg is reduced to its basename as per execve conventions.
// Note that the lifetime of the char* arguments in the returned array
// are controlled by the lifetime of the strings in args.
std::vector<const char *> ConvertToCArgs(const std::vector<std::string> &args) {
  std::vector<const char *> c_args;
  c_args.push_back(Basename(args[0].c_str()));
  for (int i = 1; i < args.size(); i++) {
    c_args.push_back(args[i].c_str());
  }
  c_args.push_back(nullptr);
  return c_args;
}

// Spawns a subprocess for given arguments args. The first argument is used
// for the executable path.
void RunSubProcess(const std::vector<std::string> &args) {
  std::vector<const char *> exec_argv = ConvertToCArgs(args);
  pid_t pid;
  int status = posix_spawn(&pid, args[0].c_str(), nullptr, nullptr,
                           const_cast<char **>(exec_argv.data()), environ);
  if (status == 0) {
    int wait_status;
    do {
      wait_status = waitpid(pid, &status, 0);
    } while ((wait_status == -1) && (errno == EINTR));
    if (wait_status < 0) {
      std::cerr << "Error waiting on child process '" << args[0] << "'. "
                << strerror(errno) << "\n";
      abort();
    }
    if (WEXITSTATUS(status) != 0) {
      std::cerr << "Error in child process '" << args[0] << "'. "
                << WEXITSTATUS(status) << "\n";
      abort();
    }
  } else {
    std::cerr << "Error forking process '" << args[0] << "'. "
              << strerror(status) << "\n";
    abort();
  }
}

// Finds and replaces all instances of oldsub with newsub, in-place on str.
void FindAndReplace(const std::string &oldsub, const std::string &newsub,
                    std::string *str) {
  int start = 0;
  while ((start = str->find(oldsub, start)) != std::string::npos) {
    str->replace(start, oldsub.length(), newsub);
    start += newsub.length();
  }
}

// If arg is of the classic flag form "foo=bar", and flagname is 'foo', sets
// str to point to a new std::string 'bar' and returns true.
// Otherwise, returns false.
bool SetArgIfFlagPresent(const std::string &arg, const std::string &flagname,
                         std::string *str) {
  std::string prefix_string = flagname + "=";
  if (arg.compare(0, prefix_string.length(), prefix_string) == 0) {
    *str = arg.substr(prefix_string.length());
    return true;
  }
  return false;
}

// Returns the DEVELOPER_DIR environment variable in the current process
// environment. Aborts if this variable is unset.
std::string GetMandatoryEnvVar(const std::string &var_name) {
  char *env_value = getenv(var_name.c_str());
  if (env_value == nullptr) {
    std::cerr << "Error: " << var_name << " not set.\n";
    abort();
  }
  return env_value;
}

// Returns true if `str` starts with the specified `prefix`.
bool StartsWith(const std::string &str, const std::string &prefix) {
  return str.compare(0, prefix.size(), prefix) == 0;
}

// If *`str` begins `prefix`, strip it out and return true.
// Otherwise leave *`str` unchanged and return false.
bool StripPrefixStringIfPresent(std::string *str, const std::string &prefix) {
  if (StartsWith(*str, prefix)) {
    *str = str->substr(prefix.size());
    return true;
  }
  return false;
}

// An RAII temporary file.
class TempFile {
 public:
  // Create a new temporary file using the given path template string (the same
  // form used by `mkstemp`). The file will automatically be deleted when the
  // object goes out of scope.
  static std::unique_ptr<TempFile> Create(const std::string &path_template) {
    const char *tmpDir = getenv("TMPDIR");
    if (!tmpDir) {
      tmpDir = "/tmp";
    }
    size_t size = strlen(tmpDir) + path_template.size() + 2;
    std::unique_ptr<char[]> path(new char[size]);
    snprintf(path.get(), size, "%s/%s", tmpDir, path_template.c_str());

    if (mkstemp(path.get()) == -1) {
      std::cerr << "Failed to create temporary file '" << path.get()
                << "': " << strerror(errno) << "\n";
      return nullptr;
    }
    return std::unique_ptr<TempFile>(new TempFile(path.get()));
  }

  // Explicitly make TempFile non-copyable and movable.
  TempFile(const TempFile &) = delete;
  TempFile &operator=(const TempFile &) = delete;
  TempFile(TempFile &&) = default;
  TempFile &operator=(TempFile &&) = default;

  ~TempFile() { remove(path_.c_str()); }

  // Gets the path to the temporary file.
  std::string GetPath() const { return path_; }

 private:
  explicit TempFile(const std::string &path) : path_(path) {}

  std::string path_;
};

static std::unique_ptr<TempFile> WriteResponseFile(
    const std::vector<std::string> &args) {
  auto response_file = TempFile::Create("wrapped_clang_params.XXXXXX");
  std::ofstream response_file_stream(response_file->GetPath());

  for (const auto &arg : args) {
    // When Clang/Swift write out a response file to communicate from driver to
    // frontend, they just quote every argument to be safe; we duplicate that
    // instead of trying to be "smarter" and only quoting when necessary.
    response_file_stream << '"';
    for (auto ch : arg) {
      if (ch == '"' || ch == '\\') {
        response_file_stream << '\\';
      }
      response_file_stream << ch;
    }
    response_file_stream << "\"\n";
  }

  response_file_stream.close();
  return response_file;
}

void ProcessArgument(const std::string arg, const std::string developer_dir,
                     const std::string sdk_root, const std::string cwd,
                     bool relative_ast_path, std::string &linked_binary,
                     std::string &dsym_path,
                     std::function<void(const std::string &)> consumer);

bool ProcessResponseFile(const std::string arg, const std::string developer_dir,
                         const std::string sdk_root, const std::string cwd,
                         bool relative_ast_path, std::string &linked_binary,
                         std::string &dsym_path,
                         std::function<void(const std::string &)> consumer) {
  auto path = arg.substr(1);
  std::ifstream original_file(path);
  // Ignore non-file args such as '@loader_path/...'
  if (!original_file.good()) {
    return false;
  }

  std::string arg_from_file;
  while (std::getline(original_file, arg_from_file)) {
    // Arguments in response files might be quoted/escaped, so we need to
    // unescape them ourselves.
    ProcessArgument(Unescape(arg_from_file), developer_dir, sdk_root, cwd,
                    relative_ast_path, linked_binary, dsym_path, consumer);
  }

  return true;
}

std::string GetCurrentDirectory() {
  // Passing null,0 causes getcwd to allocate the buffer of the correct size.
  char *buffer = getcwd(nullptr, 0);
  std::string cwd(buffer);
  free(buffer);
  return cwd;
}

void ProcessArgument(const std::string arg, const std::string developer_dir,
                     const std::string sdk_root, const std::string cwd,
                     bool relative_ast_path, std::string &linked_binary,
                     std::string &dsym_path,
                     std::function<void(const std::string &)> consumer) {
  auto new_arg = arg;
  if (arg[0] == '@') {
    if (ProcessResponseFile(arg, developer_dir, sdk_root, cwd,
                            relative_ast_path, linked_binary, dsym_path,
                            consumer)) {
      return;
    }
  }

  if (SetArgIfFlagPresent(arg, "DSYM_HINT_LINKED_BINARY", &linked_binary)) {
    return;
  }
  if (SetArgIfFlagPresent(arg, "DSYM_HINT_DSYM_PATH", &dsym_path)) {
    return;
  }

  std::string dest_dir, bitcode_symbol_map;
  if (SetArgIfFlagPresent(arg, "DEBUG_PREFIX_MAP_PWD", &dest_dir)) {
    new_arg = "-fdebug-prefix-map=" + cwd + "=" + dest_dir;
  }
  if (arg.compare("OSO_PREFIX_MAP_PWD") == 0) {
    new_arg = "-Wl,-oso_prefix," + cwd + "/";
  }

  FindAndReplace("__BAZEL_XCODE_DEVELOPER_DIR__", developer_dir, &new_arg);
  FindAndReplace("__BAZEL_XCODE_SDKROOT__", sdk_root, &new_arg);

  // Make the `add_ast_path` options used to embed Swift module references
  // absolute to enable Swift debugging without dSYMs: see
  // https://forums.swift.org/t/improving-swift-lldb-support-for-path-remappings/22694
  if (!relative_ast_path &&
      StripPrefixStringIfPresent(&new_arg, kAddASTPathPrefix)) {
    // Only modify relative paths.
    if (!StartsWith(arg, "/")) {
      new_arg = std::string(kAddASTPathPrefix) + cwd + "/" + new_arg;
    } else {
      new_arg = std::string(kAddASTPathPrefix) + new_arg;
    }
  }

  consumer(new_arg);
}

}  // namespace

int main(int argc, char *argv[]) {
  std::string tool_name;

  std::string binary_name = Basename(argv[0]);
  if (binary_name == "wrapped_clang_pp") {
    tool_name = "clang++";
  } else if (binary_name == "wrapped_clang") {
    tool_name = "clang";
  } else {
    std::cerr << "Binary must either be named 'wrapped_clang' or "
                 "'wrapped_clang_pp', not "
              << binary_name << "\n";
    abort();
  }

  std::string developer_dir = GetMandatoryEnvVar("DEVELOPER_DIR");
  std::string sdk_root = GetMandatoryEnvVar("SDKROOT");
  std::string linked_binary, dsym_path;

  const std::string cwd = GetCurrentDirectory();
  std::vector<std::string> invocation_args = {"/usr/bin/xcrun", tool_name};
  std::vector<std::string> processed_args = {};

  bool relative_ast_path = getenv("RELATIVE_AST_PATH") != nullptr;
  auto consumer = [&](const std::string &arg) {
    processed_args.push_back(arg);
  };
  for (int i = 1; i < argc; i++) {
    std::string arg(argv[i]);

    ProcessArgument(arg, developer_dir, sdk_root, cwd, relative_ast_path,
                    linked_binary, dsym_path, consumer);
  }

  // Special mode that only prints the command. Used for testing.
  if (getenv("__WRAPPED_CLANG_LOG_ONLY")) {
    for (const std::string &arg : invocation_args) std::cout << arg << ' ';
    for (const std::string &arg : processed_args) std::cout << arg << ' ';
    std::cout << "\n";
    return 0;
  }

  auto response_file = WriteResponseFile(processed_args);
  invocation_args.push_back("@" + response_file->GetPath());

  // Check to see if we should postprocess with dsymutil.
  bool postprocess = false;
  if ((!linked_binary.empty()) || (!dsym_path.empty())) {
    if ((linked_binary.empty()) || (dsym_path.empty())) {
      const char *missing_dsym_flag;
      if (linked_binary.empty()) {
        missing_dsym_flag = "DSYM_HINT_LINKED_BINARY";
      } else {
        missing_dsym_flag = "DSYM_HINT_DSYM_PATH";
      }
      std::cerr << "Error in clang wrapper: If any dsym "
                   "hint is defined, then "
                << missing_dsym_flag << " must be defined\n";
      abort();
    } else {
      postprocess = true;
    }
  }

  RunSubProcess(invocation_args);
  if (!postprocess) {
    return 0;
  }

  std::vector<std::string> dsymutil_args = {"/usr/bin/xcrun",
                                            "dsymutil",
                                            linked_binary,
                                            "-o",
                                            dsym_path,
                                            "--flat",
                                            "--no-swiftmodule-timestamp"};
  RunSubProcess(dsymutil_args);
  return 0;
}
<|endoftext|>


<commit_before>
#include <memory>
#include <string>
#include <vector>

#include "astronomy/time_scales.hpp"
#include "base/file.hpp"
#include "base/not_null.hpp"
#include "base/pull_serializer.hpp"
#include "base/push_deserializer.hpp"
#include "glog/logging.h"
#include "gmock/gmock.h"
#include "google/protobuf/io/coded_stream.h"
#include "gtest/gtest.h"
#include "ksp_plugin/interface.hpp"
#include "ksp_plugin/plugin.hpp"
#include "serialization/ksp_plugin.pb.h"
#include "testing_utilities/serialization.hpp"

namespace principia {
namespace interface {

using astronomy::operator""_TT;
using astronomy::TTSecond;
using astronomy::date_time::DateTime;
using astronomy::date_time::operator""_DateTime;
using base::not_null;
using base::OFStream;
using base::PullSerializer;
using base::PushDeserializer;
using ksp_plugin::Plugin;
using quantities::Speed;
using testing_utilities::ReadLinesFromBase64File;
using testing_utilities::ReadLinesFromHexadecimalFile;
using ::testing::AllOf;
using ::testing::ElementsAre;
using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::IsFalse;
using ::testing::IsTrue;
using ::testing::Not;
using ::testing::NotNull;
using ::testing::internal::CaptureStderr;
using ::testing::internal::GetCapturedStderr;

const char preferred_compressor[] = "gipfeli";
const char preferred_encoder[] = "base64";

class PluginCompatibilityTest : public testing::Test {
 protected:
  PluginCompatibilityTest() {
    google::SetStderrLogging(google::WARNING);
  }

  // Reads a plugin from a file containing only the "serialized_plugin = "
  // lines, with "serialized_plugin = " dropped.
  static not_null<std::unique_ptr<Plugin const>> ReadPluginFromFile(
      std::filesystem::path const& filename,
      std::string_view const compressor,
      std::string_view const encoder) {
    Plugin const* plugin = nullptr;

    PushDeserializer* deserializer = nullptr;
    auto const lines =
        encoder == "hexadecimal" ? ReadLinesFromHexadecimalFile(filename)
        : encoder == "base64"    ? ReadLinesFromBase64File(filename)
                                  : std::vector<std::string>{};
    CHECK(!lines.empty());

    LOG(ERROR) << "Deserialization starting";
    for (std::string const& line : lines) {
      principia__DeserializePlugin(line.c_str(),
                                   &deserializer,
                                   &plugin,
                                   compressor.data(),
                                   encoder.data());
    }
    principia__DeserializePlugin("",
                                 &deserializer,
                                 &plugin,
                                 compressor.data(),
                                 encoder.data());
    LOG(ERROR) << "Deserialization complete";

    return std::unique_ptr<Plugin const>(plugin);
  }

  // Writes a plugin to a file.
  static void WritePluginToFile(
      std::filesystem::path const& filename,
      std::string_view const compressor,
      std::string_view const encoder,
      not_null<std::unique_ptr<Plugin const>> plugin) {
    OFStream file(filename);
    PullSerializer* serializer = nullptr;
    char const* b64 = nullptr;

    LOG(ERROR) << "Serialization starting";
    for (;;) {
      b64 = principia__SerializePlugin(
          plugin.get(), &serializer, preferred_compressor, preferred_encoder);
      if (b64 == nullptr) {
        break;
      }
      file << b64 << "\n";
      principia__DeleteString(&b64);
    }
    LOG(ERROR) << "Serialization complete";

    Plugin const* released_plugin = plugin.release();
    principia__DeletePlugin(&released_plugin);
  }

  static void WriteAndReadBack(not_null<std::unique_ptr<Plugin const>> plugin1) {
    // Write the plugin to new file with the preferred format.
    WritePluginToFile(TEMP_DIR / "serialized_plugin.proto.b64",
                      preferred_compressor,
                      preferred_encoder,
                      std::move(plugin1));

    // Read the plugin from the new file to make sure that it's fine.
    auto plugin2 = ReadPluginFromFile(TEMP_DIR / "serialized_plugin.proto.b64",
                                      preferred_compressor,
                                      preferred_encoder);
  }

  static void CheckSaveCompatibility(std::filesystem::path const& filename,
                              std::string_view const compressor,
                              std::string_view const encoder) {
    // Read a plugin from the given file.
    auto plugin = ReadPluginFromFile(filename, compressor, encoder);

    WriteAndReadBack(std::move(plugin));
  }
};

TEST_F(PluginCompatibilityTest, PreCartan) {
  // This space for rent.
}

TEST_F(PluginCompatibilityTest, PreCohen) {
  CaptureStderr();
  CheckSaveCompatibility(
      SOLUTION_DIR / "ksp_plugin_test" / "saves" / "3039.proto.hex",
      /*compressor=*/"",
      /*decoder=*/"hexadecimal");
  EXPECT_THAT(
      GetCapturedStderr(),
      AllOf(
          HasSubstr(
              "pre-Cohen ContinuousTrajectory"),  // Regression test for #3039.
          HasSubstr("pre-Cauchy"),                // The save is even older.
          Not(HasSubstr("pre-Cartan"))            // But not *that* old.
          ));
}

TEST_F(PluginCompatibilityTest, Reach) {
  CaptureStderr();
  not_null<std::unique_ptr<Plugin const>> plugin = ReadPluginFromFile(
      SOLUTION_DIR / "ksp_plugin_test" / "saves" / "3072.proto.b64",
      /*compressor=*/"gipfeli",
      /*decoder=*/"base64");
  EXPECT_THAT(GetCapturedStderr(),
              AllOf(HasSubstr("pre-Galileo"), Not(HasSubstr("pre-Frobenius"))));
  auto const test = plugin->GetVessel("f2d77873-4776-4809-9dfb-de9e7a0620a6");
  EXPECT_THAT(test->name(), Eq("TEST"));
  EXPECT_THAT(TTSecond(test->psychohistory().front().time),
              Eq("1970-08-14T08:03:18"_DateTime));
  EXPECT_THAT(TTSecond(test->psychohistory().back().time),
              Eq("1970-08-14T08:47:05"_DateTime));
  EXPECT_THAT(test->has_flight_plan(), IsFalse());

  auto const infnity =
      plugin->GetVessel("29142a79-7acd-47a9-a34d-f9f2a8e1b4ed");
  EXPECT_THAT(infnity->name(), Eq("IFNITY-5.2"));
  EXPECT_THAT(TTSecond(infnity->psychohistory().front().time),
              Eq("1970-08-14T08:03:46"_DateTime));
  EXPECT_THAT(TTSecond(infnity->psychohistory().back().time),
              Eq("1970-08-14T08:47:05"_DateTime));
  EXPECT_THAT(infnity->has_flight_plan(), IsTrue());
  EXPECT_THAT(infnity->flight_plan().number_of_manœuvres(), Eq(16));
  std::vector<DateTime> manœuvre_tt_seconds;
  std::vector<Speed> manœuvre_Δvs;
  for (int i = 0; i < infnity->flight_plan().number_of_manœuvres(); ++i) {
    manœuvre_tt_seconds.push_back(
        TTSecond(infnity->flight_plan().GetManœuvre(i).initial_time()));
    manœuvre_Δvs.push_back(infnity->flight_plan().GetManœuvre(i).Δv().Norm());
  }
  EXPECT_THAT(manœuvre_tt_seconds,
              ElementsAre("1970-08-14T09:34:49"_DateTime,
                          "1970-08-15T13:59:24"_DateTime,
                          "1970-12-22T07:48:21"_DateTime,
                          "1971-01-08T17:36:55"_DateTime,
                          "1971-07-02T17:16:00"_DateTime,
                          "1971-09-06T03:27:33"_DateTime,
                          "1972-02-13T22:47:26"_DateTime,
                          "1972-03-25T16:30:19"_DateTime,
                          "1972-12-24T04:09:32"_DateTime,
                          "1973-06-04T01:59:07"_DateTime,
                          "1973-07-09T06:07:17"_DateTime,
                          "1973-09-10T03:59:44"_DateTime,
                          "1974-11-20T17:34:27"_DateTime,
                          "1975-10-07T01:29:45"_DateTime,
                          "1975-12-29T21:27:13"_DateTime,
                          "1977-07-28T22:47:53"_DateTime));
  EXPECT_THAT(manœuvre_Δvs,
              ElementsAre(+3.80488671073918022e+03 * (Metre / Second),
                          +3.04867185471741759e-04 * (Metre / Second),
                          +1.58521291818444873e-03 * (Metre / Second),
                          +1.40000000034068623e-03 * (Metre / Second),
                          +1.00000000431022681e-04 * (Metre / Second),
                          +1.78421858738381537e-03 * (Metre / Second),
                          +7.72606625794511597e-04 * (Metre / Second),
                          +5.32846131747503372e-03 * (Metre / Second),
                          +3.45000000046532824e-03 * (Metre / Second),
                          +9.10695453328359134e-03 * (Metre / Second),
                          +4.49510921430966881e-01 * (Metre / Second),
                          +1.00000000431022681e-04 * (Metre / Second),
                          +5.10549409572428781e-01 * (Metre / Second),
                          +2.86686518692948443e-02 * (Metre / Second),
                          +1.00404183285598275e-03 * (Metre / Second),
                          +1.39666705839172456e-01 * (Metre / Second)));

  // Make sure that we can upgrade, save, and reload.
  WriteAndReadBack(std::move(plugin));
}

// Use for debugging saves given by users.
TEST_F(PluginCompatibilityTest, DISABLED_SECULAR_Debug) {
  CheckSaveCompatibility(
      R"(P:\Public Mockingbird\Principia\Saves\2685\five-minute-scene-change-neptune.txt)",
      /*compressor=*/"gipfeli",
      /*decoder=*/"base64");
}

}  // namespace interface
}  // namespace principia
<commit_msg>a problem for another day<commit_after>
#include <memory>
#include <string>
#include <vector>

#include "astronomy/time_scales.hpp"
#include "base/file.hpp"
#include "base/not_null.hpp"
#include "base/pull_serializer.hpp"
#include "base/push_deserializer.hpp"
#include "glog/logging.h"
#include "gmock/gmock.h"
#include "google/protobuf/io/coded_stream.h"
#include "gtest/gtest.h"
#include "ksp_plugin/interface.hpp"
#include "ksp_plugin/plugin.hpp"
#include "serialization/ksp_plugin.pb.h"
#include "testing_utilities/serialization.hpp"

namespace principia {
namespace interface {

using astronomy::operator""_TT;
using astronomy::TTSecond;
using astronomy::date_time::DateTime;
using astronomy::date_time::operator""_DateTime;
using base::not_null;
using base::OFStream;
using base::PullSerializer;
using base::PushDeserializer;
using ksp_plugin::Plugin;
using quantities::Speed;
using testing_utilities::ReadLinesFromBase64File;
using testing_utilities::ReadLinesFromHexadecimalFile;
using ::testing::AllOf;
using ::testing::ElementsAre;
using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::IsFalse;
using ::testing::IsTrue;
using ::testing::Not;
using ::testing::NotNull;
using ::testing::Pair;
using ::testing::internal::CaptureStderr;
using ::testing::internal::GetCapturedStderr;

const char preferred_compressor[] = "gipfeli";
const char preferred_encoder[] = "base64";

class PluginCompatibilityTest : public testing::Test {
 protected:
  PluginCompatibilityTest() {
    google::SetStderrLogging(google::WARNING);
  }

  // Reads a plugin from a file containing only the "serialized_plugin = "
  // lines, with "serialized_plugin = " dropped.
  static not_null<std::unique_ptr<Plugin const>> ReadPluginFromFile(
      std::filesystem::path const& filename,
      std::string_view const compressor,
      std::string_view const encoder) {
    Plugin const* plugin = nullptr;

    PushDeserializer* deserializer = nullptr;
    auto const lines =
        encoder == "hexadecimal" ? ReadLinesFromHexadecimalFile(filename)
        : encoder == "base64"    ? ReadLinesFromBase64File(filename)
                                  : std::vector<std::string>{};
    CHECK(!lines.empty());

    LOG(ERROR) << "Deserialization starting";
    for (std::string const& line : lines) {
      principia__DeserializePlugin(line.c_str(),
                                   &deserializer,
                                   &plugin,
                                   compressor.data(),
                                   encoder.data());
    }
    principia__DeserializePlugin("",
                                 &deserializer,
                                 &plugin,
                                 compressor.data(),
                                 encoder.data());
    LOG(ERROR) << "Deserialization complete";

    return std::unique_ptr<Plugin const>(plugin);
  }

  // Writes a plugin to a file.
  static void WritePluginToFile(
      std::filesystem::path const& filename,
      std::string_view const compressor,
      std::string_view const encoder,
      not_null<std::unique_ptr<Plugin const>> plugin) {
    OFStream file(filename);
    PullSerializer* serializer = nullptr;
    char const* b64 = nullptr;

    LOG(ERROR) << "Serialization starting";
    for (;;) {
      b64 = principia__SerializePlugin(
          plugin.get(), &serializer, preferred_compressor, preferred_encoder);
      if (b64 == nullptr) {
        break;
      }
      file << b64 << "\n";
      principia__DeleteString(&b64);
    }
    LOG(ERROR) << "Serialization complete";

    Plugin const* released_plugin = plugin.release();
    principia__DeletePlugin(&released_plugin);
  }

  static void WriteAndReadBack(not_null<std::unique_ptr<Plugin const>> plugin1) {
    // Write the plugin to new file with the preferred format.
    WritePluginToFile(TEMP_DIR / "serialized_plugin.proto.b64",
                      preferred_compressor,
                      preferred_encoder,
                      std::move(plugin1));

    // Read the plugin from the new file to make sure that it's fine.
    auto plugin2 = ReadPluginFromFile(TEMP_DIR / "serialized_plugin.proto.b64",
                                      preferred_compressor,
                                      preferred_encoder);
  }

  static void CheckSaveCompatibility(std::filesystem::path const& filename,
                              std::string_view const compressor,
                              std::string_view const encoder) {
    // Read a plugin from the given file.
    auto plugin = ReadPluginFromFile(filename, compressor, encoder);

    WriteAndReadBack(std::move(plugin));
  }
};

TEST_F(PluginCompatibilityTest, PreCartan) {
  // This space for rent.
}

TEST_F(PluginCompatibilityTest, PreCohen) {
  CaptureStderr();
  CheckSaveCompatibility(
      SOLUTION_DIR / "ksp_plugin_test" / "saves" / "3039.proto.hex",
      /*compressor=*/"",
      /*decoder=*/"hexadecimal");
  EXPECT_THAT(
      GetCapturedStderr(),
      AllOf(
          HasSubstr(
              "pre-Cohen ContinuousTrajectory"),  // Regression test for #3039.
          HasSubstr("pre-Cauchy"),                // The save is even older.
          Not(HasSubstr("pre-Cartan"))            // But not *that* old.
          ));
}

TEST_F(PluginCompatibilityTest, Reach) {
  CaptureStderr();
  not_null<std::unique_ptr<Plugin const>> plugin = ReadPluginFromFile(
      SOLUTION_DIR / "ksp_plugin_test" / "saves" / "3072.proto.b64",
      /*compressor=*/"gipfeli",
      /*decoder=*/"base64");
  EXPECT_THAT(GetCapturedStderr(),
              AllOf(HasSubstr("pre-Galileo"), Not(HasSubstr("pre-Frobenius"))));
  auto const test = plugin->GetVessel("f2d77873-4776-4809-9dfb-de9e7a0620a6");
  EXPECT_THAT(test->name(), Eq("TEST"));
  EXPECT_THAT(TTSecond(test->psychohistory().front().time),
              Eq("1970-08-14T08:03:18"_DateTime));
  EXPECT_THAT(TTSecond(test->psychohistory().back().time),
              Eq("1970-08-14T08:47:05"_DateTime));
  EXPECT_THAT(test->has_flight_plan(), IsFalse());

  auto const infnity =
      plugin->GetVessel("29142a79-7acd-47a9-a34d-f9f2a8e1b4ed");
  EXPECT_THAT(infnity->name(), Eq("IFNITY-5.2"));
  EXPECT_THAT(TTSecond(infnity->psychohistory().front().time),
              Eq("1970-08-14T08:03:46"_DateTime));
  EXPECT_THAT(TTSecond(infnity->psychohistory().back().time),
              Eq("1970-08-14T08:47:05"_DateTime));
  EXPECT_THAT(infnity->has_flight_plan(), IsTrue());
  EXPECT_THAT(infnity->flight_plan().number_of_manœuvres(), Eq(16));
  std::vector<std::pair<DateTime, Speed>> manœuvre_ignition_tt_seconds_and_Δvs;
  for (int i = 0; i < infnity->flight_plan().number_of_manœuvres(); ++i) {
    manœuvre_ignition_tt_seconds_and_Δvs.emplace_back(
        TTSecond(infnity->flight_plan().GetManœuvre(i).initial_time()),
        infnity->flight_plan().GetManœuvre(i).Δv().Norm());
  }
  // The flight plan only covers the inner solar system (this is probably
  // because of ).  It also differs from https://youtu.be/7BDxZV7UD9I?t=439.
  // TODO(egg): Compute the flybys and figure out what exactly is going on in
  // this flight plan.
  EXPECT_THAT(manœuvre_ignition_tt_seconds_and_Δvs,
              ElementsAre(Pair("1970-08-14T09:34:49"_DateTime,
                               +3.80488671073918022e+03 * (Metre / Second)),
                          Pair("1970-08-15T13:59:24"_DateTime,
                               1.58521291818444873e-03 * (Metre / Second)),
                          Pair("1970-12-22T07:48:21"_DateTime,
                               3.04867185471741759e-04 * (Metre / Second)),
                          Pair("1971-01-08T17:36:55"_DateTime,
                               1.40000000034068623e-03 * (Metre / Second)),
                          Pair("1971-07-02T17:16:00"_DateTime,
                               1.00000000431022681e-04 * (Metre / Second)),
                          Pair("1971-09-06T03:27:33"_DateTime,
                               1.78421858738381537e-03 * (Metre / Second)),
                          Pair("1972-02-13T22:47:26"_DateTime,
                               7.72606625794511597e-04 * (Metre / Second)),
                          Pair("1972-03-25T16:30:19"_DateTime,
                               5.32846131747503372e-03 * (Metre / Second)),
                          Pair("1972-12-24T04:09:32"_DateTime,
                               3.45000000046532824e-03 * (Metre / Second)),
                          Pair("1973-06-04T01:59:07"_DateTime,
                               9.10695453328359134e-03 * (Metre / Second)),
                          Pair("1973-07-09T06:07:17"_DateTime,
                               4.49510921430966881e-01 * (Metre / Second)),
                          Pair("1973-09-10T03:59:44"_DateTime,
                               1.00000000431022681e-04 * (Metre / Second)),
                          Pair("1974-11-20T17:34:27"_DateTime,
                               5.10549409572428781e-01 * (Metre / Second)),
                          Pair("1975-10-07T01:29:45"_DateTime,
                               2.86686518692948443e-02 * (Metre / Second)),
                          Pair("1975-12-29T21:27:13"_DateTime,
                               1.00404183285598275e-03 * (Metre / Second)),
                          Pair("1977-07-28T22:47:53"_DateTime,
                               1.39666705839172456e-01 * (Metre / Second))));

  // Make sure that we can upgrade, save, and reload.
  WriteAndReadBack(std::move(plugin));
}

// Use for debugging saves given by users.
TEST_F(PluginCompatibilityTest, DISABLED_SECULAR_Debug) {
  CheckSaveCompatibility(
      R"(P:\Public Mockingbird\Principia\Saves\2685\five-minute-scene-change-neptune.txt)",
      /*compressor=*/"gipfeli",
      /*decoder=*/"base64");
}

}  // namespace interface
}  // namespace principia
<|endoftext|>