<commit_before>/*
 * Copyright 2007-2021 CM4all GmbH
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "CommandLine.hxx"
#include "Config.hxx"
#include "net/Parser.hxx"
#include "io/Logger.hxx"
#include "util/StringView.hxx"
#include "util/IterableSplitString.hxx"
#include "version.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <getopt.h>
#include <string.h>

static void
PrintUsage()
{
	puts("usage: cm4all-beng-proxy [options]\n\n"
	     "valid options:\n"
#ifdef __GLIBC__
	     " --help\n"
#endif
	     " -h             help (this text)\n"
#ifdef __GLIBC__
	     " --version\n"
#endif
	     " -V             show cm4all-beng-proxy version\n"
#ifdef __GLIBC__
	     " --verbose\n"
#endif
	     " -v             be more verbose\n"
#ifdef __GLIBC__
	     " --quiet\n"
#endif
	     " -q             be quiet\n"
#ifdef __GLIBC__
	     " --config-file file\n"
#endif
	     " -f file        load this configuration file\n"
#ifdef __GLIBC__
	     " --logger-user name\n"
#endif
	     " -U name        execute the error logger program with this user id\n"
#ifdef __GLIBC__
	     " --document-root DIR\n"
#endif
	     " -r DIR         set the document root\n"
#ifdef __GLIBC__
	     " --translation-socket PATH\n"
#endif
	     " -t PATH        set the path to the translation server socket\n"
#ifdef __GLIBC__
	     " --cluster-size N\n"
#endif
	     " -C N           set the size of the beng-lb cluster\n"
#ifdef __GLIBC__
	     " --cluster-node N\n"
#endif
	     " -N N           set the index of this node in the beng-lb cluster\n"
#ifdef __GLIBC__
	     " --ua-classes PATH\n"
#endif
	     " -a PATH        load the User-Agent classification rules from this file\n"
#ifdef __GLIBC__
	     " --set NAME=VALUE  tweak an internal variable, see manual for details\n"
#endif
	     " -s NAME=VALUE  \n"
	     "\n"
	     );
}

static void arg_error(const char *argv0, const char *fmt, ...)
	__attribute__ ((noreturn))
	__attribute__((format(printf,2,3)));
static void arg_error(const char *argv0, const char *fmt, ...) {
	if (fmt != nullptr) {
		va_list ap;

		fputs(argv0, stderr);
		fputs(": ", stderr);

		va_start(ap, fmt);
		vfprintf(stderr, fmt, ap);
		va_end(ap);

		putc('\n', stderr);
	}

	fprintf(stderr, "Try '%s --help' for more information.\n",
		argv0);
	exit(1);
}

template<typename F>
static void
SplitForEach(const char *p, char separator, F &&f)
{
	for (auto value : IterableSplitString(p, separator))
		if (!value.empty())
			f(std::string(value.data, value.size).c_str());
}

static void
HandleSet(BpConfig &config,
	  const char *argv0, const char *p)
{
	const char *eq;

	eq = strchr(p, '=');
	if (eq == nullptr)
		arg_error(argv0, "No '=' found in --set argument");

	if (eq == p)
		arg_error(argv0, "No name found in --set argument");

	const StringView name(p, eq - p);
	const char *const value = eq + 1;

	try {
		config.HandleSet(name, value);
	} catch (const std::runtime_error &e) {
		arg_error(argv0, "Error while parsing \"--set %.*s\": %s",
			  (int)name.size, name.data, e.what());
	}
}

/** read configuration options from the command line */
void
ParseCommandLine(BpCmdLine &cmdline, BpConfig &config, int argc, char **argv)
{
	int ret;
	char *endptr;
#ifdef __GLIBC__
	static constexpr struct option long_options[] = {
		{"help", 0, nullptr, 'h'},
		{"version", 0, nullptr, 'V'},
		{"verbose", 0, nullptr, 'v'},
		{"quiet", 0, nullptr, 'q'},
		{"config-file", 1, nullptr, 'f'},
		{"user", 1, nullptr, 'u'},
		{"logger-user", 1, nullptr, 'U'},
		{"translation-socket", 1, nullptr, 't'},
		{"cluster-size", 1, nullptr, 'C'},
		{"cluster-node", 1, nullptr, 'N'},
		{"ua-classes", 1, nullptr, 'a'},
		{"set", 1, nullptr, 's'},
		{"debug-listener-tag", 1, nullptr, 'L'},
		{nullptr, 0, nullptr, 0}
	};
#endif
	unsigned verbose = 1;

	while (1) {
#ifdef __GLIBC__
		int option_index = 0;

		ret = getopt_long(argc, argv,
				  "hVvqf:U:t:B:C:N:s:",
				  long_options, &option_index);
#else
		ret = getopt(argc, argv,
			     "hVvqf:U:t:B:C:N:s:");
#endif
		if (ret == -1)
			break;

		switch (ret) {
		case 'h':
			PrintUsage();
			exit(0);

		case 'V':
			printf("cm4all-beng-proxy v%s\n", VERSION);
			exit(0);

		case 'v':
			++verbose;
			break;

		case 'q':
			verbose = 0;
			break;

		case 'f':
			cmdline.config_file = optarg;
			break;

		case 'U':
			cmdline.logger_user.Lookup(optarg);
			break;

		case 't':
			config.translation_sockets.emplace_front(ParseSocketAddress(optarg, 0, false));
			break;

		case 'C':
			config.cluster_size = strtoul(optarg, &endptr, 10);
			if (endptr == optarg || *endptr != 0 ||
			    config.cluster_size > 1024)
				arg_error(argv[0], "Invalid cluster size number");

			if (config.cluster_node >= config.cluster_size)
				config.cluster_node = 0;
			break;

		case 'N':
			config.cluster_node = strtoul(optarg, &endptr, 10);
			if (endptr == optarg || *endptr != 0)
				arg_error(argv[0], "Invalid cluster size number");

			if ((config.cluster_node != 0 || config.cluster_size != 0) &&
			    config.cluster_node >= config.cluster_size)
				arg_error(argv[0], "Cluster node too large");
			break;

		case 'a':
			cmdline.ua_classification_file = optarg;
			break;

		case 's':
			HandleSet(config, argv[0], optarg);
			break;

		case 'L':
			cmdline.debug_listener_tag = optarg;
			break;

		case '?':
			arg_error(argv[0], nullptr);

		default:
			exit(1);
		}
	}

	SetLogLevel(verbose);

	/* check non-option arguments */

	if (optind < argc)
		arg_error(argv[0], "unrecognized argument: %s", argv[optind]);
}
<commit_msg>bp/CommandLine: remove obsolete --user option<commit_after>/*
 * Copyright 2007-2021 CM4all GmbH
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "CommandLine.hxx"
#include "Config.hxx"
#include "net/Parser.hxx"
#include "io/Logger.hxx"
#include "util/StringView.hxx"
#include "util/IterableSplitString.hxx"
#include "version.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <getopt.h>
#include <string.h>

static void
PrintUsage()
{
	puts("usage: cm4all-beng-proxy [options]\n\n"
	     "valid options:\n"
#ifdef __GLIBC__
	     " --help\n"
#endif
	     " -h             help (this text)\n"
#ifdef __GLIBC__
	     " --version\n"
#endif
	     " -V             show cm4all-beng-proxy version\n"
#ifdef __GLIBC__
	     " --verbose\n"
#endif
	     " -v             be more verbose\n"
#ifdef __GLIBC__
	     " --quiet\n"
#endif
	     " -q             be quiet\n"
#ifdef __GLIBC__
	     " --config-file file\n"
#endif
	     " -f file        load this configuration file\n"
#ifdef __GLIBC__
	     " --logger-user name\n"
#endif
	     " -U name        execute the error logger program with this user id\n"
#ifdef __GLIBC__
	     " --document-root DIR\n"
#endif
	     " -r DIR         set the document root\n"
#ifdef __GLIBC__
	     " --translation-socket PATH\n"
#endif
	     " -t PATH        set the path to the translation server socket\n"
#ifdef __GLIBC__
	     " --cluster-size N\n"
#endif
	     " -C N           set the size of the beng-lb cluster\n"
#ifdef __GLIBC__
	     " --cluster-node N\n"
#endif
	     " -N N           set the index of this node in the beng-lb cluster\n"
#ifdef __GLIBC__
	     " --ua-classes PATH\n"
#endif
	     " -a PATH        load the User-Agent classification rules from this file\n"
#ifdef __GLIBC__
	     " --set NAME=VALUE  tweak an internal variable, see manual for details\n"
#endif
	     " -s NAME=VALUE  \n"
	     "\n"
	     );
}

static void arg_error(const char *argv0, const char *fmt, ...)
	__attribute__ ((noreturn))
	__attribute__((format(printf,2,3)));
static void arg_error(const char *argv0, const char *fmt, ...) {
	if (fmt != nullptr) {
		va_list ap;

		fputs(argv0, stderr);
		fputs(": ", stderr);

		va_start(ap, fmt);
		vfprintf(stderr, fmt, ap);
		va_end(ap);

		putc('\n', stderr);
	}

	fprintf(stderr, "Try '%s --help' for more information.\n",
		argv0);
	exit(1);
}

template<typename F>
static void
SplitForEach(const char *p, char separator, F &&f)
{
	for (auto value : IterableSplitString(p, separator))
		if (!value.empty())
			f(std::string(value.data, value.size).c_str());
}

static void
HandleSet(BpConfig &config,
	  const char *argv0, const char *p)
{
	const char *eq;

	eq = strchr(p, '=');
	if (eq == nullptr)
		arg_error(argv0, "No '=' found in --set argument");

	if (eq == p)
		arg_error(argv0, "No name found in --set argument");

	const StringView name(p, eq - p);
	const char *const value = eq + 1;

	try {
		config.HandleSet(name, value);
	} catch (const std::runtime_error &e) {
		arg_error(argv0, "Error while parsing \"--set %.*s\": %s",
			  (int)name.size, name.data, e.what());
	}
}

/** read configuration options from the command line */
void
ParseCommandLine(BpCmdLine &cmdline, BpConfig &config, int argc, char **argv)
{
	int ret;
	char *endptr;
#ifdef __GLIBC__
	static constexpr struct option long_options[] = {
		{"help", 0, nullptr, 'h'},
		{"version", 0, nullptr, 'V'},
		{"verbose", 0, nullptr, 'v'},
		{"quiet", 0, nullptr, 'q'},
		{"config-file", 1, nullptr, 'f'},
		{"logger-user", 1, nullptr, 'U'},
		{"translation-socket", 1, nullptr, 't'},
		{"cluster-size", 1, nullptr, 'C'},
		{"cluster-node", 1, nullptr, 'N'},
		{"ua-classes", 1, nullptr, 'a'},
		{"set", 1, nullptr, 's'},
		{"debug-listener-tag", 1, nullptr, 'L'},
		{nullptr, 0, nullptr, 0}
	};
#endif
	unsigned verbose = 1;

	while (1) {
#ifdef __GLIBC__
		int option_index = 0;

		ret = getopt_long(argc, argv,
				  "hVvqf:U:t:B:C:N:s:",
				  long_options, &option_index);
#else
		ret = getopt(argc, argv,
			     "hVvqf:U:t:B:C:N:s:");
#endif
		if (ret == -1)
			break;

		switch (ret) {
		case 'h':
			PrintUsage();
			exit(0);

		case 'V':
			printf("cm4all-beng-proxy v%s\n", VERSION);
			exit(0);

		case 'v':
			++verbose;
			break;

		case 'q':
			verbose = 0;
			break;

		case 'f':
			cmdline.config_file = optarg;
			break;

		case 'U':
			cmdline.logger_user.Lookup(optarg);
			break;

		case 't':
			config.translation_sockets.emplace_front(ParseSocketAddress(optarg, 0, false));
			break;

		case 'C':
			config.cluster_size = strtoul(optarg, &endptr, 10);
			if (endptr == optarg || *endptr != 0 ||
			    config.cluster_size > 1024)
				arg_error(argv[0], "Invalid cluster size number");

			if (config.cluster_node >= config.cluster_size)
				config.cluster_node = 0;
			break;

		case 'N':
			config.cluster_node = strtoul(optarg, &endptr, 10);
			if (endptr == optarg || *endptr != 0)
				arg_error(argv[0], "Invalid cluster size number");

			if ((config.cluster_node != 0 || config.cluster_size != 0) &&
			    config.cluster_node >= config.cluster_size)
				arg_error(argv[0], "Cluster node too large");
			break;

		case 'a':
			cmdline.ua_classification_file = optarg;
			break;

		case 's':
			HandleSet(config, argv[0], optarg);
			break;

		case 'L':
			cmdline.debug_listener_tag = optarg;
			break;

		case '?':
			arg_error(argv[0], nullptr);

		default:
			exit(1);
		}
	}

	SetLogLevel(verbose);

	/* check non-option arguments */

	if (optind < argc)
		arg_error(argv[0], "unrecognized argument: %s", argv[optind]);
}
<|endoftext|>


<commit_before><commit_msg>11995 - I Can Guess the Data Structure!<commit_after><|endoftext|>


<commit_before>#include <tests/lib/test.h>

#include <tests/lib/glib-helpers/test-conn-helper.h>

#include <tests/lib/glib/contacts-conn.h>

#include <TelepathyQt4/Connection>
#include <TelepathyQt4/Contact>
#include <TelepathyQt4/ContactManager>
#include <TelepathyQt4/PendingContacts>
#include <TelepathyQt4/PendingContactInfo>

#include <telepathy-glib/debug.h>

using namespace Tp;

class TestContactsInfo : public Test
{
    Q_OBJECT

public:
    TestContactsInfo(QObject *parent = 0)
        : Test(parent), mConn(0), mContactsInfoFieldsUpdated(0)
    { }

protected Q_SLOTS:
    void onContactInfoFieldsChanged(const Tp::Contact::InfoFields &);

private Q_SLOTS:
    void initTestCase();
    void init();

    void testInfo();

    void cleanup();
    void cleanupTestCase();

private:
    TestConnHelper *mConn;
    int mContactsInfoFieldsUpdated;
};

void TestContactsInfo::onContactInfoFieldsChanged(const Tp::Contact::InfoFields &info)
{
    Q_UNUSED(info);
    mContactsInfoFieldsUpdated++;
    mLoop->exit(0);
}

void TestContactsInfo::initTestCase()
{
    initTestCaseImpl();

    g_type_init();
    g_set_prgname("contacts-info");
    tp_debug_set_flags("all");
    dbus_g_bus_get(DBUS_BUS_STARTER, 0);

    mConn = new TestConnHelper(this,
            TP_TESTS_TYPE_CONTACTS_CONNECTION,
            "account", "me@example.com",
            "protocol", "foo",
            NULL);
    QCOMPARE(mConn->connect(), true);
}

void TestContactsInfo::init()
{
    initImpl();
    mContactsInfoFieldsUpdated = 0;
}

void TestContactsInfo::testInfo()
{
    ContactManagerPtr contactManager = mConn->client()->contactManager();

    QVERIFY(contactManager->supportedFeatures().contains(Contact::FeatureInfo));

    QStringList validIDs = QStringList() << QLatin1String("foo")
        << QLatin1String("bar");
    QList<ContactPtr> contacts = mConn->contacts(validIDs, Contact::FeatureInfo);
    QCOMPARE(contacts.size(), validIDs.size());
    for (int i = 0; i < contacts.size(); i++) {
        ContactPtr contact = contacts[i];

        QCOMPARE(contact->requestedFeatures().contains(Contact::FeatureInfo), true);
        QCOMPARE(contact->actualFeatures().contains(Contact::FeatureInfo), true);

        QVERIFY(contact->infoFields().allFields().isEmpty());

        QVERIFY(connect(contact.data(),
                        SIGNAL(infoFieldsChanged(const Tp::Contact::InfoFields &)),
                        SLOT(onContactInfoFieldsChanged(const Tp::Contact::InfoFields &))));
    }

    GPtrArray *info_default = (GPtrArray *) dbus_g_type_specialized_construct (
              TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST);
    {
        const gchar * const field_values[2] = {
            "FooBar", NULL
        };
        g_ptr_array_add (info_default, tp_value_array_build (3,
                    G_TYPE_STRING, "n",
                    G_TYPE_STRV, NULL,
                    G_TYPE_STRV, field_values,
                    G_TYPE_INVALID));
    }
    tp_tests_contacts_connection_set_default_contact_info(TP_TESTS_CONTACTS_CONNECTION(mConn->service()),
            info_default);

    GPtrArray *info_1 = (GPtrArray *) dbus_g_type_specialized_construct (
              TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST);
    {
        const gchar * const field_values[2] = {
            "Foo", NULL
        };
        g_ptr_array_add (info_1, tp_value_array_build (3,
                    G_TYPE_STRING, "n",
                    G_TYPE_STRV, NULL,
                    G_TYPE_STRV, field_values,
                    G_TYPE_INVALID));
    }
    GPtrArray *info_2 = (GPtrArray *) dbus_g_type_specialized_construct (
              TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST);
    {
        const gchar * const field_values[2] = {
            "Bar", NULL
        };
        g_ptr_array_add (info_2, tp_value_array_build (3,
                    G_TYPE_STRING, "n",
                    G_TYPE_STRV, NULL,
                    G_TYPE_STRV, field_values,
                    G_TYPE_INVALID));
    }

    TpHandle handles[] = { 0, 0 };
    TpHandleRepoIface *serviceRepo = tp_base_connection_get_handles(
            TP_BASE_CONNECTION(mConn->service()), TP_HANDLE_TYPE_CONTACT);

    for (unsigned i = 0; i < 2; i++) {
        handles[i] = tp_handle_ensure(serviceRepo, qPrintable(validIDs[i]),
                NULL, NULL);
    }

    tp_tests_contacts_connection_change_contact_info(TP_TESTS_CONTACTS_CONNECTION(mConn->service()),
            handles[0], info_1);
    tp_tests_contacts_connection_change_contact_info(TP_TESTS_CONTACTS_CONNECTION(mConn->service()),
            handles[1], info_2);

    while (mContactsInfoFieldsUpdated != 2)  {
        QCOMPARE(mLoop->exec(), 0);
    }

    QCOMPARE(mContactsInfoFieldsUpdated, 2);

    mContactsInfoFieldsUpdated = 0;
    ContactPtr contactFoo = contacts[0];
    ContactPtr contactBar = contacts[1];

    QCOMPARE(contactFoo->infoFields().isValid(), true);
    QCOMPARE(contactFoo->infoFields().allFields().size(), 1);
    QCOMPARE(contactFoo->infoFields().allFields()[0].fieldName, QLatin1String("n"));
    QCOMPARE(contactFoo->infoFields().allFields()[0].fieldValue[0], QLatin1String("Foo"));
    QCOMPARE(contactBar->infoFields().isValid(), true);
    QCOMPARE(contactBar->infoFields().allFields().size(), 1);
    QCOMPARE(contactBar->infoFields().allFields()[0].fieldName, QLatin1String("n"));
    QCOMPARE(contactBar->infoFields().allFields()[0].fieldValue[0], QLatin1String("Bar"));

    mContactsInfoFieldsUpdated = 0;
    Q_FOREACH (const ContactPtr &contact, contacts) {
        PendingOperation *op = contact->refreshInfo();
        QVERIFY(connect(op,
                    SIGNAL(finished(Tp::PendingOperation*)),
                    SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
        QCOMPARE(mLoop->exec(), 0);
    }

    while (mContactsInfoFieldsUpdated != contacts.size())  {
        mLoop->processEvents();
    }

    QCOMPARE(mContactsInfoFieldsUpdated, contacts.size());

    for (int i = 0; i < contacts.size(); i++) {
        ContactPtr contact = contacts[i];
        QVERIFY(disconnect(contact.data(),
                    SIGNAL(infoFieldsChanged(const Tp::Contact::InfoFields &)),
                    this,
                    SLOT(onContactInfoFieldsChanged(const Tp::Contact::InfoFields &))));
    }

    PendingContactInfo *pci = contactFoo->requestInfo();
    QVERIFY(connect(pci,
                SIGNAL(finished(Tp::PendingOperation*)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
    while (!pci->isFinished()) {
        QCOMPARE(mLoop->exec(), 0);
    }

    QCOMPARE(pci->infoFields().isValid(), true);
    QCOMPARE(pci->infoFields().allFields().size(), 1);
    QCOMPARE(pci->infoFields().allFields()[0].fieldName, QLatin1String("n"));
    QCOMPARE(pci->infoFields().allFields()[0].fieldValue[0], QLatin1String("FooBar"));

    g_boxed_free(TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, info_default);
    g_boxed_free(TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, info_1);
    g_boxed_free(TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, info_2);
}

void TestContactsInfo::cleanup()
{
    cleanupImpl();
}

void TestContactsInfo::cleanupTestCase()
{
    QCOMPARE(mConn->disconnect(), true);
    delete mConn;

    cleanupTestCaseImpl();
}

QTEST_MAIN(TestContactsInfo)
#include "_gen/contacts-info.cpp.moc.hpp"
<commit_msg>contacts-info test: Add test to check how many times RefreshContactInfo is called.<commit_after>#include <tests/lib/test.h>

#include <tests/lib/glib-helpers/test-conn-helper.h>

#include <tests/lib/glib/contacts-conn.h>

#include <TelepathyQt4/Connection>
#include <TelepathyQt4/Contact>
#include <TelepathyQt4/ContactManager>
#include <TelepathyQt4/PendingContacts>
#include <TelepathyQt4/PendingContactInfo>

#include <telepathy-glib/debug.h>

using namespace Tp;

class TestContactsInfo : public Test
{
    Q_OBJECT

public:
    TestContactsInfo(QObject *parent = 0)
        : Test(parent), mConn(0), mContactsInfoFieldsUpdated(0)
    { }

protected Q_SLOTS:
    void onContactInfoFieldsChanged(const Tp::Contact::InfoFields &);

private Q_SLOTS:
    void initTestCase();
    void init();

    void testInfo();

    void cleanup();
    void cleanupTestCase();

private:
    TestConnHelper *mConn;
    int mContactsInfoFieldsUpdated;
};

void TestContactsInfo::onContactInfoFieldsChanged(const Tp::Contact::InfoFields &info)
{
    Q_UNUSED(info);
    mContactsInfoFieldsUpdated++;
}

void TestContactsInfo::initTestCase()
{
    initTestCaseImpl();

    g_type_init();
    g_set_prgname("contacts-info");
    tp_debug_set_flags("all");
    dbus_g_bus_get(DBUS_BUS_STARTER, 0);

    mConn = new TestConnHelper(this,
            TP_TESTS_TYPE_CONTACTS_CONNECTION,
            "account", "me@example.com",
            "protocol", "foo",
            NULL);
    QCOMPARE(mConn->connect(), true);
}

void TestContactsInfo::init()
{
    initImpl();
    mContactsInfoFieldsUpdated = 0;
}

void TestContactsInfo::testInfo()
{
    ContactManagerPtr contactManager = mConn->client()->contactManager();

    QVERIFY(contactManager->supportedFeatures().contains(Contact::FeatureInfo));

    QStringList validIDs = QStringList() << QLatin1String("foo")
        << QLatin1String("bar");
    QList<ContactPtr> contacts = mConn->contacts(validIDs, Contact::FeatureInfo);
    QCOMPARE(contacts.size(), validIDs.size());
    for (int i = 0; i < contacts.size(); i++) {
        ContactPtr contact = contacts[i];

        QCOMPARE(contact->requestedFeatures().contains(Contact::FeatureInfo), true);
        QCOMPARE(contact->actualFeatures().contains(Contact::FeatureInfo), true);

        QVERIFY(contact->infoFields().allFields().isEmpty());

        QVERIFY(connect(contact.data(),
                        SIGNAL(infoFieldsChanged(const Tp::Contact::InfoFields &)),
                        SLOT(onContactInfoFieldsChanged(const Tp::Contact::InfoFields &))));
    }

    GPtrArray *info_default = (GPtrArray *) dbus_g_type_specialized_construct (
              TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST);
    {
        const gchar * const field_values[2] = {
            "FooBar", NULL
        };
        g_ptr_array_add (info_default, tp_value_array_build (3,
                    G_TYPE_STRING, "n",
                    G_TYPE_STRV, NULL,
                    G_TYPE_STRV, field_values,
                    G_TYPE_INVALID));
    }
    tp_tests_contacts_connection_set_default_contact_info(TP_TESTS_CONTACTS_CONNECTION(mConn->service()),
            info_default);

    GPtrArray *info_1 = (GPtrArray *) dbus_g_type_specialized_construct (
              TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST);
    {
        const gchar * const field_values[2] = {
            "Foo", NULL
        };
        g_ptr_array_add (info_1, tp_value_array_build (3,
                    G_TYPE_STRING, "n",
                    G_TYPE_STRV, NULL,
                    G_TYPE_STRV, field_values,
                    G_TYPE_INVALID));
    }
    GPtrArray *info_2 = (GPtrArray *) dbus_g_type_specialized_construct (
              TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST);
    {
        const gchar * const field_values[2] = {
            "Bar", NULL
        };
        g_ptr_array_add (info_2, tp_value_array_build (3,
                    G_TYPE_STRING, "n",
                    G_TYPE_STRV, NULL,
                    G_TYPE_STRV, field_values,
                    G_TYPE_INVALID));
    }

    TpHandle handles[] = { 0, 0 };
    TpHandleRepoIface *serviceRepo = tp_base_connection_get_handles(
            TP_BASE_CONNECTION(mConn->service()), TP_HANDLE_TYPE_CONTACT);

    for (unsigned i = 0; i < 2; i++) {
        handles[i] = tp_handle_ensure(serviceRepo, qPrintable(validIDs[i]),
                NULL, NULL);
    }

    tp_tests_contacts_connection_change_contact_info(TP_TESTS_CONTACTS_CONNECTION(mConn->service()),
            handles[0], info_1);
    tp_tests_contacts_connection_change_contact_info(TP_TESTS_CONTACTS_CONNECTION(mConn->service()),
            handles[1], info_2);

    while (mContactsInfoFieldsUpdated != 2)  {
        mLoop->processEvents();
    }

    QCOMPARE(mContactsInfoFieldsUpdated, 2);

    mContactsInfoFieldsUpdated = 0;
    ContactPtr contactFoo = contacts[0];
    ContactPtr contactBar = contacts[1];

    QCOMPARE(contactFoo->infoFields().isValid(), true);
    QCOMPARE(contactFoo->infoFields().allFields().size(), 1);
    QCOMPARE(contactFoo->infoFields().allFields()[0].fieldName, QLatin1String("n"));
    QCOMPARE(contactFoo->infoFields().allFields()[0].fieldValue[0], QLatin1String("Foo"));
    QCOMPARE(contactBar->infoFields().isValid(), true);
    QCOMPARE(contactBar->infoFields().allFields().size(), 1);
    QCOMPARE(contactBar->infoFields().allFields()[0].fieldName, QLatin1String("n"));
    QCOMPARE(contactBar->infoFields().allFields()[0].fieldValue[0], QLatin1String("Bar"));

    TpTestsContactsConnection *serviceConn = TP_TESTS_CONTACTS_CONNECTION(mConn->service());
    QCOMPARE(serviceConn->refresh_contact_info_called, static_cast<uint>(0));

    mContactsInfoFieldsUpdated = 0;
    Q_FOREACH (const ContactPtr &contact, contacts) {
        QVERIFY(contact->refreshInfo());
    }
    while (mContactsInfoFieldsUpdated != contacts.size())  {
        mLoop->processEvents();
    }

    QCOMPARE(mContactsInfoFieldsUpdated, contacts.size());

    QCOMPARE(serviceConn->refresh_contact_info_called, static_cast<uint>(contacts.size()));

    for (int i = 0; i < contacts.size(); i++) {
        ContactPtr contact = contacts[i];
        QVERIFY(disconnect(contact.data(),
                    SIGNAL(infoFieldsChanged(const Tp::Contact::InfoFields &)),
                    this,
                    SLOT(onContactInfoFieldsChanged(const Tp::Contact::InfoFields &))));
    }

    PendingContactInfo *pci = contactFoo->requestInfo();
    QVERIFY(connect(pci,
                SIGNAL(finished(Tp::PendingOperation*)),
                SLOT(expectSuccessfulCall(Tp::PendingOperation*))));
    while (!pci->isFinished()) {
        QCOMPARE(mLoop->exec(), 0);
    }

    QCOMPARE(pci->infoFields().isValid(), true);
    QCOMPARE(pci->infoFields().allFields().size(), 1);
    QCOMPARE(pci->infoFields().allFields()[0].fieldName, QLatin1String("n"));
    QCOMPARE(pci->infoFields().allFields()[0].fieldValue[0], QLatin1String("FooBar"));

    g_boxed_free(TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, info_default);
    g_boxed_free(TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, info_1);
    g_boxed_free(TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, info_2);
}

void TestContactsInfo::cleanup()
{
    cleanupImpl();
}

void TestContactsInfo::cleanupTestCase()
{
    QCOMPARE(mConn->disconnect(), true);
    delete mConn;

    cleanupTestCaseImpl();
}

QTEST_MAIN(TestContactsInfo)
#include "_gen/contacts-info.cpp.moc.hpp"
<|endoftext|>


<commit_before>// Copyright 2018 The Crashpad 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.

#include "client/crashpad_client.h"

#include <fcntl.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "client/client_argv_handling.h"
#include "util/file/file_io.h"
#include "util/linux/exception_handler_client.h"
#include "util/linux/exception_information.h"
#include "util/linux/scoped_pr_set_dumpable.h"
#include "util/linux/scoped_pr_set_ptracer.h"
#include "util/misc/from_pointer_cast.h"
#include "util/posix/double_fork_and_exec.h"
#include "util/posix/signals.h"

namespace crashpad {

namespace {

std::string FormatArgumentInt(const std::string& name, int value) {
  return base::StringPrintf("--%s=%d", name.c_str(), value);
}

std::string FormatArgumentAddress(const std::string& name, void* addr) {
  return base::StringPrintf("--%s=%p", name.c_str(), addr);
}

#if defined(OS_ANDROID)

std::vector<std::string> BuildAppProcessArgs(
    const std::string& class_name,
    const base::FilePath& database,
    const base::FilePath& metrics_dir,
    const std::string& url,
    const std::map<std::string, std::string>& annotations,
    const std::vector<std::string>& arguments,
    int socket) {
  std::vector<std::string> argv;
#if defined(ARCH_CPU_64_BITS)
  argv.push_back("/system/bin/app_process64");
#else
  argv.push_back("/system/bin/app_process32");
#endif
  argv.push_back("/system/bin");
  argv.push_back("--application");
  argv.push_back(class_name);

  std::vector<std::string> handler_argv = BuildHandlerArgvStrings(
      base::FilePath(), database, metrics_dir, url, annotations, arguments);

  if (socket != kInvalidFileHandle) {
    handler_argv.push_back(FormatArgumentInt("initial-client-fd", socket));
  }

  argv.insert(argv.end(), handler_argv.begin() + 1, handler_argv.end());
  return argv;
}

std::vector<std::string> BuildArgsToLaunchWithLinker(
    const std::string& handler_trampoline,
    const std::string& handler_library,
    bool is_64_bit,
    const base::FilePath& database,
    const base::FilePath& metrics_dir,
    const std::string& url,
    const std::map<std::string, std::string>& annotations,
    const std::vector<std::string>& arguments,
    int socket) {
  std::vector<std::string> argv;
  if (is_64_bit) {
    argv.push_back("/system/bin/linker64");
  } else {
    argv.push_back("/system/bin/linker");
  }
  argv.push_back(handler_trampoline);
  argv.push_back(handler_library);

  std::vector<std::string> handler_argv = BuildHandlerArgvStrings(
      base::FilePath(), database, metrics_dir, url, annotations, arguments);

  if (socket != kInvalidFileHandle) {
    handler_argv.push_back(FormatArgumentInt("initial-client-fd", socket));
  }

  argv.insert(argv.end(), handler_argv.begin() + 1, handler_argv.end());
  return argv;
}

#endif  // OS_ANDROID

// Launches a single use handler to snapshot this process.
class LaunchAtCrashHandler {
 public:
  static LaunchAtCrashHandler* Get() {
    static LaunchAtCrashHandler* instance = new LaunchAtCrashHandler();
    return instance;
  }

  bool Initialize(std::vector<std::string>* argv_in,
                  const std::vector<std::string>* envp) {
    argv_strings_.swap(*argv_in);

    if (envp) {
      envp_strings_ = *envp;
      StringVectorToCStringVector(envp_strings_, &envp_);
      set_envp_ = true;
    }

    argv_strings_.push_back(FormatArgumentAddress("trace-parent-with-exception",
                                                  &exception_information_));

    StringVectorToCStringVector(argv_strings_, &argv_);
    return Signals::InstallCrashHandlers(HandleCrash, 0, &old_actions_);
  }

  bool HandleCrashNonFatal(int signo, siginfo_t* siginfo, void* context) {
    if (first_chance_handler_ &&
        first_chance_handler_(
            signo, siginfo, static_cast<ucontext_t*>(context))) {
      return true;
    }

    exception_information_.siginfo_address =
        FromPointerCast<decltype(exception_information_.siginfo_address)>(
            siginfo);
    exception_information_.context_address =
        FromPointerCast<decltype(exception_information_.context_address)>(
            context);
    exception_information_.thread_id = syscall(SYS_gettid);

    ScopedPrSetPtracer set_ptracer(getpid(), /* may_log= */ false);
    ScopedPrSetDumpable set_dumpable(/* may_log= */ false);

    pid_t pid = fork();
    if (pid < 0) {
      return false;
    }
    if (pid == 0) {
      if (set_envp_) {
        execve(argv_[0],
               const_cast<char* const*>(argv_.data()),
               const_cast<char* const*>(envp_.data()));
      } else {
        execv(argv_[0], const_cast<char* const*>(argv_.data()));
      }
      _exit(EXIT_FAILURE);
    }

    int status;
    waitpid(pid, &status, 0);
    return false;
  }

  void HandleCrashFatal(int signo, siginfo_t* siginfo, void* context) {
    if (enabled_ && HandleCrashNonFatal(signo, siginfo, context)) {
      return;
    }
    Signals::RestoreHandlerAndReraiseSignalOnReturn(
        siginfo, old_actions_.ActionForSignal(signo));
  }

  void SetFirstChanceHandler(CrashpadClient::FirstChanceHandler handler) {
    first_chance_handler_ = handler;
  }

  static void Disable() { enabled_ = false; }

 private:
  LaunchAtCrashHandler() = default;

  ~LaunchAtCrashHandler() = delete;

  static void HandleCrash(int signo, siginfo_t* siginfo, void* context) {
    auto state = Get();
    state->HandleCrashFatal(signo, siginfo, context);
  }

  Signals::OldActions old_actions_ = {};
  std::vector<std::string> argv_strings_;
  std::vector<const char*> argv_;
  std::vector<std::string> envp_strings_;
  std::vector<const char*> envp_;
  ExceptionInformation exception_information_;
  CrashpadClient::FirstChanceHandler first_chance_handler_ = nullptr;
  bool set_envp_ = false;

  static thread_local bool enabled_;

  DISALLOW_COPY_AND_ASSIGN(LaunchAtCrashHandler);
};
thread_local bool LaunchAtCrashHandler::enabled_ = true;

// A pointer to the currently installed crash signal handler. This allows
// the static method CrashpadClient::DumpWithoutCrashing to simulate a crash
// using the currently configured crash handling strategy.
static LaunchAtCrashHandler* g_crash_handler;

}  // namespace

CrashpadClient::CrashpadClient() {}

CrashpadClient::~CrashpadClient() {}

bool CrashpadClient::StartHandler(
    const base::FilePath& handler,
    const base::FilePath& database,
    const base::FilePath& metrics_dir,
    const std::string& url,
    const std::map<std::string, std::string>& annotations,
    const std::vector<std::string>& arguments,
    bool restartable,
    bool asynchronous_start) {
  // TODO(jperaza): Implement this after the Android/Linux ExceptionHandlerSever
  // supports accepting new connections.
  // https://crashpad.chromium.org/bug/30
  NOTREACHED();
  return false;
}

#if defined(OS_ANDROID)

// static
bool CrashpadClient::StartJavaHandlerAtCrash(
    const std::string& class_name,
    const std::vector<std::string>* env,
    const base::FilePath& database,
    const base::FilePath& metrics_dir,
    const std::string& url,
    const std::map<std::string, std::string>& annotations,
    const std::vector<std::string>& arguments) {
  std::vector<std::string> argv = BuildAppProcessArgs(class_name,
                                                      database,
                                                      metrics_dir,
                                                      url,
                                                      annotations,
                                                      arguments,
                                                      kInvalidFileHandle);

  auto signal_handler = LaunchAtCrashHandler::Get();
  if (signal_handler->Initialize(&argv, env)) {
    DCHECK(!g_crash_handler);
    g_crash_handler = signal_handler;
    return true;
  }
  return false;
}

// static
bool CrashpadClient::StartJavaHandlerForClient(
    const std::string& class_name,
    const std::vector<std::string>* env,
    const base::FilePath& database,
    const base::FilePath& metrics_dir,
    const std::string& url,
    const std::map<std::string, std::string>& annotations,
    const std::vector<std::string>& arguments,
    int socket) {
  std::vector<std::string> argv = BuildAppProcessArgs(
      class_name, database, metrics_dir, url, annotations, arguments, socket);
  return DoubleForkAndExec(argv, env, socket, false, nullptr);
}

// static
bool CrashpadClient::StartHandlerWithLinkerAtCrash(
    const std::string& handler_trampoline,
    const std::string& handler_library,
    bool is_64_bit,
    const std::vector<std::string>* env,
    const base::FilePath& database,
    const base::FilePath& metrics_dir,
    const std::string& url,
    const std::map<std::string, std::string>& annotations,
    const std::vector<std::string>& arguments) {
  std::vector<std::string> argv =
      BuildArgsToLaunchWithLinker(handler_trampoline,
                                  handler_library,
                                  is_64_bit,
                                  database,
                                  metrics_dir,
                                  url,
                                  annotations,
                                  arguments,
                                  kInvalidFileHandle);
  auto signal_handler = LaunchAtCrashHandler::Get();
  if (signal_handler->Initialize(&argv, env)) {
    DCHECK(!g_crash_handler);
    g_crash_handler = signal_handler;
    return true;
  }
  return false;
}

// static
bool CrashpadClient::StartHandlerWithLinkerForClient(
    const std::string& handler_trampoline,
    const std::string& handler_library,
    bool is_64_bit,
    const std::vector<std::string>* env,
    const base::FilePath& database,
    const base::FilePath& metrics_dir,
    const std::string& url,
    const std::map<std::string, std::string>& annotations,
    const std::vector<std::string>& arguments,
    int socket) {
  std::vector<std::string> argv =
      BuildArgsToLaunchWithLinker(handler_trampoline,
                                  handler_library,
                                  is_64_bit,
                                  database,
                                  metrics_dir,
                                  url,
                                  annotations,
                                  arguments,
                                  socket);
  return DoubleForkAndExec(argv, env, socket, false, nullptr);
}

#endif

// static
bool CrashpadClient::StartHandlerAtCrash(
    const base::FilePath& handler,
    const base::FilePath& database,
    const base::FilePath& metrics_dir,
    const std::string& url,
    const std::map<std::string, std::string>& annotations,
    const std::vector<std::string>& arguments) {
  std::vector<std::string> argv = BuildHandlerArgvStrings(
      handler, database, metrics_dir, url, annotations, arguments);

  auto signal_handler = LaunchAtCrashHandler::Get();
  if (signal_handler->Initialize(&argv, nullptr)) {
    DCHECK(!g_crash_handler);
    g_crash_handler = signal_handler;
    return true;
  }
  return false;
}

// static
bool CrashpadClient::StartHandlerForClient(
    const base::FilePath& handler,
    const base::FilePath& database,
    const base::FilePath& metrics_dir,
    const std::string& url,
    const std::map<std::string, std::string>& annotations,
    const std::vector<std::string>& arguments,
    int socket) {
  std::vector<std::string> argv = BuildHandlerArgvStrings(
      handler, database, metrics_dir, url, annotations, arguments);

  argv.push_back(FormatArgumentInt("initial-client-fd", socket));

  return DoubleForkAndExec(argv, nullptr, socket, true, nullptr);
}

// static
void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) {
  DCHECK(g_crash_handler);

#if defined(ARCH_CPU_ARMEL)
  memset(context->uc_regspace, 0, sizeof(context->uc_regspace));
#elif defined(ARCH_CPU_ARM64)
  memset(context->uc_mcontext.__reserved,
         0,
         sizeof(context->uc_mcontext.__reserved));
#endif

  siginfo_t siginfo;
  siginfo.si_signo = Signals::kSimulatedSigno;
  siginfo.si_errno = 0;
  siginfo.si_code = 0;
  g_crash_handler->HandleCrashNonFatal(
      siginfo.si_signo, &siginfo, reinterpret_cast<void*>(context));
}

// static
void CrashpadClient::CrashWithoutDump(const std::string& message) {
  LaunchAtCrashHandler::Disable();
  LOG(FATAL) << message;
}

// static
void CrashpadClient::SetFirstChanceExceptionHandler(
    FirstChanceHandler handler) {
  DCHECK(g_crash_handler);
  g_crash_handler->SetFirstChanceHandler(handler);
}

}  // namespace crashpad
<commit_msg>linux: Disable DumpWithoutCrash() if Crashpad isn't initialized<commit_after>// Copyright 2018 The Crashpad 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.

#include "client/crashpad_client.h"

#include <fcntl.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "client/client_argv_handling.h"
#include "util/file/file_io.h"
#include "util/linux/exception_handler_client.h"
#include "util/linux/exception_information.h"
#include "util/linux/scoped_pr_set_dumpable.h"
#include "util/linux/scoped_pr_set_ptracer.h"
#include "util/misc/from_pointer_cast.h"
#include "util/posix/double_fork_and_exec.h"
#include "util/posix/signals.h"

namespace crashpad {

namespace {

std::string FormatArgumentInt(const std::string& name, int value) {
  return base::StringPrintf("--%s=%d", name.c_str(), value);
}

std::string FormatArgumentAddress(const std::string& name, void* addr) {
  return base::StringPrintf("--%s=%p", name.c_str(), addr);
}

#if defined(OS_ANDROID)

std::vector<std::string> BuildAppProcessArgs(
    const std::string& class_name,
    const base::FilePath& database,
    const base::FilePath& metrics_dir,
    const std::string& url,
    const std::map<std::string, std::string>& annotations,
    const std::vector<std::string>& arguments,
    int socket) {
  std::vector<std::string> argv;
#if defined(ARCH_CPU_64_BITS)
  argv.push_back("/system/bin/app_process64");
#else
  argv.push_back("/system/bin/app_process32");
#endif
  argv.push_back("/system/bin");
  argv.push_back("--application");
  argv.push_back(class_name);

  std::vector<std::string> handler_argv = BuildHandlerArgvStrings(
      base::FilePath(), database, metrics_dir, url, annotations, arguments);

  if (socket != kInvalidFileHandle) {
    handler_argv.push_back(FormatArgumentInt("initial-client-fd", socket));
  }

  argv.insert(argv.end(), handler_argv.begin() + 1, handler_argv.end());
  return argv;
}

std::vector<std::string> BuildArgsToLaunchWithLinker(
    const std::string& handler_trampoline,
    const std::string& handler_library,
    bool is_64_bit,
    const base::FilePath& database,
    const base::FilePath& metrics_dir,
    const std::string& url,
    const std::map<std::string, std::string>& annotations,
    const std::vector<std::string>& arguments,
    int socket) {
  std::vector<std::string> argv;
  if (is_64_bit) {
    argv.push_back("/system/bin/linker64");
  } else {
    argv.push_back("/system/bin/linker");
  }
  argv.push_back(handler_trampoline);
  argv.push_back(handler_library);

  std::vector<std::string> handler_argv = BuildHandlerArgvStrings(
      base::FilePath(), database, metrics_dir, url, annotations, arguments);

  if (socket != kInvalidFileHandle) {
    handler_argv.push_back(FormatArgumentInt("initial-client-fd", socket));
  }

  argv.insert(argv.end(), handler_argv.begin() + 1, handler_argv.end());
  return argv;
}

#endif  // OS_ANDROID

// Launches a single use handler to snapshot this process.
class LaunchAtCrashHandler {
 public:
  static LaunchAtCrashHandler* Get() {
    static LaunchAtCrashHandler* instance = new LaunchAtCrashHandler();
    return instance;
  }

  bool Initialize(std::vector<std::string>* argv_in,
                  const std::vector<std::string>* envp) {
    argv_strings_.swap(*argv_in);

    if (envp) {
      envp_strings_ = *envp;
      StringVectorToCStringVector(envp_strings_, &envp_);
      set_envp_ = true;
    }

    argv_strings_.push_back(FormatArgumentAddress("trace-parent-with-exception",
                                                  &exception_information_));

    StringVectorToCStringVector(argv_strings_, &argv_);
    return Signals::InstallCrashHandlers(HandleCrash, 0, &old_actions_);
  }

  bool HandleCrashNonFatal(int signo, siginfo_t* siginfo, void* context) {
    if (first_chance_handler_ &&
        first_chance_handler_(
            signo, siginfo, static_cast<ucontext_t*>(context))) {
      return true;
    }

    exception_information_.siginfo_address =
        FromPointerCast<decltype(exception_information_.siginfo_address)>(
            siginfo);
    exception_information_.context_address =
        FromPointerCast<decltype(exception_information_.context_address)>(
            context);
    exception_information_.thread_id = syscall(SYS_gettid);

    ScopedPrSetPtracer set_ptracer(getpid(), /* may_log= */ false);
    ScopedPrSetDumpable set_dumpable(/* may_log= */ false);

    pid_t pid = fork();
    if (pid < 0) {
      return false;
    }
    if (pid == 0) {
      if (set_envp_) {
        execve(argv_[0],
               const_cast<char* const*>(argv_.data()),
               const_cast<char* const*>(envp_.data()));
      } else {
        execv(argv_[0], const_cast<char* const*>(argv_.data()));
      }
      _exit(EXIT_FAILURE);
    }

    int status;
    waitpid(pid, &status, 0);
    return false;
  }

  void HandleCrashFatal(int signo, siginfo_t* siginfo, void* context) {
    if (enabled_ && HandleCrashNonFatal(signo, siginfo, context)) {
      return;
    }
    Signals::RestoreHandlerAndReraiseSignalOnReturn(
        siginfo, old_actions_.ActionForSignal(signo));
  }

  void SetFirstChanceHandler(CrashpadClient::FirstChanceHandler handler) {
    first_chance_handler_ = handler;
  }

  static void Disable() { enabled_ = false; }

 private:
  LaunchAtCrashHandler() = default;

  ~LaunchAtCrashHandler() = delete;

  static void HandleCrash(int signo, siginfo_t* siginfo, void* context) {
    auto state = Get();
    state->HandleCrashFatal(signo, siginfo, context);
  }

  Signals::OldActions old_actions_ = {};
  std::vector<std::string> argv_strings_;
  std::vector<const char*> argv_;
  std::vector<std::string> envp_strings_;
  std::vector<const char*> envp_;
  ExceptionInformation exception_information_;
  CrashpadClient::FirstChanceHandler first_chance_handler_ = nullptr;
  bool set_envp_ = false;

  static thread_local bool enabled_;

  DISALLOW_COPY_AND_ASSIGN(LaunchAtCrashHandler);
};
thread_local bool LaunchAtCrashHandler::enabled_ = true;

// A pointer to the currently installed crash signal handler. This allows
// the static method CrashpadClient::DumpWithoutCrashing to simulate a crash
// using the currently configured crash handling strategy.
static LaunchAtCrashHandler* g_crash_handler;

}  // namespace

CrashpadClient::CrashpadClient() {}

CrashpadClient::~CrashpadClient() {}

bool CrashpadClient::StartHandler(
    const base::FilePath& handler,
    const base::FilePath& database,
    const base::FilePath& metrics_dir,
    const std::string& url,
    const std::map<std::string, std::string>& annotations,
    const std::vector<std::string>& arguments,
    bool restartable,
    bool asynchronous_start) {
  // TODO(jperaza): Implement this after the Android/Linux ExceptionHandlerSever
  // supports accepting new connections.
  // https://crashpad.chromium.org/bug/30
  NOTREACHED();
  return false;
}

#if defined(OS_ANDROID)

// static
bool CrashpadClient::StartJavaHandlerAtCrash(
    const std::string& class_name,
    const std::vector<std::string>* env,
    const base::FilePath& database,
    const base::FilePath& metrics_dir,
    const std::string& url,
    const std::map<std::string, std::string>& annotations,
    const std::vector<std::string>& arguments) {
  std::vector<std::string> argv = BuildAppProcessArgs(class_name,
                                                      database,
                                                      metrics_dir,
                                                      url,
                                                      annotations,
                                                      arguments,
                                                      kInvalidFileHandle);

  auto signal_handler = LaunchAtCrashHandler::Get();
  if (signal_handler->Initialize(&argv, env)) {
    DCHECK(!g_crash_handler);
    g_crash_handler = signal_handler;
    return true;
  }
  return false;
}

// static
bool CrashpadClient::StartJavaHandlerForClient(
    const std::string& class_name,
    const std::vector<std::string>* env,
    const base::FilePath& database,
    const base::FilePath& metrics_dir,
    const std::string& url,
    const std::map<std::string, std::string>& annotations,
    const std::vector<std::string>& arguments,
    int socket) {
  std::vector<std::string> argv = BuildAppProcessArgs(
      class_name, database, metrics_dir, url, annotations, arguments, socket);
  return DoubleForkAndExec(argv, env, socket, false, nullptr);
}

// static
bool CrashpadClient::StartHandlerWithLinkerAtCrash(
    const std::string& handler_trampoline,
    const std::string& handler_library,
    bool is_64_bit,
    const std::vector<std::string>* env,
    const base::FilePath& database,
    const base::FilePath& metrics_dir,
    const std::string& url,
    const std::map<std::string, std::string>& annotations,
    const std::vector<std::string>& arguments) {
  std::vector<std::string> argv =
      BuildArgsToLaunchWithLinker(handler_trampoline,
                                  handler_library,
                                  is_64_bit,
                                  database,
                                  metrics_dir,
                                  url,
                                  annotations,
                                  arguments,
                                  kInvalidFileHandle);
  auto signal_handler = LaunchAtCrashHandler::Get();
  if (signal_handler->Initialize(&argv, env)) {
    DCHECK(!g_crash_handler);
    g_crash_handler = signal_handler;
    return true;
  }
  return false;
}

// static
bool CrashpadClient::StartHandlerWithLinkerForClient(
    const std::string& handler_trampoline,
    const std::string& handler_library,
    bool is_64_bit,
    const std::vector<std::string>* env,
    const base::FilePath& database,
    const base::FilePath& metrics_dir,
    const std::string& url,
    const std::map<std::string, std::string>& annotations,
    const std::vector<std::string>& arguments,
    int socket) {
  std::vector<std::string> argv =
      BuildArgsToLaunchWithLinker(handler_trampoline,
                                  handler_library,
                                  is_64_bit,
                                  database,
                                  metrics_dir,
                                  url,
                                  annotations,
                                  arguments,
                                  socket);
  return DoubleForkAndExec(argv, env, socket, false, nullptr);
}

#endif

// static
bool CrashpadClient::StartHandlerAtCrash(
    const base::FilePath& handler,
    const base::FilePath& database,
    const base::FilePath& metrics_dir,
    const std::string& url,
    const std::map<std::string, std::string>& annotations,
    const std::vector<std::string>& arguments) {
  std::vector<std::string> argv = BuildHandlerArgvStrings(
      handler, database, metrics_dir, url, annotations, arguments);

  auto signal_handler = LaunchAtCrashHandler::Get();
  if (signal_handler->Initialize(&argv, nullptr)) {
    DCHECK(!g_crash_handler);
    g_crash_handler = signal_handler;
    return true;
  }
  return false;
}

// static
bool CrashpadClient::StartHandlerForClient(
    const base::FilePath& handler,
    const base::FilePath& database,
    const base::FilePath& metrics_dir,
    const std::string& url,
    const std::map<std::string, std::string>& annotations,
    const std::vector<std::string>& arguments,
    int socket) {
  std::vector<std::string> argv = BuildHandlerArgvStrings(
      handler, database, metrics_dir, url, annotations, arguments);

  argv.push_back(FormatArgumentInt("initial-client-fd", socket));

  return DoubleForkAndExec(argv, nullptr, socket, true, nullptr);
}

// static
void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) {
  if (!g_crash_handler) {
    DLOG(ERROR) << "Crashpad isn't enabled";
    return;
  }

#if defined(ARCH_CPU_ARMEL)
  memset(context->uc_regspace, 0, sizeof(context->uc_regspace));
#elif defined(ARCH_CPU_ARM64)
  memset(context->uc_mcontext.__reserved,
         0,
         sizeof(context->uc_mcontext.__reserved));
#endif

  siginfo_t siginfo;
  siginfo.si_signo = Signals::kSimulatedSigno;
  siginfo.si_errno = 0;
  siginfo.si_code = 0;
  g_crash_handler->HandleCrashNonFatal(
      siginfo.si_signo, &siginfo, reinterpret_cast<void*>(context));
}

// static
void CrashpadClient::CrashWithoutDump(const std::string& message) {
  LaunchAtCrashHandler::Disable();
  LOG(FATAL) << message;
}

// static
void CrashpadClient::SetFirstChanceExceptionHandler(
    FirstChanceHandler handler) {
  DCHECK(g_crash_handler);
  g_crash_handler->SetFirstChanceHandler(handler);
}

}  // namespace crashpad
<|endoftext|>


<commit_before>#ifndef ENTT_CORE_MEMORY_HPP
#define ENTT_CORE_MEMORY_HPP

#include <cstddef>
#include <limits>
#include <memory>
#include <type_traits>
#include <utility>
#include "../config/config.h"

namespace entt {

/**
 * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
 * @tparam Type Pointer type.
 * @param ptr Fancy or raw pointer.
 * @return A raw pointer that represents the address of the original pointer.
 */
template<typename Type>
[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT {
    if constexpr(std::is_pointer_v<std::remove_const_t<std::remove_reference_t<Type>>>) {
        return ptr;
    } else {
        return to_address(std::forward<Type>(ptr).operator->());
    }
}

/**
 * @brief Utility function to design allocation-aware containers.
 * @tparam Allocator Type of allocator.
 * @param lhs A valid allocator.
 * @param rhs Another valid allocator.
 */
template<typename Allocator>
constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
    if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
        lhs = rhs;
    }
}

/**
 * @brief Utility function to design allocation-aware containers.
 * @tparam Allocator Type of allocator.
 * @param lhs A valid allocator.
 * @param rhs Another valid allocator.
 */
template<typename Allocator>
constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
    if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
        lhs = std::move(rhs);
    }
}

/**
 * @brief Utility function to design allocation-aware containers.
 * @tparam Allocator Type of allocator.
 * @param lhs A valid allocator.
 * @param rhs Another valid allocator.
 */
template<typename Allocator>
constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
    ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers");

    if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
        using std::swap;
        swap(lhs, rhs);
    }
}

/**
 * @brief Checks whether a value is a power of two or not.
 * @param value A value that may or may not be a power of two.
 * @return True if the value is a power of two, false otherwise.
 */
[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
    return value && ((value & (value - 1)) == 0);
}

/**
 * @brief Computes the smallest power of two greater than or equal to a value.
 * @param value The value to use.
 * @return The smallest power of two greater than or equal to the given value.
 */
[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
    ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
    std::size_t curr = value - (value != 0u);

    for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
        curr |= curr >> next;
    }

    return ++curr;
}

/**
 * @brief Fast module utility function (powers of two only).
 * @param value A value for which to calculate the modulus.
 * @param mod _Modulus_, it must be a power of two.
 * @return The common remainder.
 */
[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT {
    ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two");
    return value & (mod - 1u);
}

/**
 * @brief General purpose deleter for allocator-aware unique pointers.
 * @tparam Args Types of arguments to use to construct the object.
 */
template<typename Allocator>
struct allocation_deleter: private Allocator {
    /*! @brief Allocator type. */
    using allocator_type = Allocator;
    /*! @brief Pointer type. */
    using pointer = typename std::allocator_traits<Allocator>::pointer;

    /**
     * @brief Inherited constructors.
     * @param allocator The allocator to use.
     */
    allocation_deleter(const allocator_type &allocator)
        : Allocator{allocator} {}

    /**
     * @brief Destroys the pointed object and deallocates its memory.
     * @param ptr A valid pointer to an object of the given type.
     */
    void operator()(pointer ptr) {
        using alloc_traits = typename std::allocator_traits<Allocator>;
        alloc_traits::destroy(*this, to_address(ptr));
        alloc_traits::deallocate(*this, ptr, 1u);
    }
};

/**
 * @brief Allows `std::unique_ptr` to use allocators.
 * @tparam Type Type of object to allocate for and to construct.
 * @tparam Allocator Type of allocator used to manage memory and elements.
 * @tparam Args Types of arguments to use to construct the object.
 * @param allocator The allocator to use.
 * @param args Parameters to use to construct an object for the entity.
 * @return A properly initialized unique pointer with a custom deleter.
 */
template<typename Type, typename Allocator, typename... Args>
auto allocate_unique(Allocator &allocator, Args &&...args) {
    using alloc = typename std::allocator_traits<Allocator>::template rebind_alloc<Type>;
    using alloc_traits = typename std::allocator_traits<alloc>;

    alloc type_allocator{allocator};
    auto ptr = alloc_traits::allocate(type_allocator, 1u);

    ENTT_TRY {
        alloc_traits::construct(type_allocator, to_address(ptr), std::forward<Args>(args)...);
    }
    ENTT_CATCH {
        alloc_traits::deallocate(type_allocator, ptr, 1u);
        ENTT_THROW;
    }

    return std::unique_ptr<Type, allocation_deleter<alloc>>{ptr, type_allocator};
}

} // namespace entt

#endif
<commit_msg>memory: suppress a (shadow) warning from gcc<commit_after>#ifndef ENTT_CORE_MEMORY_HPP
#define ENTT_CORE_MEMORY_HPP

#include <cstddef>
#include <limits>
#include <memory>
#include <type_traits>
#include <utility>
#include "../config/config.h"

namespace entt {

/**
 * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
 * @tparam Type Pointer type.
 * @param ptr Fancy or raw pointer.
 * @return A raw pointer that represents the address of the original pointer.
 */
template<typename Type>
[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT {
    if constexpr(std::is_pointer_v<std::remove_const_t<std::remove_reference_t<Type>>>) {
        return ptr;
    } else {
        return to_address(std::forward<Type>(ptr).operator->());
    }
}

/**
 * @brief Utility function to design allocation-aware containers.
 * @tparam Allocator Type of allocator.
 * @param lhs A valid allocator.
 * @param rhs Another valid allocator.
 */
template<typename Allocator>
constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
    if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
        lhs = rhs;
    }
}

/**
 * @brief Utility function to design allocation-aware containers.
 * @tparam Allocator Type of allocator.
 * @param lhs A valid allocator.
 * @param rhs Another valid allocator.
 */
template<typename Allocator>
constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
    if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
        lhs = std::move(rhs);
    }
}

/**
 * @brief Utility function to design allocation-aware containers.
 * @tparam Allocator Type of allocator.
 * @param lhs A valid allocator.
 * @param rhs Another valid allocator.
 */
template<typename Allocator>
constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
    ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers");

    if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
        using std::swap;
        swap(lhs, rhs);
    }
}

/**
 * @brief Checks whether a value is a power of two or not.
 * @param value A value that may or may not be a power of two.
 * @return True if the value is a power of two, false otherwise.
 */
[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
    return value && ((value & (value - 1)) == 0);
}

/**
 * @brief Computes the smallest power of two greater than or equal to a value.
 * @param value The value to use.
 * @return The smallest power of two greater than or equal to the given value.
 */
[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
    ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
    std::size_t curr = value - (value != 0u);

    for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
        curr |= curr >> next;
    }

    return ++curr;
}

/**
 * @brief Fast module utility function (powers of two only).
 * @param value A value for which to calculate the modulus.
 * @param mod _Modulus_, it must be a power of two.
 * @return The common remainder.
 */
[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT {
    ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two");
    return value & (mod - 1u);
}

/**
 * @brief General purpose deleter for allocator-aware unique pointers.
 * @tparam Args Types of arguments to use to construct the object.
 */
template<typename Allocator>
struct allocation_deleter: private Allocator {
    /*! @brief Allocator type. */
    using allocator_type = Allocator;
    /*! @brief Pointer type. */
    using pointer = typename std::allocator_traits<Allocator>::pointer;

    /**
     * @brief Inherited constructors.
     * @param alloc The allocator to use.
     */
    allocation_deleter(const allocator_type &alloc)
        : Allocator{alloc} {}

    /**
     * @brief Destroys the pointed object and deallocates its memory.
     * @param ptr A valid pointer to an object of the given type.
     */
    void operator()(pointer ptr) {
        using alloc_traits = typename std::allocator_traits<Allocator>;
        alloc_traits::destroy(*this, to_address(ptr));
        alloc_traits::deallocate(*this, ptr, 1u);
    }
};

/**
 * @brief Allows `std::unique_ptr` to use allocators.
 * @tparam Type Type of object to allocate for and to construct.
 * @tparam Allocator Type of allocator used to manage memory and elements.
 * @tparam Args Types of arguments to use to construct the object.
 * @param allocator The allocator to use.
 * @param args Parameters to use to construct an object for the entity.
 * @return A properly initialized unique pointer with a custom deleter.
 */
template<typename Type, typename Allocator, typename... Args>
auto allocate_unique(Allocator &allocator, Args &&...args) {
    using alloc = typename std::allocator_traits<Allocator>::template rebind_alloc<Type>;
    using alloc_traits = typename std::allocator_traits<alloc>;

    alloc type_allocator{allocator};
    auto ptr = alloc_traits::allocate(type_allocator, 1u);

    ENTT_TRY {
        alloc_traits::construct(type_allocator, to_address(ptr), std::forward<Args>(args)...);
    }
    ENTT_CATCH {
        alloc_traits::deallocate(type_allocator, ptr, 1u);
        ENTT_THROW;
    }

    return std::unique_ptr<Type, allocation_deleter<alloc>>{ptr, type_allocator};
}

} // namespace entt

#endif
<|endoftext|>


<commit_before>#include "stdafx.h"
/*
* Copyright (c) 2007-2009 SlimDX Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include <d3d10.h>
#include <d3dx10.h>

#include "../stack_array.h"

#include "BlendState.h"
#include "DepthStencilState.h"
#include "DepthStencilView.h"
#include "OutputMergerWrapper.h"
#include "RenderTargetView.h"

using namespace System;

namespace SlimDX
{
namespace Direct3D10
{
	OutputMergerWrapper::OutputMergerWrapper( ID3D10Device* device )
	{
		if( device == 0 )
			throw gcnew ArgumentNullException( "device" );
		m_Device = device;
	}

	void OutputMergerWrapper::DepthStencilState::set( SlimDX::Direct3D10::DepthStencilState^ value )
	{
		ID3D10DepthStencilState* oldState;
		int oldReference;
		m_Device->OMGetDepthStencilState( &oldState, reinterpret_cast<UINT*>( &oldReference ) );
		oldState->Release();

		if( value == nullptr )
			m_Device->OMSetDepthStencilState( 0, oldReference );
		else
			m_Device->OMSetDepthStencilState( value->InternalPointer, oldReference );
	}

	SlimDX::Direct3D10::DepthStencilState^ OutputMergerWrapper::DepthStencilState::get()
	{
		ID3D10DepthStencilState* oldState = 0;
		int oldReference = 0;
		m_Device->OMGetDepthStencilState( &oldState, reinterpret_cast<UINT*>( &oldReference ) );

		return SlimDX::Direct3D10::DepthStencilState::FromPointer( oldState );
	}

	void OutputMergerWrapper::DepthStencilReference::set( int value )
	{
		ID3D10DepthStencilState* oldState = 0;
		int oldReference = 0;
		m_Device->OMGetDepthStencilState( &oldState, reinterpret_cast<UINT*>( &oldReference ) );

		m_Device->OMSetDepthStencilState( oldState, value );
		oldState->Release();
	}

	int OutputMergerWrapper::DepthStencilReference::get()
	{
		ID3D10DepthStencilState* oldState = 0;
		int oldReference = 0;
		m_Device->OMGetDepthStencilState( &oldState, reinterpret_cast<UINT*>( &oldReference ) );
		oldState->Release();

		return oldReference;
	}

	void OutputMergerWrapper::BlendState::set( SlimDX::Direct3D10::BlendState^ value )
	{
		ID3D10BlendState* oldState = 0;
		float oldFactor[4];
		int oldMask = 0;
		m_Device->OMGetBlendState( &oldState, oldFactor, reinterpret_cast<UINT*>( &oldMask ) );
		oldState->Release();

		if( value == nullptr )
			m_Device->OMSetBlendState( 0, oldFactor, oldMask );
		else
			m_Device->OMSetBlendState( value->InternalPointer, oldFactor, oldMask );
	}

	SlimDX::Direct3D10::BlendState^ OutputMergerWrapper::BlendState::get()
	{
		ID3D10BlendState* oldState = 0;
		float oldFactor[4];
		int oldMask = 0;
		m_Device->OMGetBlendState( &oldState, oldFactor, reinterpret_cast<UINT*>( &oldMask ) );

		return SlimDX::Direct3D10::BlendState::FromPointer( oldState );
	}

	void OutputMergerWrapper::BlendFactor::set( Color4 value )
	{
		ID3D10BlendState* oldState = 0;
		float oldFactor[4];
		int oldMask = 0;
		m_Device->OMGetBlendState( &oldState, oldFactor, reinterpret_cast<UINT*>( &oldMask ) );

		float newFactor[4] = { value.Red, value.Green, value.Blue, value.Alpha };
		m_Device->OMSetBlendState( oldState, newFactor, oldMask );
		oldState->Release();
	}

	Color4 OutputMergerWrapper::BlendFactor::get()
	{
		ID3D10BlendState* oldState = 0;
		float oldFactor[4];
		int oldMask = 0;
		m_Device->OMGetBlendState( &oldState, oldFactor, reinterpret_cast<UINT*>( &oldMask ) );
		oldState->Release();

		return Color4( oldFactor[3], oldFactor[0], oldFactor[1], oldFactor[2] );
	}

	void OutputMergerWrapper::BlendSampleMask::set( int value )
	{
		ID3D10BlendState* oldState = 0;
		float oldFactor[4];
		int oldMask = 0;
		m_Device->OMGetBlendState( &oldState, oldFactor, reinterpret_cast<UINT*>( &oldMask ) );

		m_Device->OMSetBlendState( oldState, oldFactor, value );
		oldState->Release();
	}

	int OutputMergerWrapper::BlendSampleMask::get()
	{
		ID3D10BlendState* oldState = 0;
		float oldFactor[4];
		int oldMask = 0;
		m_Device->OMGetBlendState( &oldState, oldFactor, reinterpret_cast<UINT*>( &oldMask ) );
		oldState->Release();

		return oldMask;
	}

	void OutputMergerWrapper::SetTargets( RenderTargetView^ renderTargetView )
	{
		SetTargets( nullptr, renderTargetView );
	}

	void OutputMergerWrapper::SetTargets( DepthStencilView^ depthStencilView, RenderTargetView^ renderTargetView )
	{
		ID3D10DepthStencilView *nativeDSV = depthStencilView == nullptr ? 0 : static_cast<ID3D10DepthStencilView*>( depthStencilView->InternalPointer );
		ID3D10RenderTargetView *nativeRTV[] = { renderTargetView == nullptr ? 0 : static_cast<ID3D10RenderTargetView*>( renderTargetView->InternalPointer ) };

		m_Device->OMSetRenderTargets( 1, nativeRTV, nativeDSV );
	}

	void OutputMergerWrapper::SetTargets( ... array<RenderTargetView^>^ renderTargets )
	{
		SetTargets( nullptr, renderTargets );
	}

	void OutputMergerWrapper::SetTargets( DepthStencilView^ depthStencilView, ... array<RenderTargetView^>^ renderTargets )
	{
		ID3D10DepthStencilView *nativeDSV = depthStencilView == nullptr ? 0 : static_cast<ID3D10DepthStencilView*>( depthStencilView->InternalPointer );
		ID3D10RenderTargetView* nativeRTVs[D3D10_SIMULTANEOUS_RENDER_TARGET_COUNT];

		if( renderTargets == nullptr )
		{
			m_Device->OMSetRenderTargets( 0, 0, nativeDSV );
		}
		else
		{
			for( int i = 0; i < renderTargets->Length; ++i )
				nativeRTVs[ i ] = renderTargets[ i ] == nullptr ? 0 : static_cast<ID3D10RenderTargetView*>( renderTargets[ i ]->InternalPointer );
			m_Device->OMSetRenderTargets( renderTargets->Length, nativeRTVs, nativeDSV );
		}
	}

	DepthStencilView^ OutputMergerWrapper::GetDepthStencilView()
	{
		ID3D10DepthStencilView *view;

		m_Device->OMGetRenderTargets( 0, 0, &view );
		return DepthStencilView::FromPointer( view );
	}

	array<RenderTargetView^>^ OutputMergerWrapper::GetRenderTargets( int count )
	{
		stack_array<ID3D10RenderTargetView*> targets = stackalloc( ID3D10RenderTargetView*, count );
		array<RenderTargetView^>^ results = gcnew array<RenderTargetView^>( count );

		m_Device->OMGetRenderTargets( count, &targets[0], 0 );

		for( int i = 0; i < count; i++ )
			results[i] = RenderTargetView::FromPointer( targets[i] );

		return results;
	}
}
}
<commit_msg>Added null checks to oldState->Release in OutputMergerWrapper. Fixes issue 536.<commit_after>#include "stdafx.h"
/*
* Copyright (c) 2007-2009 SlimDX Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include <d3d10.h>
#include <d3dx10.h>

#include "../stack_array.h"

#include "BlendState.h"
#include "DepthStencilState.h"
#include "DepthStencilView.h"
#include "OutputMergerWrapper.h"
#include "RenderTargetView.h"

using namespace System;

namespace SlimDX
{
namespace Direct3D10
{
	OutputMergerWrapper::OutputMergerWrapper( ID3D10Device* device )
	{
		if( device == 0 )
			throw gcnew ArgumentNullException( "device" );
		m_Device = device;
	}

	void OutputMergerWrapper::DepthStencilState::set( SlimDX::Direct3D10::DepthStencilState^ value )
	{
		ID3D10DepthStencilState* oldState;
		int oldReference;
		m_Device->OMGetDepthStencilState( &oldState, reinterpret_cast<UINT*>( &oldReference ) );
		if( oldState != NULL )
			oldState->Release();

		if( value == nullptr )
			m_Device->OMSetDepthStencilState( 0, oldReference );
		else
			m_Device->OMSetDepthStencilState( value->InternalPointer, oldReference );
	}

	SlimDX::Direct3D10::DepthStencilState^ OutputMergerWrapper::DepthStencilState::get()
	{
		ID3D10DepthStencilState* oldState = 0;
		int oldReference = 0;
		m_Device->OMGetDepthStencilState( &oldState, reinterpret_cast<UINT*>( &oldReference ) );

		return SlimDX::Direct3D10::DepthStencilState::FromPointer( oldState );
	}

	void OutputMergerWrapper::DepthStencilReference::set( int value )
	{
		ID3D10DepthStencilState* oldState = 0;
		int oldReference = 0;
		m_Device->OMGetDepthStencilState( &oldState, reinterpret_cast<UINT*>( &oldReference ) );

		m_Device->OMSetDepthStencilState( oldState, value );
		if( oldState != NULL )
			oldState->Release();
	}

	int OutputMergerWrapper::DepthStencilReference::get()
	{
		ID3D10DepthStencilState* oldState = 0;
		int oldReference = 0;
		m_Device->OMGetDepthStencilState( &oldState, reinterpret_cast<UINT*>( &oldReference ) );
		if( oldState != NULL )
			oldState->Release();

		return oldReference;
	}

	void OutputMergerWrapper::BlendState::set( SlimDX::Direct3D10::BlendState^ value )
	{
		ID3D10BlendState* oldState = 0;
		float oldFactor[4];
		int oldMask = 0;
		m_Device->OMGetBlendState( &oldState, oldFactor, reinterpret_cast<UINT*>( &oldMask ) );
		if( oldState != NULL )
			oldState->Release();

		if( value == nullptr )
			m_Device->OMSetBlendState( 0, oldFactor, oldMask );
		else
			m_Device->OMSetBlendState( value->InternalPointer, oldFactor, oldMask );
	}

	SlimDX::Direct3D10::BlendState^ OutputMergerWrapper::BlendState::get()
	{
		ID3D10BlendState* oldState = 0;
		float oldFactor[4];
		int oldMask = 0;
		m_Device->OMGetBlendState( &oldState, oldFactor, reinterpret_cast<UINT*>( &oldMask ) );

		return SlimDX::Direct3D10::BlendState::FromPointer( oldState );
	}

	void OutputMergerWrapper::BlendFactor::set( Color4 value )
	{
		ID3D10BlendState* oldState = 0;
		float oldFactor[4];
		int oldMask = 0;
		m_Device->OMGetBlendState( &oldState, oldFactor, reinterpret_cast<UINT*>( &oldMask ) );

		float newFactor[4] = { value.Red, value.Green, value.Blue, value.Alpha };
		m_Device->OMSetBlendState( oldState, newFactor, oldMask );
		if( oldState != NULL )
			oldState->Release();
	}

	Color4 OutputMergerWrapper::BlendFactor::get()
	{
		ID3D10BlendState* oldState = 0;
		float oldFactor[4];
		int oldMask = 0;
		m_Device->OMGetBlendState( &oldState, oldFactor, reinterpret_cast<UINT*>( &oldMask ) );
		if( oldState != NULL )
			oldState->Release();

		return Color4( oldFactor[3], oldFactor[0], oldFactor[1], oldFactor[2] );
	}

	void OutputMergerWrapper::BlendSampleMask::set( int value )
	{
		ID3D10BlendState* oldState = 0;
		float oldFactor[4];
		int oldMask = 0;
		m_Device->OMGetBlendState( &oldState, oldFactor, reinterpret_cast<UINT*>( &oldMask ) );

		m_Device->OMSetBlendState( oldState, oldFactor, value );
		if( oldState != NULL )
			oldState->Release();
	}

	int OutputMergerWrapper::BlendSampleMask::get()
	{
		ID3D10BlendState* oldState = 0;
		float oldFactor[4];
		int oldMask = 0;
		m_Device->OMGetBlendState( &oldState, oldFactor, reinterpret_cast<UINT*>( &oldMask ) );
		if( oldState != NULL )
			oldState->Release();

		return oldMask;
	}

	void OutputMergerWrapper::SetTargets( RenderTargetView^ renderTargetView )
	{
		SetTargets( nullptr, renderTargetView );
	}

	void OutputMergerWrapper::SetTargets( DepthStencilView^ depthStencilView, RenderTargetView^ renderTargetView )
	{
		ID3D10DepthStencilView *nativeDSV = depthStencilView == nullptr ? 0 : static_cast<ID3D10DepthStencilView*>( depthStencilView->InternalPointer );
		ID3D10RenderTargetView *nativeRTV[] = { renderTargetView == nullptr ? 0 : static_cast<ID3D10RenderTargetView*>( renderTargetView->InternalPointer ) };

		m_Device->OMSetRenderTargets( 1, nativeRTV, nativeDSV );
	}

	void OutputMergerWrapper::SetTargets( ... array<RenderTargetView^>^ renderTargets )
	{
		SetTargets( nullptr, renderTargets );
	}

	void OutputMergerWrapper::SetTargets( DepthStencilView^ depthStencilView, ... array<RenderTargetView^>^ renderTargets )
	{
		ID3D10DepthStencilView *nativeDSV = depthStencilView == nullptr ? 0 : static_cast<ID3D10DepthStencilView*>( depthStencilView->InternalPointer );
		ID3D10RenderTargetView* nativeRTVs[D3D10_SIMULTANEOUS_RENDER_TARGET_COUNT];

		if( renderTargets == nullptr )
		{
			m_Device->OMSetRenderTargets( 0, 0, nativeDSV );
		}
		else
		{
			for( int i = 0; i < renderTargets->Length; ++i )
				nativeRTVs[ i ] = renderTargets[ i ] == nullptr ? 0 : static_cast<ID3D10RenderTargetView*>( renderTargets[ i ]->InternalPointer );
			m_Device->OMSetRenderTargets( renderTargets->Length, nativeRTVs, nativeDSV );
		}
	}

	DepthStencilView^ OutputMergerWrapper::GetDepthStencilView()
	{
		ID3D10DepthStencilView *view;

		m_Device->OMGetRenderTargets( 0, 0, &view );
		return DepthStencilView::FromPointer( view );
	}

	array<RenderTargetView^>^ OutputMergerWrapper::GetRenderTargets( int count )
	{
		stack_array<ID3D10RenderTargetView*> targets = stackalloc( ID3D10RenderTargetView*, count );
		array<RenderTargetView^>^ results = gcnew array<RenderTargetView^>( count );

		m_Device->OMGetRenderTargets( count, &targets[0], 0 );

		for( int i = 0; i < count; i++ )
			results[i] = RenderTargetView::FromPointer( targets[i] );

		return results;
	}
}
}
<|endoftext|>


<commit_before>//
//  poNodeContainer.cpp
//  BasicScene
//
//  Created by Stephen Varga on 3/22/14.
//
//

#include "poNodeContainer.h"

namespace po {
    NodeContainerRef NodeContainer::create(std::string name)
    {
        return std::shared_ptr<NodeContainer>(new NodeContainer(name));
    }

    NodeContainer::NodeContainer(std::string name)
    : Node(name)
    {
    }

    //------------------------------------------------------
    #pragma mark - Scene -

    void NodeContainer::setScene(SceneRef scene)
    {
        Node::setScene(scene);
        for(NodeRef &childNode : mChildren) {
            childNode->setScene(scene);
        }
    }

    void NodeContainer::removeScene()
    {
        Node::removeScene();
        for(NodeRef &childNode : mChildren) {
            childNode->removeScene();
        }
    }

    //------------------------------------------------------
    #pragma mark - Add Children -

    void NodeContainer::addChild(NodeRef node)
    {
        setParentAndScene(node);
        mChildren.push_back(node);
    }

    void NodeContainer::addChildAt(int index, NodeRef node)
    {
        setParentAndScene(node);
        mChildren.insert(mChildren.begin()+index, node);
    }

    void NodeContainer::addChildBefore(NodeRef before, NodeRef node)
    {
        setParentAndScene(node);
        mChildren.insert(mChildren.begin() + getChildIndex(before), node);
    }

    void NodeContainer::addChildAfter(NodeRef after, NodeRef node)
    {
        setParentAndScene(node);
        mChildren.insert(mChildren.begin() + getChildIndex(after)+1, node);
    }

    //------------------------------------------------------
    #pragma mark - Get Children -

    std::vector<NodeRef> NodeContainer::getChildren()
    {
        return mChildren;
    };

    std::vector<NodeRef>& NodeContainer::getChildrenByReference()
    {
        return mChildren;
    }

    int NodeContainer::getChildIndex(const NodeRef& child)
    {
        std::vector<NodeRef>::iterator iter = std::find(mChildren.begin(), mChildren.end(), child);
        if(iter != mChildren.end())
            return (int)std::distance(mChildren.begin(), iter);
        return INVALID_INDEX;
    }

    NodeRef NodeContainer::getChildByIndex(int index)
    {
        if(index < 0 || index >= mChildren.size())
            return NodeRef();
        return *(mChildren.begin() + index);
    }

    NodeRef NodeContainer::getChildByUID(uint32_t uid)
    {
        //Go through our tree to find any node with UID
        for(NodeRef& node : mChildren) {
            NodeContainerRef container = std::dynamic_pointer_cast<NodeContainer>(node);
            if(container) {
                NodeRef foundNode = container->getChildByUID(uid);
                if(foundNode) return foundNode;
            }

            else {
                if(node->getUID() == uid) return node;
            }
        }

        //See if it is us
        if(mUid == uid) return shared_from_this();

        //Not found
        return NodeRef();
    }

    NodeRef NodeContainer::getChildByName(const std::string &name)
    {
        for(NodeRef& node : mChildren) {
            if(node->getName() == name) return node;
        }

        return NodeRef();
    }

    NodeRef NodeContainer::getFirstChild()
    {
        if(mChildren.empty())
            return NodeRef();

        return mChildren.front();
    }

    NodeRef NodeContainer::getLastChild()
    {
        if(mChildren.empty())
            return NodeRef();

        return mChildren.back();
    }

    //------------------------------------------------------
    #pragma mark - Remove Children -

    bool NodeContainer::removeChild(NodeRef node)
    {
        std::vector<NodeRef>::iterator iter = std::find(mChildren.begin(), mChildren.end(), node);
        if(iter != mChildren.end()) {
            (*iter)->removeParent();
            (*iter)->removeScene();

            #pragma message "This is not safe in recursion..."
            mChildren.erase(iter);

            return true;
        }

        return false;
    }

    bool NodeContainer::removeChildAt(int index)
    {
        if(index <= 0 || index >= mChildren.size())
            return false;

        mChildren[index]->removeParent();
        mChildren[index]->removeScene();

        mChildren.erase(mChildren.begin() + index);

        return true;
    }

    void NodeContainer::removeAllChildren()
    {
        for(NodeRef& node : mChildren) {
            node->removeParent();
            node->removeScene();
        }

        mChildren.clear();
    }

    //------------------------------------------------------
    #pragma mark - Move Children -

    void NodeContainer::moveChildToFront(NodeRef& node)
    {
        if(removeChild(node))
            addChild(node);
    }

    void NodeContainer::moveChildForward(NodeRef& node)
    {
        int idx = getChildIndex(node);
        if(removeChild(node))
            #pragma message "Does this work with a vector, or would this be out of bounds?"
            addChildAt(std::min(idx+1, getNumChildren()), node);
    }

    void NodeContainer::moveChildToBack(NodeRef& node)
    {
        if(removeChild(node))
            addChildAt(0, node);
    }

    void NodeContainer::moveChildBackward(NodeRef& node)
    {
        int idx = getChildIndex(node);
        if(removeChild(node))
            addChildAt(std::max(idx-1, 0), node);
    }

    void NodeContainer::setParentAndScene(NodeRef node)
    {
        //See if the node is already a child of another node.
        if(node->getParent())
            node->getParent()->removeChild(node);

        //Assign ourselves as the parent
        node->setParent(std::dynamic_pointer_cast<NodeContainer>(shared_from_this()));
        node->setScene(mScene.lock());
    }

    //------------------------------------------------------
    #pragma mark - Update and Draw Trees -

    void NodeContainer::updateTree()
    {
        update();
        for(NodeRef &childNode : mChildren)
            childNode->updateTree();
    }

    void NodeContainer::drawTree()
    {
        #pragma message "Need to implement matrix order"
        ci::gl::pushMatrices();
        setTransformation();

        draw();
        for(NodeRef &childNode : mChildren) {
            childNode->drawTree();

            #pragma message "For testing, should be removed"
            ci::gl::color(0,255,0);
            ci::gl::drawStrokedRect(childNode->getFrame());
        }

        if(mDrawBounds)
            drawBounds();

        ci::gl::popMatrices();
    }

    //------------------------------------------------------
    #pragma mark - Dimensions -

    ci::Rectf NodeContainer::getBounds()
    {
        //Reset Bounds
        ci::Rectf bounds = ci::Rectf(0,0,0,0);

        for(NodeRef &childNode : mChildren)
            bounds.include(childNode->getFrame());

        return bounds;
    }

    bool NodeContainer::pointInside(const ci::Vec2f &point)
    {
        for (NodeRef node : mChildren) {
            if (node->pointInside(point)) {
                return true;
            }
        }

        return false;
    }
}<commit_msg>Checking objects for visibility before including frame in bounds<commit_after>//
//  poNodeContainer.cpp
//  BasicScene
//
//  Created by Stephen Varga on 3/22/14.
//
//

#include "poNodeContainer.h"

namespace po {
    NodeContainerRef NodeContainer::create(std::string name)
    {
        return std::shared_ptr<NodeContainer>(new NodeContainer(name));
    }

    NodeContainer::NodeContainer(std::string name)
    : Node(name)
    {
    }

    //------------------------------------------------------
    #pragma mark - Scene -

    void NodeContainer::setScene(SceneRef scene)
    {
        Node::setScene(scene);
        for(NodeRef &childNode : mChildren) {
            childNode->setScene(scene);
        }
    }

    void NodeContainer::removeScene()
    {
        Node::removeScene();
        for(NodeRef &childNode : mChildren) {
            childNode->removeScene();
        }
    }

    //------------------------------------------------------
    #pragma mark - Add Children -

    void NodeContainer::addChild(NodeRef node)
    {
        setParentAndScene(node);
        mChildren.push_back(node);
    }

    void NodeContainer::addChildAt(int index, NodeRef node)
    {
        setParentAndScene(node);
        mChildren.insert(mChildren.begin()+index, node);
    }

    void NodeContainer::addChildBefore(NodeRef before, NodeRef node)
    {
        setParentAndScene(node);
        mChildren.insert(mChildren.begin() + getChildIndex(before), node);
    }

    void NodeContainer::addChildAfter(NodeRef after, NodeRef node)
    {
        setParentAndScene(node);
        mChildren.insert(mChildren.begin() + getChildIndex(after)+1, node);
    }

    //------------------------------------------------------
    #pragma mark - Get Children -

    std::vector<NodeRef> NodeContainer::getChildren()
    {
        return mChildren;
    };

    std::vector<NodeRef>& NodeContainer::getChildrenByReference()
    {
        return mChildren;
    }

    int NodeContainer::getChildIndex(const NodeRef& child)
    {
        std::vector<NodeRef>::iterator iter = std::find(mChildren.begin(), mChildren.end(), child);
        if(iter != mChildren.end())
            return (int)std::distance(mChildren.begin(), iter);
        return INVALID_INDEX;
    }

    NodeRef NodeContainer::getChildByIndex(int index)
    {
        if(index < 0 || index >= mChildren.size())
            return NodeRef();
        return *(mChildren.begin() + index);
    }

    NodeRef NodeContainer::getChildByUID(uint32_t uid)
    {
        //Go through our tree to find any node with UID
        for(NodeRef& node : mChildren) {
            NodeContainerRef container = std::dynamic_pointer_cast<NodeContainer>(node);
            if(container) {
                NodeRef foundNode = container->getChildByUID(uid);
                if(foundNode) return foundNode;
            }

            else {
                if(node->getUID() == uid) return node;
            }
        }

        //See if it is us
        if(mUid == uid) return shared_from_this();

        //Not found
        return NodeRef();
    }

    NodeRef NodeContainer::getChildByName(const std::string &name)
    {
        for(NodeRef& node : mChildren) {
            if(node->getName() == name) return node;
        }

        return NodeRef();
    }

    NodeRef NodeContainer::getFirstChild()
    {
        if(mChildren.empty())
            return NodeRef();

        return mChildren.front();
    }

    NodeRef NodeContainer::getLastChild()
    {
        if(mChildren.empty())
            return NodeRef();

        return mChildren.back();
    }

    //------------------------------------------------------
    #pragma mark - Remove Children -

    bool NodeContainer::removeChild(NodeRef node)
    {
        std::vector<NodeRef>::iterator iter = std::find(mChildren.begin(), mChildren.end(), node);
        if(iter != mChildren.end()) {
            (*iter)->removeParent();
            (*iter)->removeScene();

            #pragma message "This is not safe in recursion..."
            mChildren.erase(iter);

            return true;
        }

        return false;
    }

    bool NodeContainer::removeChildAt(int index)
    {
        if(index <= 0 || index >= mChildren.size())
            return false;

        mChildren[index]->removeParent();
        mChildren[index]->removeScene();

        mChildren.erase(mChildren.begin() + index);

        return true;
    }

    void NodeContainer::removeAllChildren()
    {
        for(NodeRef& node : mChildren) {
            node->removeParent();
            node->removeScene();
        }

        mChildren.clear();
    }

    //------------------------------------------------------
    #pragma mark - Move Children -

    void NodeContainer::moveChildToFront(NodeRef& node)
    {
        if(removeChild(node))
            addChild(node);
    }

    void NodeContainer::moveChildForward(NodeRef& node)
    {
        int idx = getChildIndex(node);
        if(removeChild(node))
            #pragma message "Does this work with a vector, or would this be out of bounds?"
            addChildAt(std::min(idx+1, getNumChildren()), node);
    }

    void NodeContainer::moveChildToBack(NodeRef& node)
    {
        if(removeChild(node))
            addChildAt(0, node);
    }

    void NodeContainer::moveChildBackward(NodeRef& node)
    {
        int idx = getChildIndex(node);
        if(removeChild(node))
            addChildAt(std::max(idx-1, 0), node);
    }

    void NodeContainer::setParentAndScene(NodeRef node)
    {
        //See if the node is already a child of another node.
        if(node->getParent())
            node->getParent()->removeChild(node);

        //Assign ourselves as the parent
        node->setParent(std::dynamic_pointer_cast<NodeContainer>(shared_from_this()));
        node->setScene(mScene.lock());
    }

    //------------------------------------------------------
    #pragma mark - Update and Draw Trees -

    void NodeContainer::updateTree()
    {
        update();
        for(NodeRef &childNode : mChildren)
            childNode->updateTree();
    }

    void NodeContainer::drawTree()
    {
        #pragma message "Need to implement matrix order"
        ci::gl::pushMatrices();
        setTransformation();

        draw();
        for(NodeRef &childNode : mChildren) {
            childNode->drawTree();

            #pragma message "For testing, should be removed"
            //ci::gl::color(0,255,0);
            //ci::gl::drawStrokedRect(childNode->getFrame());
        }

        if(mDrawBounds)
            drawBounds();

        ci::gl::popMatrices();
    }

    //------------------------------------------------------
    #pragma mark - Dimensions -

    ci::Rectf NodeContainer::getBounds()
    {
        //Reset Bounds
        ci::Rectf bounds = ci::Rectf(0,0,0,0);

        for(NodeRef &childNode : mChildren) {
			if(childNode->isVisible())
				bounds.include(childNode->getFrame());
		}

        return bounds;
    }

    bool NodeContainer::pointInside(const ci::Vec2f &point)
    {
        for (NodeRef node : mChildren) {
            if (node->pointInside(point)) {
                return true;
            }
        }

        return false;
    }
}<|endoftext|>