<commit_before>// Copyright (c) 2013-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "consensus/validation.h"
#include "data/sighash.json.h"
#include "hash.h"
#include "main.h" // For CheckTransaction
#include "random.h"
#include "script/interpreter.h"
#include "script/script.h"
#include "serialize.h"
#include "streams.h"
#include "test/test_bitcoin.h"
#include "util.h"
#include "utilstrencodings.h"
#include "version.h"

#include <iostream>

#include <boost/test/unit_test.hpp>

#include <univalue.h>

extern UniValue read_json(const std::string& jsondata);

// Old script.cpp SignatureHash function
uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType)
{
    static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
    if (nIn >= txTo.vin.size())
    {
        printf("ERROR: SignatureHash(): nIn=%d out of range\n", nIn);
        return one;
    }
    CMutableTransaction txTmp(txTo);

    // In case concatenating two scripts ends up with two codeseparators,
    // or an extra one at the end, this prevents all those possible incompatibilities.
    scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR));

    // Blank out other inputs' signatures
    for (unsigned int i = 0; i < txTmp.vin.size(); i++)
        txTmp.vin[i].scriptSig = CScript();
    txTmp.vin[nIn].scriptSig = scriptCode;

    // Blank out some of the outputs
    if ((nHashType & 0x1f) == SIGHASH_NONE)
    {
        // Wildcard payee
        txTmp.vout.clear();

        // Let the others update at will
        for (unsigned int i = 0; i < txTmp.vin.size(); i++)
            if (i != nIn)
                txTmp.vin[i].nSequence = 0;
    }
    else if ((nHashType & 0x1f) == SIGHASH_SINGLE)
    {
        // Only lock-in the txout payee at same index as txin
        unsigned int nOut = nIn;
        if (nOut >= txTmp.vout.size())
        {
            printf("ERROR: SignatureHash(): nOut=%d out of range\n", nOut);
            return one;
        }
        txTmp.vout.resize(nOut+1);
        for (unsigned int i = 0; i < nOut; i++)
            txTmp.vout[i].SetNull();

        // Let the others update at will
        for (unsigned int i = 0; i < txTmp.vin.size(); i++)
            if (i != nIn)
                txTmp.vin[i].nSequence = 0;
    }

    // Blank out other inputs completely, not recommended for open transactions
    if (nHashType & SIGHASH_ANYONECANPAY)
    {
        txTmp.vin[0] = txTmp.vin[nIn];
        txTmp.vin.resize(1);
    }

    // Serialize and hash
    CHashWriter ss(SER_GETHASH, 0);
    ss << txTmp << nHashType;
    return ss.GetHash();
}

void static RandomScript(CScript &script) {
    static const opcodetype oplist[] = {OP_FALSE, OP_1, OP_2, OP_3, OP_CHECKSIG, OP_IF, OP_VERIF, OP_RETURN, OP_CODESEPARATOR};
    script = CScript();
    int ops = (insecure_rand() % 10);
    for (int i=0; i<ops; i++)
        script << oplist[insecure_rand() % (sizeof(oplist)/sizeof(oplist[0]))];
}

void static RandomTransaction(CMutableTransaction &tx, bool fSingle) {
    tx.nVersion = insecure_rand();
    tx.vin.clear();
    tx.vout.clear();
    tx.nLockTime = (insecure_rand() % 2) ? insecure_rand() : 0;
    int ins = (insecure_rand() % 4) + 1;
    int outs = fSingle ? ins : (insecure_rand() % 4) + 1;
    for (int in = 0; in < ins; in++) {
        tx.vin.push_back(CTxIn());
        CTxIn &txin = tx.vin.back();
        txin.prevout.hash = GetRandHash();
        txin.prevout.n = insecure_rand() % 4;
        RandomScript(txin.scriptSig);
        txin.nSequence = (insecure_rand() % 2) ? insecure_rand() : (unsigned int)-1;
    }
    for (int out = 0; out < outs; out++) {
        tx.vout.push_back(CTxOut());
        CTxOut &txout = tx.vout.back();
        txout.nValue = insecure_rand() % 100000000;
        RandomScript(txout.scriptPubKey);
    }
}

BOOST_FIXTURE_TEST_SUITE(sighash_tests, BasicTestingSetup)

BOOST_AUTO_TEST_CASE(sighash_test)
{
    seed_insecure_rand(false);

    #if defined(PRINT_SIGHASH_JSON)
    std::cout << "[\n";
    std::cout << "\t[\"raw_transaction, script, input_index, hashType, signature_hash (result)\"],\n";
    #endif
    int nRandomTests = 50000;

    #if defined(PRINT_SIGHASH_JSON)
    nRandomTests = 500;
    #endif
    for (int i=0; i<nRandomTests; i++) {
        int nHashType = insecure_rand();
        CMutableTransaction txTo;
        RandomTransaction(txTo, (nHashType & 0x1f) == SIGHASH_SINGLE);
        CScript scriptCode;
        RandomScript(scriptCode);
        int nIn = insecure_rand() % txTo.vin.size();

        uint256 sh, sho;
        sho = SignatureHashOld(scriptCode, txTo, nIn, nHashType);
        sh = SignatureHash(scriptCode, txTo, nIn, nHashType);
        #if defined(PRINT_SIGHASH_JSON)
        CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
        ss << txTo;

        std::cout << "\t[\"" ;
        std::cout << HexStr(ss.begin(), ss.end()) << "\", \"";
        std::cout << HexStr(scriptCode) << "\", ";
        std::cout << nIn << ", ";
        std::cout << nHashType << ", \"";
        std::cout << sho.GetHex() << "\"]";
        if (i+1 != nRandomTests) {
          std::cout << ",";
        }
        std::cout << "\n";
        #endif
        BOOST_CHECK(sh == sho);
    }
    #if defined(PRINT_SIGHASH_JSON)
    std::cout << "]\n";
    #endif
}

// Goal: check that SignatureHash generates correct hash
BOOST_AUTO_TEST_CASE(sighash_from_data)
{
    UniValue tests = read_json(std::string(json_tests::sighash, json_tests::sighash + sizeof(json_tests::sighash)));

    for (unsigned int idx = 0; idx < tests.size(); idx++) {
        UniValue test = tests[idx];
        std::string strTest = test.write();
        if (test.size() < 1) // Allow for extra stuff (useful for comments)
        {
            BOOST_ERROR("Bad test: " << strTest);
            continue;
        }
        if (test.size() == 1) continue; // comment

        std::string raw_tx, raw_script, sigHashHex;
        int nIn, nHashType;
        uint256 sh;
        CTransaction tx;
        CScript scriptCode = CScript();

        try {
          // deserialize test data
          raw_tx = test[0].get_str();
          raw_script = test[1].get_str();
          nIn = test[2].get_int();
          nHashType = test[3].get_int();
          sigHashHex = test[4].get_str();

          uint256 sh;
          CDataStream stream(ParseHex(raw_tx), SER_NETWORK, PROTOCOL_VERSION);
          stream >> tx;

          CValidationState state;
          BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest);
          BOOST_CHECK(state.IsValid());

          std::vector<unsigned char> raw = ParseHex(raw_script);
          scriptCode.insert(scriptCode.end(), raw.begin(), raw.end());
        } catch (...) {
          BOOST_ERROR("Bad test, couldn't deserialize data: " << strTest);
          continue;
        }

        sh = SignatureHash(scriptCode, tx, nIn, nHashType);
        BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest);
    }
}
BOOST_AUTO_TEST_SUITE_END()
<commit_msg>Remove unused local variable shadowing upper local<commit_after>// Copyright (c) 2013-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "consensus/validation.h"
#include "data/sighash.json.h"
#include "hash.h"
#include "main.h" // For CheckTransaction
#include "random.h"
#include "script/interpreter.h"
#include "script/script.h"
#include "serialize.h"
#include "streams.h"
#include "test/test_bitcoin.h"
#include "util.h"
#include "utilstrencodings.h"
#include "version.h"

#include <iostream>

#include <boost/test/unit_test.hpp>

#include <univalue.h>

extern UniValue read_json(const std::string& jsondata);

// Old script.cpp SignatureHash function
uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType)
{
    static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
    if (nIn >= txTo.vin.size())
    {
        printf("ERROR: SignatureHash(): nIn=%d out of range\n", nIn);
        return one;
    }
    CMutableTransaction txTmp(txTo);

    // In case concatenating two scripts ends up with two codeseparators,
    // or an extra one at the end, this prevents all those possible incompatibilities.
    scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR));

    // Blank out other inputs' signatures
    for (unsigned int i = 0; i < txTmp.vin.size(); i++)
        txTmp.vin[i].scriptSig = CScript();
    txTmp.vin[nIn].scriptSig = scriptCode;

    // Blank out some of the outputs
    if ((nHashType & 0x1f) == SIGHASH_NONE)
    {
        // Wildcard payee
        txTmp.vout.clear();

        // Let the others update at will
        for (unsigned int i = 0; i < txTmp.vin.size(); i++)
            if (i != nIn)
                txTmp.vin[i].nSequence = 0;
    }
    else if ((nHashType & 0x1f) == SIGHASH_SINGLE)
    {
        // Only lock-in the txout payee at same index as txin
        unsigned int nOut = nIn;
        if (nOut >= txTmp.vout.size())
        {
            printf("ERROR: SignatureHash(): nOut=%d out of range\n", nOut);
            return one;
        }
        txTmp.vout.resize(nOut+1);
        for (unsigned int i = 0; i < nOut; i++)
            txTmp.vout[i].SetNull();

        // Let the others update at will
        for (unsigned int i = 0; i < txTmp.vin.size(); i++)
            if (i != nIn)
                txTmp.vin[i].nSequence = 0;
    }

    // Blank out other inputs completely, not recommended for open transactions
    if (nHashType & SIGHASH_ANYONECANPAY)
    {
        txTmp.vin[0] = txTmp.vin[nIn];
        txTmp.vin.resize(1);
    }

    // Serialize and hash
    CHashWriter ss(SER_GETHASH, 0);
    ss << txTmp << nHashType;
    return ss.GetHash();
}

void static RandomScript(CScript &script) {
    static const opcodetype oplist[] = {OP_FALSE, OP_1, OP_2, OP_3, OP_CHECKSIG, OP_IF, OP_VERIF, OP_RETURN, OP_CODESEPARATOR};
    script = CScript();
    int ops = (insecure_rand() % 10);
    for (int i=0; i<ops; i++)
        script << oplist[insecure_rand() % (sizeof(oplist)/sizeof(oplist[0]))];
}

void static RandomTransaction(CMutableTransaction &tx, bool fSingle) {
    tx.nVersion = insecure_rand();
    tx.vin.clear();
    tx.vout.clear();
    tx.nLockTime = (insecure_rand() % 2) ? insecure_rand() : 0;
    int ins = (insecure_rand() % 4) + 1;
    int outs = fSingle ? ins : (insecure_rand() % 4) + 1;
    for (int in = 0; in < ins; in++) {
        tx.vin.push_back(CTxIn());
        CTxIn &txin = tx.vin.back();
        txin.prevout.hash = GetRandHash();
        txin.prevout.n = insecure_rand() % 4;
        RandomScript(txin.scriptSig);
        txin.nSequence = (insecure_rand() % 2) ? insecure_rand() : (unsigned int)-1;
    }
    for (int out = 0; out < outs; out++) {
        tx.vout.push_back(CTxOut());
        CTxOut &txout = tx.vout.back();
        txout.nValue = insecure_rand() % 100000000;
        RandomScript(txout.scriptPubKey);
    }
}

BOOST_FIXTURE_TEST_SUITE(sighash_tests, BasicTestingSetup)

BOOST_AUTO_TEST_CASE(sighash_test)
{
    seed_insecure_rand(false);

    #if defined(PRINT_SIGHASH_JSON)
    std::cout << "[\n";
    std::cout << "\t[\"raw_transaction, script, input_index, hashType, signature_hash (result)\"],\n";
    #endif
    int nRandomTests = 50000;

    #if defined(PRINT_SIGHASH_JSON)
    nRandomTests = 500;
    #endif
    for (int i=0; i<nRandomTests; i++) {
        int nHashType = insecure_rand();
        CMutableTransaction txTo;
        RandomTransaction(txTo, (nHashType & 0x1f) == SIGHASH_SINGLE);
        CScript scriptCode;
        RandomScript(scriptCode);
        int nIn = insecure_rand() % txTo.vin.size();

        uint256 sh, sho;
        sho = SignatureHashOld(scriptCode, txTo, nIn, nHashType);
        sh = SignatureHash(scriptCode, txTo, nIn, nHashType);
        #if defined(PRINT_SIGHASH_JSON)
        CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
        ss << txTo;

        std::cout << "\t[\"" ;
        std::cout << HexStr(ss.begin(), ss.end()) << "\", \"";
        std::cout << HexStr(scriptCode) << "\", ";
        std::cout << nIn << ", ";
        std::cout << nHashType << ", \"";
        std::cout << sho.GetHex() << "\"]";
        if (i+1 != nRandomTests) {
          std::cout << ",";
        }
        std::cout << "\n";
        #endif
        BOOST_CHECK(sh == sho);
    }
    #if defined(PRINT_SIGHASH_JSON)
    std::cout << "]\n";
    #endif
}

// Goal: check that SignatureHash generates correct hash
BOOST_AUTO_TEST_CASE(sighash_from_data)
{
    UniValue tests = read_json(std::string(json_tests::sighash, json_tests::sighash + sizeof(json_tests::sighash)));

    for (unsigned int idx = 0; idx < tests.size(); idx++) {
        UniValue test = tests[idx];
        std::string strTest = test.write();
        if (test.size() < 1) // Allow for extra stuff (useful for comments)
        {
            BOOST_ERROR("Bad test: " << strTest);
            continue;
        }
        if (test.size() == 1) continue; // comment

        std::string raw_tx, raw_script, sigHashHex;
        int nIn, nHashType;
        uint256 sh;
        CTransaction tx;
        CScript scriptCode = CScript();

        try {
          // deserialize test data
          raw_tx = test[0].get_str();
          raw_script = test[1].get_str();
          nIn = test[2].get_int();
          nHashType = test[3].get_int();
          sigHashHex = test[4].get_str();

          CDataStream stream(ParseHex(raw_tx), SER_NETWORK, PROTOCOL_VERSION);
          stream >> tx;

          CValidationState state;
          BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest);
          BOOST_CHECK(state.IsValid());

          std::vector<unsigned char> raw = ParseHex(raw_script);
          scriptCode.insert(scriptCode.end(), raw.begin(), raw.end());
        } catch (...) {
          BOOST_ERROR("Bad test, couldn't deserialize data: " << strTest);
          continue;
        }

        sh = SignatureHash(scriptCode, tx, nIn, nHashType);
        BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest);
    }
}
BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>/*****************************************************************************
 * main.cpp: ActiveX control for VLC
 *****************************************************************************
 * Copyright (C) 2005 the VideoLAN team
 *
 * Authors: Damien Fouilleul <Damien.Fouilleul@laposte.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include "plugin.h"
#include "utils.h"

#include <stdio.h>

#include <comcat.h>
#include <windows.h>
#include <shlwapi.h>

using namespace std;

#define COMPANY_STR "VideoLAN"
#define PROGRAM_STR "VLCPlugin"
#define DESCRIPTION "VideoLAN VLC ActiveX Plugin"

#define THREADING_MODEL "Apartment"
#define MISC_STATUS     "131473"

#define PROGID_STR COMPANY_STR"."PROGRAM_STR

#define GUID_STRLEN 39

/*
** MingW headers do not declare those
*/
extern const CATID CATID_SafeForInitializing;
extern const CATID CATID_SafeForScripting;

static LONG i_class_ref= 0;
static HINSTANCE h_instance= 0;

HMODULE DllGetModule()
{
    return h_instance;
};

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{
    HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;

    *ppv = NULL;

    if( (CLSID_VLCPlugin == rclsid )
     || ( CLSID_VLCPlugin2 == rclsid) )
    {
        VLCPluginClass *plugin = new VLCPluginClass(&i_class_ref, h_instance, rclsid);
        hr = plugin->QueryInterface(riid, ppv);
        plugin->Release();
    }
    return hr;
};

STDAPI DllCanUnloadNow(VOID)
{
    return (0 == i_class_ref) ? S_OK: S_FALSE;
};

static inline HKEY keyCreate(HKEY parentKey, LPCSTR keyName)
{
    HKEY childKey;
    if( ERROR_SUCCESS == RegCreateKeyExA(parentKey, keyName, 0, NULL,
                REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &childKey, NULL) )
    {
        return childKey;
    }
    return NULL;
};

static inline HKEY keySet(HKEY hKey, LPCSTR valueName, const void *s, size_t len)
{
    if( NULL != hKey )
    {
        RegSetValueExA(hKey, valueName, 0, REG_SZ,
            (const BYTE*)s, len);
    }
    return hKey;
};

static inline HKEY keySetDef(HKEY hKey, const void *s, size_t len)
{
    return keySet(hKey, NULL, s, len);
};

static inline HKEY keySetDef(HKEY hKey, LPCSTR s)
{
    return keySetDef(hKey, s, strlen(s)+1);
};

static inline HKEY keyClose(HKEY hKey)
{
    if( NULL != hKey )
    {
        RegCloseKey(hKey);
    }
    return NULL;
};

static HRESULT UnregisterProgID(REFCLSID rclsid, unsigned int version)
{
    LPCSTR psz_CLSID = CStrFromGUID(rclsid);

    if( NULL == psz_CLSID )
        return E_OUTOFMEMORY;

    char progId[sizeof(PROGID_STR)+16];
    sprintf(progId, "%s.%u", PROGID_STR, version);

    SHDeleteKeyA(HKEY_CLASSES_ROOT, progId);

    HKEY hClsIDKey;
    if( ERROR_SUCCESS == RegOpenKeyExA(HKEY_CLASSES_ROOT, "CLSID", 0, KEY_WRITE, &hClsIDKey) )
    {
        SHDeleteKey(hClsIDKey, psz_CLSID);
        RegCloseKey(hClsIDKey);
    }
    CoTaskMemFree((void *)psz_CLSID);
};

STDAPI DllUnregisterServer(VOID)
{
    // unregister type lib from the registry
    UnRegisterTypeLib(LIBID_AXVLC, 1, 0, LOCALE_NEUTRAL, SYS_WIN32);

    // remove component categories we supports
    ICatRegister *pcr;
    if( SUCCEEDED(CoCreateInstance(CLSID_StdComponentCategoriesMgr,
            NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr)) ) {
        CATID implCategories[] = {
            CATID_Control,
            CATID_PersistsToPropertyBag,
            CATID_SafeForInitializing,
            CATID_SafeForScripting,
        };

        pcr->UnRegisterClassImplCategories(CLSID_VLCPlugin,
                sizeof(implCategories)/sizeof(CATID), implCategories);
        pcr->UnRegisterClassImplCategories(CLSID_VLCPlugin2,
                sizeof(implCategories)/sizeof(CATID), implCategories);
        pcr->Release();
    }

    SHDeleteKey(HKEY_CLASSES_ROOT, TEXT(PROGID_STR));

    UnregisterProgID(CLSID_VLCPlugin, 2);
    UnregisterProgID(CLSID_VLCPlugin2, 1);

    return S_OK;
};

static HRESULT RegisterClassID(HKEY hParent, REFCLSID rclsid, unsigned int version, BOOL isDefault, const char *path, size_t pathLen)
{
    char progId[sizeof(PROGID_STR)+16];
    sprintf(progId, "%s.%u", PROGID_STR, version);

    char description[sizeof(DESCRIPTION)+16];
    sprintf(description, "%s v%u", DESCRIPTION, version);

    HKEY hClassKey;
    {
        LPCSTR psz_CLSID = CStrFromGUID(rclsid);

        if( NULL == psz_CLSID )
            return E_OUTOFMEMORY;

        HKEY hProgKey = keyCreate(HKEY_CLASSES_ROOT, progId);
        if( NULL != hProgKey )
        {
            // default key value
            keySetDef(hProgKey, description);

            keyClose(keySetDef(keyCreate(hProgKey, "CLSID"),
                psz_CLSID,
                GUID_STRLEN));

            //hSubKey = keyClose(keyCreate(hBaseKey, "Insertable"));

            RegCloseKey(hProgKey);
        }
        if( isDefault )
        {
            hProgKey = keyCreate(HKEY_CLASSES_ROOT, PROGID_STR);
            if( NULL != hProgKey )
            {
                // default key value
                keySetDef(hProgKey, description);

                keyClose(keySetDef(keyCreate(hProgKey, "CLSID"),
                    psz_CLSID,
                    GUID_STRLEN));

                keyClose(keySetDef(keyCreate(hProgKey, "CurVer"),
                    progId));
            }
        }
        hClassKey = keyCreate(hParent, psz_CLSID);
        CoTaskMemFree((void *)psz_CLSID);
    }
    if( NULL != hClassKey )
    {
        // default key value
        keySetDef(hClassKey, description);

        // Control key value
        keyClose(keyCreate(hClassKey, "Control"));

        // Insertable key value
        //keyClose(keyCreate(hClassKey, "Insertable"));

        // ToolboxBitmap32 key value
        {
            char iconPath[pathLen+3];
            memcpy(iconPath, path, pathLen);
            strcpy(iconPath+pathLen, ",1");
            keyClose(keySetDef(keyCreate(hClassKey,
                "ToolboxBitmap32"),
                iconPath, sizeof(iconPath)));
        }

#ifdef BUILD_LOCALSERVER
        // LocalServer32 key value
        keyClose(keySetDef(keyCreate(hClassKey,
            "LocalServer32", path, pathLen+1)));
#else
        // InprocServer32 key value
        {
            HKEY hSubKey = keySetDef(keyCreate(hClassKey,
                "InprocServer32"),
                path, pathLen+1);
            keySet(hSubKey,
                "ThreadingModel",
                THREADING_MODEL, sizeof(THREADING_MODEL));
            keyClose(hSubKey);
        }
#endif

        // MiscStatus key value
        keyClose(keySetDef(keyCreate(hClassKey,
            "MiscStatus\\1"),
            MISC_STATUS, sizeof(MISC_STATUS)));

        // Programmable key value
        keyClose(keyCreate(hClassKey, "Programmable"));

        // ProgID key value
        keyClose(keySetDef(keyCreate(hClassKey,
            TEXT("ProgID")),
            progId));

        // VersionIndependentProgID key value
        keyClose(keySetDef(keyCreate(hClassKey,
            "VersionIndependentProgID"),
            PROGID_STR, sizeof(PROGID_STR)));

        // Version key value
        keyClose(keySetDef(keyCreate(hClassKey,
            "Version"),
            "1.0"));

        // TypeLib key value
        LPCSTR psz_LIBID = CStrFromGUID(LIBID_AXVLC);
        if( NULL != psz_LIBID )
        {
            keyClose(keySetDef(keyCreate(hClassKey,
                    "TypeLib"),
                    psz_LIBID, GUID_STRLEN));
            CoTaskMemFree((void *)psz_LIBID);
        }
        RegCloseKey(hClassKey);
    }
    return S_OK;
}

STDAPI DllRegisterServer(VOID)
{
    DllUnregisterServer();

    char DllPath[MAX_PATH];
    DWORD DllPathLen=GetModuleFileNameA(h_instance, DllPath, sizeof(DllPath)) ;
    if( 0 == DllPathLen )
        return E_UNEXPECTED;

    HKEY hBaseKey;

    if( ERROR_SUCCESS != RegOpenKeyExA(HKEY_CLASSES_ROOT, "CLSID", 0, KEY_CREATE_SUB_KEY, &hBaseKey) )
        return SELFREG_E_CLASS;

    RegisterClassID(hBaseKey, CLSID_VLCPlugin, 1, FALSE, DllPath, DllPathLen);
    RegisterClassID(hBaseKey, CLSID_VLCPlugin2, 2, TRUE, DllPath, DllPathLen);

    RegCloseKey(hBaseKey);

    // indicate which component categories we support
    ICatRegister *pcr;
    if( SUCCEEDED(CoCreateInstance(CLSID_StdComponentCategoriesMgr,
            NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr)) ) {
        CATID implCategories[] = {
            CATID_Control,
            CATID_PersistsToPropertyBag,
            CATID_SafeForInitializing,
            CATID_SafeForScripting,
        };

        pcr->RegisterClassImplCategories(CLSID_VLCPlugin,
                sizeof(implCategories)/sizeof(CATID), implCategories);
        pcr->RegisterClassImplCategories(CLSID_VLCPlugin2,
                sizeof(implCategories)/sizeof(CATID), implCategories);
        pcr->Release();
    }

    // register type lib into the registry
    ITypeLib *typeLib;

#ifdef BUILD_LOCALSERVER
    // replace .exe by .tlb
    strcpy(DllPath+DllPathLen-4, ".tlb");
#endif

#ifndef OLE2ANSI
    size_t typeLibPathLen = MultiByteToWideChar(CP_ACP, 0, DllPath, -1, NULL, 0);
    if( typeLibPathLen > 0 )
    {
        LPOLESTR typeLibPath = (LPOLESTR)CoTaskMemAlloc(typeLibPathLen*sizeof(wchar_t));
        MultiByteToWideChar(CP_ACP, 0, DllPath, DllPathLen, typeLibPath, typeLibPathLen);
        if( FAILED(LoadTypeLibEx(typeLibPath, REGKIND_REGISTER, &typeLib)) )
#ifndef BUILD_LOCALSERVER
            return SELFREG_E_TYPELIB;
        typeLib->Release();
#endif
        CoTaskMemFree((void *)typeLibPath);
    }
#else
    if( FAILED(LoadTypeLibEx((LPOLESTR)DllPath, REGKIND_REGISTER, &typeLib)) )
        return SELFREG_E_TYPELIB;
    typeLib->Release();
#endif

    return S_OK;
};

#ifdef BUILD_LOCALSERVER

/*
** easier to debug an application than a DLL on cygwin GDB :)
*/
#include <iostream>

STDAPI_(int) WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
{
    MSG msg;

    if( FAILED(OleInitialize(NULL)) )
    {
        cerr << "cannot initialize OLE" << endl;
        return 1;
    }

    h_instance = hInst;

    if( FAILED(DllRegisterServer()) )
    {
        cerr << "cannot register Local Server" << endl;
        return 1;
    }

    IUnknown *classProc = NULL;

    if( FAILED(DllGetClassObject(CLSID_VLCPlugin, IID_IUnknown, (LPVOID *)&classProc)) )
        return 0;

    DWORD dwRegisterClassObject;
    DWORD dwRegisterClassObject2;

    if( FAILED(CoRegisterClassObject(CLSID_VLCPlugin, classProc,
        CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &dwRegisterClassObject)) )
        return 0;

    DWORD dwRegisterActiveObject;

    if( FAILED(RegisterActiveObject(classProc, CLSID_VLCPlugin,
                    ACTIVEOBJECT_WEAK, &dwRegisterActiveObject)) )
        return 0;

    if( FAILED(RegisterActiveObject(classProc, CLSID_VLCPlugin2,
                    ACTIVEOBJECT_WEAK, &dwRegisterActiveObject2)) )
        return 0;

    classProc->Release();

    /*
    * Polling messages from event queue
    */
    while( S_FALSE == DllCanUnloadNow() )
    {
        while( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
        {
            if( msg.message == WM_QUIT )
                break;  // break out PeekMessage loop

            /*if(TranslateAccelerator(ghwndApp, ghAccel, &msg))
                continue;*/

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        if(msg.message == WM_QUIT)
            break;  // break out main loop

        WaitMessage();
    }

    if( SUCCEEDED(RevokeActiveObject(dwRegisterActiveObject, NULL)) )
        CoRevokeClassObject(dwRegisterClassObject);

    if( SUCCEEDED(RevokeActiveObject(dwRegisterActiveObject2, NULL)) )
        CoRevokeClassObject(dwRegisterClassObject2);

    // Reached on WM_QUIT message
    OleUninitialize();
    return ((int) msg.wParam);
};

#else

STDAPI_(BOOL) DllMain(HANDLE hModule, DWORD fdwReason, LPVOID lpReserved )
{
    switch( fdwReason )
    {
        case DLL_PROCESS_ATTACH:
            h_instance = (HINSTANCE)hModule;
            break;

        default:
            break;
    }
    return TRUE;
};

#endif

<commit_msg>- latest mingw headers removed some category definitions<commit_after>/*****************************************************************************
 * main.cpp: ActiveX control for VLC
 *****************************************************************************
 * Copyright (C) 2005 the VideoLAN team
 *
 * Authors: Damien Fouilleul <Damien.Fouilleul@laposte.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include "plugin.h"
#include "utils.h"

#include <stdio.h>

#include <comcat.h>
#include <windows.h>
#include <shlwapi.h>

#include <guiddef.h>

using namespace std;

#define COMPANY_STR "VideoLAN"
#define PROGRAM_STR "VLCPlugin"
#define DESCRIPTION "VideoLAN VLC ActiveX Plugin"

#define THREADING_MODEL "Apartment"
#define MISC_STATUS     "131473"

#define PROGID_STR COMPANY_STR"."PROGRAM_STR

#define GUID_STRLEN 39

/*
** MingW headers & libs do not declare those
*/
DEFINE_GUID(CATID_InternetAware,       0x0DE86A58, 0x2BAA, 0x11CF, 0xA2, 0x29, 0x00,0xAA,0x00,0x3D,0x73,0x52);
DEFINE_GUID(CATID_SafeForInitializing, 0x7DD95802, 0x9882, 0x11CF, 0x9F, 0xA9, 0x00,0xAA,0x00,0x6C,0x42,0xC4);
DEFINE_GUID(CATID_SafeForScripting,    0x7DD95801, 0x9882, 0x11CF, 0x9F, 0xA9, 0x00,0xAA,0x00,0x6C,0x42,0xC4);

static LONG i_class_ref= 0;
static HINSTANCE h_instance= 0;

HMODULE DllGetModule()
{
    return h_instance;
};

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{
    HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;

    *ppv = NULL;

    if( (CLSID_VLCPlugin == rclsid )
     || ( CLSID_VLCPlugin2 == rclsid) )
    {
        VLCPluginClass *plugin = new VLCPluginClass(&i_class_ref, h_instance, rclsid);
        hr = plugin->QueryInterface(riid, ppv);
        plugin->Release();
    }
    return hr;
};

STDAPI DllCanUnloadNow(VOID)
{
    return (0 == i_class_ref) ? S_OK: S_FALSE;
};

static inline HKEY keyCreate(HKEY parentKey, LPCSTR keyName)
{
    HKEY childKey;
    if( ERROR_SUCCESS == RegCreateKeyExA(parentKey, keyName, 0, NULL,
                REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &childKey, NULL) )
    {
        return childKey;
    }
    return NULL;
};

static inline HKEY keySet(HKEY hKey, LPCSTR valueName, const void *s, size_t len)
{
    if( NULL != hKey )
    {
        RegSetValueExA(hKey, valueName, 0, REG_SZ,
            (const BYTE*)s, len);
    }
    return hKey;
};

static inline HKEY keySetDef(HKEY hKey, const void *s, size_t len)
{
    return keySet(hKey, NULL, s, len);
};

static inline HKEY keySetDef(HKEY hKey, LPCSTR s)
{
    return keySetDef(hKey, s, strlen(s)+1);
};

static inline HKEY keyClose(HKEY hKey)
{
    if( NULL != hKey )
    {
        RegCloseKey(hKey);
    }
    return NULL;
};

static HRESULT UnregisterProgID(REFCLSID rclsid, unsigned int version)
{
    LPCSTR psz_CLSID = CStrFromGUID(rclsid);

    if( NULL == psz_CLSID )
        return E_OUTOFMEMORY;

    char progId[sizeof(PROGID_STR)+16];
    sprintf(progId, "%s.%u", PROGID_STR, version);

    SHDeleteKeyA(HKEY_CLASSES_ROOT, progId);

    HKEY hClsIDKey;
    if( ERROR_SUCCESS == RegOpenKeyExA(HKEY_CLASSES_ROOT, "CLSID", 0, KEY_WRITE, &hClsIDKey) )
    {
        SHDeleteKey(hClsIDKey, psz_CLSID);
        RegCloseKey(hClsIDKey);
    }
    CoTaskMemFree((void *)psz_CLSID);
};

STDAPI DllUnregisterServer(VOID)
{
    // unregister type lib from the registry
    UnRegisterTypeLib(LIBID_AXVLC, 1, 0, LOCALE_NEUTRAL, SYS_WIN32);

    // remove component categories we supports
    ICatRegister *pcr;
    if( SUCCEEDED(CoCreateInstance(CLSID_StdComponentCategoriesMgr,
            NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr)) ) {
        CATID implCategories[] = {
            CATID_Control,
            CATID_PersistsToPropertyBag,
            CATID_InternetAware,
            CATID_SafeForInitializing,
            CATID_SafeForScripting,
        };

        pcr->UnRegisterClassImplCategories(CLSID_VLCPlugin,
                sizeof(implCategories)/sizeof(CATID), implCategories);
        pcr->UnRegisterClassImplCategories(CLSID_VLCPlugin2,
                sizeof(implCategories)/sizeof(CATID), implCategories);
        pcr->Release();
    }

    SHDeleteKey(HKEY_CLASSES_ROOT, TEXT(PROGID_STR));

    UnregisterProgID(CLSID_VLCPlugin, 2);
    UnregisterProgID(CLSID_VLCPlugin2, 1);

    return S_OK;
};

static HRESULT RegisterClassID(HKEY hParent, REFCLSID rclsid, unsigned int version, BOOL isDefault, const char *path, size_t pathLen)
{
    char progId[sizeof(PROGID_STR)+16];
    sprintf(progId, "%s.%u", PROGID_STR, version);

    char description[sizeof(DESCRIPTION)+16];
    sprintf(description, "%s v%u", DESCRIPTION, version);

    HKEY hClassKey;
    {
        LPCSTR psz_CLSID = CStrFromGUID(rclsid);

        if( NULL == psz_CLSID )
            return E_OUTOFMEMORY;

        HKEY hProgKey = keyCreate(HKEY_CLASSES_ROOT, progId);
        if( NULL != hProgKey )
        {
            // default key value
            keySetDef(hProgKey, description);

            keyClose(keySetDef(keyCreate(hProgKey, "CLSID"),
                psz_CLSID,
                GUID_STRLEN));

            //hSubKey = keyClose(keyCreate(hBaseKey, "Insertable"));

            RegCloseKey(hProgKey);
        }
        if( isDefault )
        {
            hProgKey = keyCreate(HKEY_CLASSES_ROOT, PROGID_STR);
            if( NULL != hProgKey )
            {
                // default key value
                keySetDef(hProgKey, description);

                keyClose(keySetDef(keyCreate(hProgKey, "CLSID"),
                    psz_CLSID,
                    GUID_STRLEN));

                keyClose(keySetDef(keyCreate(hProgKey, "CurVer"),
                    progId));
            }
        }
        hClassKey = keyCreate(hParent, psz_CLSID);
        CoTaskMemFree((void *)psz_CLSID);
    }
    if( NULL != hClassKey )
    {
        // default key value
        keySetDef(hClassKey, description);

        // Control key value
        keyClose(keyCreate(hClassKey, "Control"));

        // Insertable key value
        //keyClose(keyCreate(hClassKey, "Insertable"));

        // ToolboxBitmap32 key value
        {
            char iconPath[pathLen+3];
            memcpy(iconPath, path, pathLen);
            strcpy(iconPath+pathLen, ",1");
            keyClose(keySetDef(keyCreate(hClassKey,
                "ToolboxBitmap32"),
                iconPath, sizeof(iconPath)));
        }

#ifdef BUILD_LOCALSERVER
        // LocalServer32 key value
        keyClose(keySetDef(keyCreate(hClassKey,
            "LocalServer32", path, pathLen+1)));
#else
        // InprocServer32 key value
        {
            HKEY hSubKey = keySetDef(keyCreate(hClassKey,
                "InprocServer32"),
                path, pathLen+1);
            keySet(hSubKey,
                "ThreadingModel",
                THREADING_MODEL, sizeof(THREADING_MODEL));
            keyClose(hSubKey);
        }
#endif

        // MiscStatus key value
        keyClose(keySetDef(keyCreate(hClassKey,
            "MiscStatus\\1"),
            MISC_STATUS, sizeof(MISC_STATUS)));

        // Programmable key value
        keyClose(keyCreate(hClassKey, "Programmable"));

        // ProgID key value
        keyClose(keySetDef(keyCreate(hClassKey,
            TEXT("ProgID")),
            progId));

        // VersionIndependentProgID key value
        keyClose(keySetDef(keyCreate(hClassKey,
            "VersionIndependentProgID"),
            PROGID_STR, sizeof(PROGID_STR)));

        // Version key value
        keyClose(keySetDef(keyCreate(hClassKey,
            "Version"),
            "1.0"));

        // TypeLib key value
        LPCSTR psz_LIBID = CStrFromGUID(LIBID_AXVLC);
        if( NULL != psz_LIBID )
        {
            keyClose(keySetDef(keyCreate(hClassKey,
                    "TypeLib"),
                    psz_LIBID, GUID_STRLEN));
            CoTaskMemFree((void *)psz_LIBID);
        }
        RegCloseKey(hClassKey);
    }
    return S_OK;
}

STDAPI DllRegisterServer(VOID)
{
    DllUnregisterServer();

    char DllPath[MAX_PATH];
    DWORD DllPathLen=GetModuleFileNameA(h_instance, DllPath, sizeof(DllPath)) ;
    if( 0 == DllPathLen )
        return E_UNEXPECTED;

    HKEY hBaseKey;

    if( ERROR_SUCCESS != RegOpenKeyExA(HKEY_CLASSES_ROOT, "CLSID", 0, KEY_CREATE_SUB_KEY, &hBaseKey) )
        return SELFREG_E_CLASS;

    RegisterClassID(hBaseKey, CLSID_VLCPlugin, 1, FALSE, DllPath, DllPathLen);
    RegisterClassID(hBaseKey, CLSID_VLCPlugin2, 2, TRUE, DllPath, DllPathLen);

    RegCloseKey(hBaseKey);

    // indicate which component categories we support
    ICatRegister *pcr;
    if( SUCCEEDED(CoCreateInstance(CLSID_StdComponentCategoriesMgr,
            NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr)) ) {
        CATID implCategories[] = {
            CATID_Control,
            CATID_PersistsToPropertyBag,
            CATID_InternetAware,
            CATID_SafeForInitializing,
            CATID_SafeForScripting,
        };

        pcr->RegisterClassImplCategories(CLSID_VLCPlugin,
                sizeof(implCategories)/sizeof(CATID), implCategories);
        pcr->RegisterClassImplCategories(CLSID_VLCPlugin2,
                sizeof(implCategories)/sizeof(CATID), implCategories);
        pcr->Release();
    }

    // register type lib into the registry
    ITypeLib *typeLib;

#ifdef BUILD_LOCALSERVER
    // replace .exe by .tlb
    strcpy(DllPath+DllPathLen-4, ".tlb");
#endif

#ifndef OLE2ANSI
    size_t typeLibPathLen = MultiByteToWideChar(CP_ACP, 0, DllPath, -1, NULL, 0);
    if( typeLibPathLen > 0 )
    {
        LPOLESTR typeLibPath = (LPOLESTR)CoTaskMemAlloc(typeLibPathLen*sizeof(wchar_t));
        MultiByteToWideChar(CP_ACP, 0, DllPath, DllPathLen, typeLibPath, typeLibPathLen);
        if( FAILED(LoadTypeLibEx(typeLibPath, REGKIND_REGISTER, &typeLib)) )
#ifndef BUILD_LOCALSERVER
            return SELFREG_E_TYPELIB;
        typeLib->Release();
#endif
        CoTaskMemFree((void *)typeLibPath);
    }
#else
    if( FAILED(LoadTypeLibEx((LPOLESTR)DllPath, REGKIND_REGISTER, &typeLib)) )
        return SELFREG_E_TYPELIB;
    typeLib->Release();
#endif

    return S_OK;
};

#ifdef BUILD_LOCALSERVER

/*
** easier to debug an application than a DLL on cygwin GDB :)
*/
#include <iostream>

STDAPI_(int) WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
{
    MSG msg;

    if( FAILED(OleInitialize(NULL)) )
    {
        cerr << "cannot initialize OLE" << endl;
        return 1;
    }

    h_instance = hInst;

    if( FAILED(DllRegisterServer()) )
    {
        cerr << "cannot register Local Server" << endl;
        return 1;
    }

    IUnknown *classProc = NULL;

    if( FAILED(DllGetClassObject(CLSID_VLCPlugin, IID_IUnknown, (LPVOID *)&classProc)) )
        return 0;

    DWORD dwRegisterClassObject;
    DWORD dwRegisterClassObject2;

    if( FAILED(CoRegisterClassObject(CLSID_VLCPlugin, classProc,
        CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &dwRegisterClassObject)) )
        return 0;

    DWORD dwRegisterActiveObject;

    if( FAILED(RegisterActiveObject(classProc, CLSID_VLCPlugin,
                    ACTIVEOBJECT_WEAK, &dwRegisterActiveObject)) )
        return 0;

    if( FAILED(RegisterActiveObject(classProc, CLSID_VLCPlugin2,
                    ACTIVEOBJECT_WEAK, &dwRegisterActiveObject2)) )
        return 0;

    classProc->Release();

    /*
    * Polling messages from event queue
    */
    while( S_FALSE == DllCanUnloadNow() )
    {
        while( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
        {
            if( msg.message == WM_QUIT )
                break;  // break out PeekMessage loop

            /*if(TranslateAccelerator(ghwndApp, ghAccel, &msg))
                continue;*/

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        if(msg.message == WM_QUIT)
            break;  // break out main loop

        WaitMessage();
    }

    if( SUCCEEDED(RevokeActiveObject(dwRegisterActiveObject, NULL)) )
        CoRevokeClassObject(dwRegisterClassObject);

    if( SUCCEEDED(RevokeActiveObject(dwRegisterActiveObject2, NULL)) )
        CoRevokeClassObject(dwRegisterClassObject2);

    // Reached on WM_QUIT message
    OleUninitialize();
    return ((int) msg.wParam);
};

#else

STDAPI_(BOOL) DllMain(HANDLE hModule, DWORD fdwReason, LPVOID lpReserved )
{
    switch( fdwReason )
    {
        case DLL_PROCESS_ATTACH:
            h_instance = (HINSTANCE)hModule;
            break;

        default:
            break;
    }
    return TRUE;
};

#endif

<|endoftext|>


<commit_before>
#include "utils.h"

#include <functional>
#include <chrono>
#include <cstring>

#ifdef _WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif

namespace utils
{
    void RunLoop(float deltaSec, std::function<bool(double, float)> loopFunc)
    {
        using namespace std::chrono;
        using DoubleDuration = duration<double>;
        using DoubleTimePoint = time_point<high_resolution_clock, DoubleDuration>;

        const auto period = duration_cast<high_resolution_clock::duration>(duration<float>(deltaSec));

        auto currentTime = high_resolution_clock::now();
        auto accumulator = high_resolution_clock::duration();
        auto userTime = high_resolution_clock::duration();

        const auto MAX_FRAME_DURATION = duration_cast<high_resolution_clock::duration>(milliseconds(250));

        bool keepLooping = true;
        while (keepLooping)
        {
            auto now = high_resolution_clock::now();
            auto frameTime = (now - currentTime);
            if (frameTime > MAX_FRAME_DURATION)
            {
                frameTime = MAX_FRAME_DURATION;
            }
            currentTime = now;
            accumulator += frameTime;
            while (accumulator >= period)
            {
                DoubleDuration curTimeDouble = duration_cast<DoubleDuration>(userTime);
                keepLooping = loopFunc(curTimeDouble.count(), deltaSec) && keepLooping;
                accumulator -= period;
                userTime += period;
            }

        }
    }

    void FixWorkingDirectory()
    {
        char temp[128] = {};
        const char *dir = getcwd(temp, sizeof(temp));
        const char *bin_pos = strstr(dir, "bin");
        const char *build_pos = strstr(dir, "premake");
        if (bin_pos)
        {
            chdir("..");
        }
        else if (build_pos)
        {
            chdir("../../..");
        }
    }
}
<commit_msg>Changed FixWorkingDir() to just back up one dir level. The binaries should only be run from the bin directory.<commit_after>
#include "utils.h"

#include <functional>
#include <chrono>
#include <cstring>

#ifdef _WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif

namespace utils
{
    void RunLoop(float deltaSec, std::function<bool(double, float)> loopFunc)
    {
        using namespace std::chrono;
        using DoubleDuration = duration<double>;
        using DoubleTimePoint = time_point<high_resolution_clock, DoubleDuration>;

        const auto period = duration_cast<high_resolution_clock::duration>(duration<float>(deltaSec));

        auto currentTime = high_resolution_clock::now();
        auto accumulator = high_resolution_clock::duration();
        auto userTime = high_resolution_clock::duration();

        const auto MAX_FRAME_DURATION = duration_cast<high_resolution_clock::duration>(milliseconds(250));

        bool keepLooping = true;
        while (keepLooping)
        {
            auto now = high_resolution_clock::now();
            auto frameTime = (now - currentTime);
            if (frameTime > MAX_FRAME_DURATION)
            {
                frameTime = MAX_FRAME_DURATION;
            }
            currentTime = now;
            accumulator += frameTime;
            while (accumulator >= period)
            {
                DoubleDuration curTimeDouble = duration_cast<DoubleDuration>(userTime);
                keepLooping = loopFunc(curTimeDouble.count(), deltaSec) && keepLooping;
                accumulator -= period;
                userTime += period;
            }

        }
    }

    void FixWorkingDirectory()
    {
        chdir("..");
    }
}
<|endoftext|>


<commit_before>#include "util/log.hpp"
#include "util/isatty.hpp"
#include <boost/algorithm/string/predicate.hpp>
#include <cstdio>
#include <iostream>
#include <mutex>
#include <string>

namespace osrm
{
namespace util
{

namespace
{
static const char COL_RESET[]{"\x1b[0m"};
static const char RED[]{"\x1b[31m"};
static const char YELLOW[]{"\x1b[33m"};
static const char MAGENTA[]{"\x1b[35m"};
// static const char GREEN[] { "\x1b[32m"};
// static const char BLUE[] { "\x1b[34m"};
// static const char CYAN[] { "\x1b[36m"};
}

void LogPolicy::Unmute() { m_is_mute = false; }

void LogPolicy::Mute() { m_is_mute = true; }

bool LogPolicy::IsMute() const { return m_is_mute; }

LogLevel LogPolicy::GetLevel() const { return m_level; }

void LogPolicy::SetLevel(LogLevel level) { m_level = level; }

void LogPolicy::SetLevel(std::string const &level)
{
    // Keep in sync with LogLevel definition
    if (boost::iequals(level, "NONE"))
        m_level = logNONE;
    else if (boost::iequals(level, "ERROR"))
        m_level = logERROR;
    else if (boost::iequals(level, "WARNING"))
        m_level = logWARNING;
    else if (boost::iequals(level, "INFO"))
        m_level = logINFO;
    else if (boost::iequals(level, "DEBUG"))
        m_level = logDEBUG;
    else
        m_level = logINFO;
}

LogPolicy &LogPolicy::GetInstance()
{
    static LogPolicy runningInstance;
    return runningInstance;
}

std::string LogPolicy::GetLevels()
{
    // Keep in sync with LogLevel definition
    return "NONE, ERROR, WARNING, INFO, DEBUG";
}

Log::Log(LogLevel level_, std::ostream &ostream) : level(level_), stream(ostream)
{
    std::lock_guard<std::mutex> lock(get_mutex());
    if (!LogPolicy::GetInstance().IsMute() && level <= LogPolicy::GetInstance().GetLevel())
    {
        const bool is_terminal = IsStdoutATTY();
        switch (level)
        {
        case logNONE:
            break;
        case logWARNING:
            stream << (is_terminal ? YELLOW : "") << "[warn] ";
            break;
        case logERROR:
            stream << (is_terminal ? RED : "") << "[error] ";
            break;
        case logDEBUG:
            stream << (is_terminal ? MAGENTA : "") << "[debug] ";
            break;
        default: // logINFO:
            stream << "[info] ";
            break;
        }
    }
}

Log::Log(LogLevel level_) : Log(level_, buffer) {}

std::mutex &Log::get_mutex()
{
    static std::mutex mtx;
    return mtx;
}

/**
 * Close down this logging instance.
 * This destructor is responsible for flushing any buffered data,
 * and printing a newline character (each logger object is responsible for only one line)
 * Because sub-classes can replace the `stream` object - we need to verify whether
 * we're writing to std::cerr/cout, or whether we should write to the stream
 */
Log::~Log()
{
    std::lock_guard<std::mutex> lock(get_mutex());
    if (!LogPolicy::GetInstance().IsMute() && level <= LogPolicy::GetInstance().GetLevel())
    {
        const bool usestd = (&stream == &buffer);
        const bool is_terminal = IsStdoutATTY();
        if (usestd)
        {
            switch (level)
            {
            case logNONE:
                break;
            case logWARNING:
            case logERROR:
                std::cerr << buffer.str();
                std::cerr << (is_terminal ? COL_RESET : "");
                std::cerr << std::endl;
                break;
            case logDEBUG:
            case logINFO:
            default:
                std::cout << buffer.str();
                std::cout << (is_terminal ? COL_RESET : "");
                std::cout << std::endl;
                break;
            }
        }
        else
        {
            stream << (is_terminal ? COL_RESET : "");
            stream << std::endl;
        }
    }
}

UnbufferedLog::UnbufferedLog(LogLevel level_)
    : Log(level_, (level_ == logWARNING || level_ == logERROR) ? std::cerr : std::cout)
{
    stream.flags(std::ios_base::unitbuf);
}
}
}
<commit_msg>Revert "chore: remove compile time debug log control"<commit_after>#include "util/log.hpp"
#include "util/isatty.hpp"
#include <boost/algorithm/string/predicate.hpp>
#include <cstdio>
#include <iostream>
#include <mutex>
#include <string>

namespace osrm
{
namespace util
{

namespace
{
static const char COL_RESET[]{"\x1b[0m"};
static const char RED[]{"\x1b[31m"};
static const char YELLOW[]{"\x1b[33m"};
#ifndef NDEBUG
static const char MAGENTA[]{"\x1b[35m"};
#endif
// static const char GREEN[] { "\x1b[32m"};
// static const char BLUE[] { "\x1b[34m"};
// static const char CYAN[] { "\x1b[36m"};
}

void LogPolicy::Unmute() { m_is_mute = false; }

void LogPolicy::Mute() { m_is_mute = true; }

bool LogPolicy::IsMute() const { return m_is_mute; }

LogLevel LogPolicy::GetLevel() const { return m_level; }

void LogPolicy::SetLevel(LogLevel level) { m_level = level; }

void LogPolicy::SetLevel(std::string const &level)
{
    // Keep in sync with LogLevel definition
    if (boost::iequals(level, "NONE"))
        m_level = logNONE;
    else if (boost::iequals(level, "ERROR"))
        m_level = logERROR;
    else if (boost::iequals(level, "WARNING"))
        m_level = logWARNING;
    else if (boost::iequals(level, "INFO"))
        m_level = logINFO;
    else if (boost::iequals(level, "DEBUG"))
        m_level = logDEBUG;
    else
        m_level = logINFO;
}

LogPolicy &LogPolicy::GetInstance()
{
    static LogPolicy runningInstance;
    return runningInstance;
}

std::string LogPolicy::GetLevels()
{
    // Keep in sync with LogLevel definition
    return "NONE, ERROR, WARNING, INFO, DEBUG";
}

Log::Log(LogLevel level_, std::ostream &ostream) : level(level_), stream(ostream)
{
    std::lock_guard<std::mutex> lock(get_mutex());
    if (!LogPolicy::GetInstance().IsMute() && level <= LogPolicy::GetInstance().GetLevel())
    {
        const bool is_terminal = IsStdoutATTY();
        switch (level)
        {
        case logNONE:
            break;
        case logWARNING:
            stream << (is_terminal ? YELLOW : "") << "[warn] ";
            break;
        case logERROR:
            stream << (is_terminal ? RED : "") << "[error] ";
            break;
        case logDEBUG:
#ifndef NDEBUG
            stream << (is_terminal ? MAGENTA : "") << "[debug] ";
#endif
            break;
        default: // logINFO:
            stream << "[info] ";
            break;
        }
    }
}

Log::Log(LogLevel level_) : Log(level_, buffer) {}

std::mutex &Log::get_mutex()
{
    static std::mutex mtx;
    return mtx;
}

/**
 * Close down this logging instance.
 * This destructor is responsible for flushing any buffered data,
 * and printing a newline character (each logger object is responsible for only one line)
 * Because sub-classes can replace the `stream` object - we need to verify whether
 * we're writing to std::cerr/cout, or whether we should write to the stream
 */
Log::~Log()
{
    std::lock_guard<std::mutex> lock(get_mutex());
    if (!LogPolicy::GetInstance().IsMute() && level <= LogPolicy::GetInstance().GetLevel())
    {
        const bool usestd = (&stream == &buffer);
        const bool is_terminal = IsStdoutATTY();
        if (usestd)
        {
            switch (level)
            {
            case logNONE:
                break;
            case logWARNING:
            case logERROR:
                std::cerr << buffer.str();
                std::cerr << (is_terminal ? COL_RESET : "");
                std::cerr << std::endl;
                break;
            case logDEBUG:
#ifdef NDEBUG
                break;
#endif
            case logINFO:
            default:
                std::cout << buffer.str();
                std::cout << (is_terminal ? COL_RESET : "");
                std::cout << std::endl;
                break;
            }
        }
        else
        {
            stream << (is_terminal ? COL_RESET : "");
            stream << std::endl;
        }
    }
}

UnbufferedLog::UnbufferedLog(LogLevel level_)
    : Log(level_, (level_ == logWARNING || level_ == logERROR) ? std::cerr : std::cout)
{
    stream.flags(std::ios_base::unitbuf);
}
}
}
<|endoftext|>


<commit_before>#include <assert.h>
#include <stdio.h>
#include <atomic>
#include <new>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <pthread.h>
#include <signal.h>
#include <setjmp.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include "mtrace.h"
#include "fstest.h"
#include "libutil.h"
#include "spinbarrier.hh"

extern char _end[];
__thread sigjmp_buf pf_jmpbuf;
__thread int pf_active;

class double_barrier {
  spin_barrier enter_;
  spin_barrier exit_;

public:
  double_barrier() : enter_(2), exit_(2) {}
  void sync() { enter(); exit(); }
  void enter() { enter_.join(); }
  void exit() { exit_.join(); }
};

struct testproc {
  double_barrier setup;
  std::atomic<void (*)(void)> setupf1;
  std::atomic<void (*)(void)> setupf2;

  testproc(void (*s1)(void), void (*s2)(void)) : setupf1(s1), setupf2(s2) {}
  void run() {
    setup.enter();
    setupf1();
    setupf2();
    setup.exit();
  }
};

struct testfunc {
  double_barrier start;
  double_barrier stop;

  std::atomic<int (*)(void)> func;
  std::atomic<int> retval;

  testfunc(int (*f)(void)) : func(f) {}
  void run() {
#ifndef XV6_USER
    errno = 0;
#endif
    start.sync();
    retval = func();
    stop.sync();
  }
};

static void*
testfunc_thread(void* arg)
{
  madvise(0, (size_t) _end, MADV_WILLNEED);
  testfunc* f = (testfunc*) arg;
  f->run();
  return nullptr;
}

static void
run_test(testproc* tp, testfunc* tf, fstest* t, int first_func, bool do_pin)
{
  assert(first_func == 0 || first_func == 1);
  if (do_pin)
    setaffinity(2);

  for (int i = 0; i < 2; i++)
    new (&tp[i]) testproc(t->proc[i].setup_proc, t->setup_procfinal);
  for (int i = 0; i < 2; i++)
    new (&tf[i]) testfunc(t->func[i].call);

  t->setup_common();

  pid_t pids[2] = { 0, 0 };
  for (int p = 0; p < 2; p++) {
    int nfunc = 0;
    for (int f = 0; f < 2; f++)
      if (t->func[f].callproc == p)
        nfunc++;
    if (nfunc == 0)
      continue;

    fflush(stdout);
    pids[p] = fork();
    assert(pids[p] >= 0);

    if (pids[p] == 0) {
      // Get all text and data structures
      madvise(0, (size_t) _end, MADV_WILLNEED);
      // Prime the VM system (this must be kept in sync with
      // fs_testgen.py)
      void *r = mmap((void*)0x12345600000, 4 * 4096, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
      if (r == (void*)-1)
        setup_error("mmap");
      munmap(r, 4 * 4096);

      // Run setup
      tp[p].run();

      int ndone = 0;
      pthread_t tid[2];
      for (int f = 0; f < 2; f++) {
        if (t->func[f].callproc == p) {
          if (do_pin)
            setaffinity(f);
          ndone++;
          if (ndone == nfunc)
            testfunc_thread(&tf[f]);
          else
            pthread_create(&tid[f], 0, testfunc_thread, (void*) &tf[f]);
        }
      }

      if (nfunc == 2)
        pthread_join(tid[0], nullptr);
      exit(0);
    }
  }

  for (int p = 0; p < 2; p++) if (pids[p]) tp[p].setup.sync();
  t->setup_final();

  for (int i = 0; i < 2; i++) tf[i].start.enter();

  char mtname[64];
  snprintf(mtname, sizeof(mtname), "%s", t->testname);
  mtenable_type(mtrace_record_ascope, mtname);

  for (int i = 0; i < 2; i++) {
    tf[first_func ^ i].start.exit();
    tf[first_func ^ i].stop.enter();
  }

  mtdisable(mtname);

  for (int i = 0; i < 2; i++) tf[i].stop.exit();

  for (int p = 0; p < 2; p++)
    if (pids[p])
      assert(waitpid(pids[p], nullptr, 0) >= 0);

  t->cleanup();
}

static void
pf_handler(int signo)
{
  if (pf_active)
    siglongjmp(pf_jmpbuf, signo);
  // Let the error happen
  signal(signo, SIG_DFL);
}

static bool verbose = false;
static bool check_commutativity = false;
static bool run_threads = false;
static bool check_results = false;
static fstest *cur_test = nullptr;

void
expect_result(const char *varname, long got, long expect)
{
  if (!check_results) return;
  if (got == expect) return;
  auto name = cur_test->testname;
#ifdef XV6_USER
  printf("%s: expected %s == %ld, got %ld\n",
         name, varname, expect, got);
#else
  printf("%s: expected %s == %ld, got %ld (errno %s)\n",
         name, varname, expect, got, strerror(errno));
#endif
}

void
expect_errno(int expect)
{
#ifndef XV6_USER
  if (!check_results) return;
  if (errno == expect) return;
  auto name = cur_test->testname;
  printf("%s: expected errno == %s, got %s\n",
         name, strerror(expect), strerror(errno));
#endif
}

static void
usage(const char* prog)
{
  fprintf(stderr, "Usage: %s [-v] [-c] [-t] [-r] [-n NPARTS] [-p THISPART] [min[-max]]\n", prog);
}

int
main(int ac, char** av)
{
  uint32_t min = 0;
  uint32_t max;
  int nparts = -1;
  int thispart = -1;

  uint32_t ntests = 0;
  for (ntests = min; fstests[ntests].testname; ntests++)
    ;
  max = ntests - 1;

  for (;;) {
    int opt = getopt(ac, av, "vctrp:n:");
    if (opt == -1)
      break;

    switch (opt) {
    case 'v':
      verbose = true;
      break;

    case 'c':
      check_commutativity = true;
      break;

    case 't':
      run_threads = true;
      break;

    case 'r':
      run_threads = true;
      check_results = true;
      break;

    case 'n':
      nparts = atoi(optarg);
      break;

    case 'p':
      thispart = atoi(optarg);
      break;

    default:
      usage(av[0]);
      return -1;
    }
  }

  if (optind < ac) {
    char* dash = strchr(av[optind], '-');
    if (!dash) {
      min = max = atoi(av[optind]);
    } else {
      *dash = '\0';
      if (av[optind])
        min = atoi(av[optind]);
      if (*(dash + 1))
        max = atoi(dash + 1);
    }
  } else if (nparts >= 0 || thispart >= 0) {
    if (nparts < 0 || thispart < 0) {
      usage(av[0]);
      return -1;
    }

    uint32_t partsize = (ntests + nparts - 1) /nparts;
    min = partsize * thispart;
    max = partsize * (thispart + 1) - 1;
  }

  if (!check_commutativity && !run_threads) {
    fprintf(stderr, "Must specify one of -c or -t\n");
    usage(av[0]);
    return -1;
  }

  printf("fstest:");
  if (verbose)
    printf(" verbose");
  if (check_commutativity)
    printf(" check");
  if (run_threads)
    printf(" threads");
  if (check_results)
    printf(" results");
  if (min == 0 && max == ntests - 1)
    printf(" all");
  else if (min == max)
    printf(" %d", min);
  else
    printf(" %d-%d", min, max);
  printf("\n");

  testproc* tp = (testproc*) mmap(nullptr, 4096, PROT_READ | PROT_WRITE,
                                  MAP_SHARED | MAP_ANONYMOUS, -1, 0);
  assert(tp != MAP_FAILED);
  assert(2*sizeof(*tp) <= 4096);

  testfunc* tf = (testfunc*) mmap(nullptr, 4096, PROT_READ | PROT_WRITE,
                                  MAP_SHARED | MAP_ANONYMOUS, -1, 0);
  assert(tf != MAP_FAILED);
  assert(2*sizeof(*tf) <= 4096);

  madvise(0, (size_t) _end, MADV_WILLNEED);

  signal(SIGPIPE, SIG_IGN);
  signal(SIGBUS, pf_handler);
  signal(SIGSEGV, pf_handler);

  for (uint32_t t = min; t <= max && t < ntests; t++) {
    cur_test = &fstests[t];

    if (verbose)
      printf("%s (test %d) starting\n", fstests[t].testname, t);

    if (check_commutativity) {
      run_test(tp, tf, &fstests[t], 0, false);
      int ra0 = tf[0].retval;
      int ra1 = tf[1].retval;

      run_test(tp, tf, &fstests[t], 1, false);
      int rb0 = tf[0].retval;
      int rb1 = tf[1].retval;

      if (ra0 == rb0 && ra1 == rb1) {
        if (verbose)
          printf("%s: commutes: %s->%d %s->%d\n",
                 fstests[t].testname,
                 fstests[t].func[0].callname, ra0, fstests[t].func[1].callname, ra1);
      } else {
        printf("%s: diverges: %s->%d %s->%d vs %s->%d %s->%d\n",
               fstests[t].testname,
               fstests[t].func[0].callname, ra0, fstests[t].func[1].callname, ra1,
               fstests[t].func[1].callname, rb1, fstests[t].func[0].callname, rb0);
      }
    }

    if (run_threads) {
      run_test(tp, tf, &fstests[t], 0, true);
    }
  }

  printf("fstest: done\n");
}
<commit_msg>fstest: Prime non-fixed mmap region<commit_after>#include <assert.h>
#include <stdio.h>
#include <atomic>
#include <new>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <pthread.h>
#include <signal.h>
#include <setjmp.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include "mtrace.h"
#include "fstest.h"
#include "libutil.h"
#include "spinbarrier.hh"

extern char _end[];
__thread sigjmp_buf pf_jmpbuf;
__thread int pf_active;

class double_barrier {
  spin_barrier enter_;
  spin_barrier exit_;

public:
  double_barrier() : enter_(2), exit_(2) {}
  void sync() { enter(); exit(); }
  void enter() { enter_.join(); }
  void exit() { exit_.join(); }
};

struct testproc {
  double_barrier setup;
  std::atomic<void (*)(void)> setupf1;
  std::atomic<void (*)(void)> setupf2;

  testproc(void (*s1)(void), void (*s2)(void)) : setupf1(s1), setupf2(s2) {}
  void run() {
    setup.enter();
    setupf1();
    setupf2();
    setup.exit();
  }
};

struct testfunc {
  double_barrier start;
  double_barrier stop;

  std::atomic<int (*)(void)> func;
  std::atomic<int> retval;

  testfunc(int (*f)(void)) : func(f) {}
  void run() {
#ifndef XV6_USER
    errno = 0;
#endif
    start.sync();
    retval = func();
    stop.sync();
  }
};

static void*
testfunc_thread(void* arg)
{
  madvise(0, (size_t) _end, MADV_WILLNEED);
  testfunc* f = (testfunc*) arg;
  f->run();
  return nullptr;
}

static void
run_test(testproc* tp, testfunc* tf, fstest* t, int first_func, bool do_pin)
{
  assert(first_func == 0 || first_func == 1);
  if (do_pin)
    setaffinity(2);

  for (int i = 0; i < 2; i++)
    new (&tp[i]) testproc(t->proc[i].setup_proc, t->setup_procfinal);
  for (int i = 0; i < 2; i++)
    new (&tf[i]) testfunc(t->func[i].call);

  t->setup_common();

  pid_t pids[2] = { 0, 0 };
  for (int p = 0; p < 2; p++) {
    int nfunc = 0;
    for (int f = 0; f < 2; f++)
      if (t->func[f].callproc == p)
        nfunc++;
    if (nfunc == 0)
      continue;

    fflush(stdout);
    pids[p] = fork();
    assert(pids[p] >= 0);

    if (pids[p] == 0) {
      // Get all text and data structures
      madvise(0, (size_t) _end, MADV_WILLNEED);

      // Prime the VM system (this must be kept in sync with
      // fs_testgen.py)
      void *r = mmap((void*)0x12345600000, 4 * 4096, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
      if (r == (void*)-1)
        setup_error("mmap (fixed)");
      munmap(r, 4 * 4096);

      r = mmap(0, 4 * 4096, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
      if (r == (void*)-1)
        setup_error("mmap (non-fixed)");
      munmap(r, 4 * 4096);

      // Run setup
      tp[p].run();

      int ndone = 0;
      pthread_t tid[2];
      for (int f = 0; f < 2; f++) {
        if (t->func[f].callproc == p) {
          if (do_pin)
            setaffinity(f);
          ndone++;
          if (ndone == nfunc)
            testfunc_thread(&tf[f]);
          else
            pthread_create(&tid[f], 0, testfunc_thread, (void*) &tf[f]);
        }
      }

      if (nfunc == 2)
        pthread_join(tid[0], nullptr);
      exit(0);
    }
  }

  for (int p = 0; p < 2; p++) if (pids[p]) tp[p].setup.sync();
  t->setup_final();

  for (int i = 0; i < 2; i++) tf[i].start.enter();

  char mtname[64];
  snprintf(mtname, sizeof(mtname), "%s", t->testname);
  mtenable_type(mtrace_record_ascope, mtname);

  for (int i = 0; i < 2; i++) {
    tf[first_func ^ i].start.exit();
    tf[first_func ^ i].stop.enter();
  }

  mtdisable(mtname);

  for (int i = 0; i < 2; i++) tf[i].stop.exit();

  for (int p = 0; p < 2; p++)
    if (pids[p])
      assert(waitpid(pids[p], nullptr, 0) >= 0);

  t->cleanup();
}

static void
pf_handler(int signo)
{
  if (pf_active)
    siglongjmp(pf_jmpbuf, signo);
  // Let the error happen
  signal(signo, SIG_DFL);
}

static bool verbose = false;
static bool check_commutativity = false;
static bool run_threads = false;
static bool check_results = false;
static fstest *cur_test = nullptr;

void
expect_result(const char *varname, long got, long expect)
{
  if (!check_results) return;
  if (got == expect) return;
  auto name = cur_test->testname;
#ifdef XV6_USER
  printf("%s: expected %s == %ld, got %ld\n",
         name, varname, expect, got);
#else
  printf("%s: expected %s == %ld, got %ld (errno %s)\n",
         name, varname, expect, got, strerror(errno));
#endif
}

void
expect_errno(int expect)
{
#ifndef XV6_USER
  if (!check_results) return;
  if (errno == expect) return;
  auto name = cur_test->testname;
  printf("%s: expected errno == %s, got %s\n",
         name, strerror(expect), strerror(errno));
#endif
}

static void
usage(const char* prog)
{
  fprintf(stderr, "Usage: %s [-v] [-c] [-t] [-r] [-n NPARTS] [-p THISPART] [min[-max]]\n", prog);
}

int
main(int ac, char** av)
{
  uint32_t min = 0;
  uint32_t max;
  int nparts = -1;
  int thispart = -1;

  uint32_t ntests = 0;
  for (ntests = min; fstests[ntests].testname; ntests++)
    ;
  max = ntests - 1;

  for (;;) {
    int opt = getopt(ac, av, "vctrp:n:");
    if (opt == -1)
      break;

    switch (opt) {
    case 'v':
      verbose = true;
      break;

    case 'c':
      check_commutativity = true;
      break;

    case 't':
      run_threads = true;
      break;

    case 'r':
      run_threads = true;
      check_results = true;
      break;

    case 'n':
      nparts = atoi(optarg);
      break;

    case 'p':
      thispart = atoi(optarg);
      break;

    default:
      usage(av[0]);
      return -1;
    }
  }

  if (optind < ac) {
    char* dash = strchr(av[optind], '-');
    if (!dash) {
      min = max = atoi(av[optind]);
    } else {
      *dash = '\0';
      if (av[optind])
        min = atoi(av[optind]);
      if (*(dash + 1))
        max = atoi(dash + 1);
    }
  } else if (nparts >= 0 || thispart >= 0) {
    if (nparts < 0 || thispart < 0) {
      usage(av[0]);
      return -1;
    }

    uint32_t partsize = (ntests + nparts - 1) /nparts;
    min = partsize * thispart;
    max = partsize * (thispart + 1) - 1;
  }

  if (!check_commutativity && !run_threads) {
    fprintf(stderr, "Must specify one of -c or -t\n");
    usage(av[0]);
    return -1;
  }

  printf("fstest:");
  if (verbose)
    printf(" verbose");
  if (check_commutativity)
    printf(" check");
  if (run_threads)
    printf(" threads");
  if (check_results)
    printf(" results");
  if (min == 0 && max == ntests - 1)
    printf(" all");
  else if (min == max)
    printf(" %d", min);
  else
    printf(" %d-%d", min, max);
  printf("\n");

  testproc* tp = (testproc*) mmap(nullptr, 4096, PROT_READ | PROT_WRITE,
                                  MAP_SHARED | MAP_ANONYMOUS, -1, 0);
  assert(tp != MAP_FAILED);
  assert(2*sizeof(*tp) <= 4096);

  testfunc* tf = (testfunc*) mmap(nullptr, 4096, PROT_READ | PROT_WRITE,
                                  MAP_SHARED | MAP_ANONYMOUS, -1, 0);
  assert(tf != MAP_FAILED);
  assert(2*sizeof(*tf) <= 4096);

  madvise(0, (size_t) _end, MADV_WILLNEED);

  signal(SIGPIPE, SIG_IGN);
  signal(SIGBUS, pf_handler);
  signal(SIGSEGV, pf_handler);

  for (uint32_t t = min; t <= max && t < ntests; t++) {
    cur_test = &fstests[t];

    if (verbose)
      printf("%s (test %d) starting\n", fstests[t].testname, t);

    if (check_commutativity) {
      run_test(tp, tf, &fstests[t], 0, false);
      int ra0 = tf[0].retval;
      int ra1 = tf[1].retval;

      run_test(tp, tf, &fstests[t], 1, false);
      int rb0 = tf[0].retval;
      int rb1 = tf[1].retval;

      if (ra0 == rb0 && ra1 == rb1) {
        if (verbose)
          printf("%s: commutes: %s->%d %s->%d\n",
                 fstests[t].testname,
                 fstests[t].func[0].callname, ra0, fstests[t].func[1].callname, ra1);
      } else {
        printf("%s: diverges: %s->%d %s->%d vs %s->%d %s->%d\n",
               fstests[t].testname,
               fstests[t].func[0].callname, ra0, fstests[t].func[1].callname, ra1,
               fstests[t].func[1].callname, rb1, fstests[t].func[0].callname, rb0);
      }
    }

    if (run_threads) {
      run_test(tp, tf, &fstests[t], 0, true);
    }
  }

  printf("fstest: done\n");
}
<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "navigatortreemodel.h"

#include <nodeabstractproperty.h>
#include <nodelistproperty.h>
#include <abstractview.h>
#include <qmlitemnode.h>
#include <invalididexception.h>

#include <QMimeData>

namespace QmlDesigner {

NavigatorTreeModel::NavigatorTreeModel(QObject *parent)
    : QStandardItemModel(parent),
      m_blockItemChangedSignal(false)
{
    invisibleRootItem()->setFlags(Qt::NoItemFlags);

    #ifdef _LOCK_ITEMS_
    setColumnCount(3);
    #else
    setColumnCount(2);
    #endif

    setSupportedDragActions(Qt::LinkAction);

    connect(this, SIGNAL(itemChanged(QStandardItem*)),
            this, SLOT(handleChangedItem(QStandardItem*)));
}

NavigatorTreeModel::~NavigatorTreeModel()
{
}

Qt::DropActions NavigatorTreeModel::supportedDropActions() const
{
    return Qt::LinkAction;
}

QStringList NavigatorTreeModel::mimeTypes() const
{
     QStringList types;
     types << "application/vnd.modelnode.list";
     return types;
}

QMimeData *NavigatorTreeModel::mimeData(const QModelIndexList &indexList) const
{
     QMimeData *mimeData = new QMimeData();
     QByteArray encodedData;

     QSet<QModelIndex> rowAlreadyUsedSet;

     QDataStream stream(&encodedData, QIODevice::WriteOnly);

     foreach (const QModelIndex &index, indexList) {
         if (!index.isValid())
             continue;
         QModelIndex idIndex = index.sibling(index.row(), 0);
         if (rowAlreadyUsedSet.contains(idIndex))
             continue;

         rowAlreadyUsedSet.insert(idIndex);
         stream << idIndex.data(Qt::UserRole).toUInt();
     }

     mimeData->setData("application/vnd.modelnode.list", encodedData);

     return mimeData;
}

static bool isAnchestorInList(const ModelNode &node, const QList<ModelNode> &nodeList)
{
    foreach (const ModelNode &nodeInList, nodeList) {
        if (nodeInList.isAncestorOf(node))
            return true;
    }

    return false;
}

bool NavigatorTreeModel::dropMimeData(const QMimeData *data,
                  Qt::DropAction action,
                  int row,
                  int column,
                  const QModelIndex &parentIndex)
{
    if (action == Qt::IgnoreAction)
        return true;
    if (action != Qt::LinkAction)
        return false;
    if (!data->hasFormat("application/vnd.modelnode.list"))
        return false;
    if (column > 1)
        return false;
    if (parentIndex.model() != this)
        return false;

    QModelIndex parentIdIndex = parentIndex;
    parentIdIndex = parentIdIndex.sibling(parentIdIndex.row(), 0);

    Q_ASSERT(parentIdIndex.isValid());

    int targetIndex = 0;
    if (row > -1) {
        targetIndex = row;
    } else {
        targetIndex = rowCount(parentIdIndex);
    }

    QByteArray encodedData = data->data("application/vnd.modelnode.list");
    QDataStream stream(&encodedData, QIODevice::ReadOnly);

    QmlItemNode parentItemNode(nodeForIndex(parentIdIndex));

    QList<ModelNode> nodeList;

    while (!stream.atEnd()) {
        uint nodeHash;
        stream >> nodeHash;
        ModelNode node(nodeForHash(nodeHash));
        if (!node.isValid() || (parentItemNode == node) || node.isAncestorOf(parentItemNode))
            continue;
        nodeList.append(node);
    }

    RewriterTransaction transaction = m_view->beginRewriterTransaction();
    foreach (const ModelNode &node, nodeList) {
        if (!isAnchestorInList(node, nodeList)) {
            if (node.parentProperty().parentModelNode() != parentItemNode) {
                QmlItemNode itemNode(node);
                if (node != parentItemNode) {
                    itemNode.setParent(parentItemNode);
                }
            }

            if (node.parentProperty().isNodeListProperty()) {
                int index = node.parentProperty().toNodeListProperty().toModelNodeList().indexOf(node);
                if (index < targetIndex) { // item is first removed from oldIndex, then inserted at new index
                    --targetIndex;
                }
                if (index != targetIndex) {
                    node.parentProperty().toNodeListProperty().slide(index, targetIndex);
                }
            }
        }
    }

    propagateInvisible(parentItemNode, isNodeInvisible(parentItemNode));

    return false; // don't let the view do drag&drop on its own
}

NavigatorTreeModel::ItemRow NavigatorTreeModel::createItemRow(const ModelNode &node)
{
    Q_ASSERT(node.isValid());

    uint hash = qHash(node);

    QStandardItem *idItem = new QStandardItem;
    idItem->setDragEnabled(true);
    idItem->setDropEnabled(node.metaInfo().isContainer());
    idItem->setEditable(true);
    idItem->setData(hash, Qt::UserRole);

    #ifdef _LOCK_ITEMS_
    QStandardItem *lockItem = new QStandardItem;
    lockItem->setDragEnabled(true);
    lockItem->setDropEnabled(node.metaInfo().isContainer());
    lockItem->setEditable(false);
    lockItem->setCheckable(true);
    lockItem->setData(hash, Qt::UserRole);
    #endif

    QStandardItem *visibilityItem = new QStandardItem;
    visibilityItem->setDropEnabled(node.metaInfo().isContainer());
    visibilityItem->setCheckable(true);
    visibilityItem->setEditable(false);
    visibilityItem->setData(hash, Qt::UserRole);

    #ifdef _LOCK_ITEMS_
    return ItemRow(idItem, lockItem, visibilityItem);
    #else
    return ItemRow(idItem, visibilityItem);
    #endif
}

void NavigatorTreeModel::updateItemRow(const ModelNode &node, ItemRow items)
{
    bool blockSignal = blockItemChangedSignal(true);

    items.idItem->setText(node.id());
    //items.idItem->setToolTip(!node.id().isEmpty()?node.type():"");
    items.idItem->setToolTip(node.type());
    items.visibilityItem->setCheckState(node.auxiliaryData("invisible").toBool() ? Qt::Unchecked : Qt::Checked);

    blockItemChangedSignal(blockSignal);
}

/**
  Update the information shown for a node / property
  */
void NavigatorTreeModel::updateItemRow(const ModelNode &node)
{
    if (!containsNode(node))
        return;

    updateItemRow(node, itemRowForNode(node));
}

/**
  Updates the sibling position of the item, depending on the position in the model.
  */
void NavigatorTreeModel::updateItemRowOrder(const ModelNode &node)
{
    if (!containsNode(node))
        return;

    ItemRow itemRow = itemRowForNode(node);
    int currentRow = itemRow.idItem->row();
    int newRow = currentRow;
    if (node.parentProperty().parentModelNode().isValid())
        newRow = modelNodeChildren(node.parentProperty().parentModelNode()).indexOf(node);
    Q_ASSERT(newRow >= 0);

    if (currentRow != newRow) {
        QStandardItem *parentIdItem = itemRow.idItem->parent();
        QList<QStandardItem*> items = parentIdItem->takeRow(currentRow);
        parentIdItem->insertRow(newRow, items);
    }
}

void NavigatorTreeModel::handleChangedItem(QStandardItem *item)
{
    if (m_blockItemChangedSignal)
        return;

    uint nodeHash = item->data(Qt::UserRole).toUInt();
    Q_ASSERT(nodeHash && containsNodeHash(nodeHash));
    ModelNode node = nodeForHash(nodeHash);

    ItemRow itemRow = itemRowForNode(node);
    if (item == itemRow.idItem) {
        try {
            if (ModelNode::isValidId(item->text()))
                node.setId(item->text());
            else
                item->setText(node.id());
        } catch (InvalidIdException &) {
            item->setText(node.id());
        }
    } else if (item == itemRow.visibilityItem) {
        bool invisible = (item->checkState() == Qt::Unchecked);

        node.setAuxiliaryData("invisible", invisible);
        propagateInvisible(node,isNodeInvisible(node));
    }
}

void NavigatorTreeModel::propagateInvisible(const ModelNode &node, const bool &invisible)
{
    QList <ModelNode> children = node.allDirectSubModelNodes();
    foreach (ModelNode child, children) {
        child.setAuxiliaryData("childOfInvisible",invisible);
        if (!child.auxiliaryData("invisible").toBool())
            propagateInvisible(child,invisible);
    }
}

ModelNode NavigatorTreeModel::nodeForHash(uint hash) const
{
    ModelNode node = m_nodeHash.value(hash);
    Q_ASSERT(node.isValid());
    return node;
}

bool NavigatorTreeModel::containsNodeHash(uint hash) const
{
    return m_nodeHash.contains(hash);
}

bool NavigatorTreeModel::containsNode(const ModelNode &node) const
{
    return m_nodeItemHash.contains(node);
}

NavigatorTreeModel::ItemRow NavigatorTreeModel::itemRowForNode(const ModelNode &node)
{
    Q_ASSERT(node.isValid());
    return m_nodeItemHash.value(node);
}

void NavigatorTreeModel::setView(AbstractView *view)
{
    m_view = view;
    addSubTree(view->rootModelNode());
}

void NavigatorTreeModel::clearView()
{
    m_view.clear();
    m_nodeHash.clear();
    m_nodeItemHash.clear();
    clear();
}

QModelIndex NavigatorTreeModel::indexForNode(const ModelNode &node) const
{
    Q_ASSERT(node.isValid());
    if (!containsNode(node))
        return QModelIndex();
    ItemRow row = m_nodeItemHash.value(node);
    return row.idItem->index();
}

ModelNode NavigatorTreeModel::nodeForIndex(const QModelIndex &index) const
{
    Q_ASSERT(index.isValid());
    uint hash = index.data(Qt::UserRole).toUInt();
    Q_ASSERT(hash);
    Q_ASSERT(containsNodeHash(hash));
    return nodeForHash(hash);
}

bool NavigatorTreeModel::isInTree(const ModelNode &node) const
{
    return m_nodeHash.keys().contains(qHash(node));
}

bool NavigatorTreeModel::isNodeInvisible(const QModelIndex &index) const
{
    return isNodeInvisible(nodeForIndex(index));
}

bool NavigatorTreeModel::isNodeInvisible(const ModelNode &node) const
{
    bool nodeInvisible = node.auxiliaryData("invisible").toBool();
    if (node.hasAuxiliaryData("childOfInvisible"))
        nodeInvisible = nodeInvisible || node.auxiliaryData("childOfInvisible").toBool();
    return nodeInvisible;
}

/**
  Adds node & all children to the visible tree hierarchy (if node should be visible at all).

  It always adds the node to the _end_ of the list of items.
  */
void NavigatorTreeModel::addSubTree(const ModelNode &node)
{
    Q_ASSERT(node.isValid());
    Q_ASSERT(!containsNodeHash(qHash(node)));

    // only add items that are in the modelNodeChildren list (that means, visible in the editor)
    if (!node.isRootNode()
        && !modelNodeChildren(node.parentProperty().parentModelNode()).contains(node)) {
        return;
    }

    ItemRow newRow = createItemRow(node);
    m_nodeHash.insert(qHash(node), node);
    m_nodeItemHash.insert(node, newRow);

    updateItemRow(node, newRow);

    foreach (const ModelNode &childNode, modelNodeChildren(node))
        addSubTree(childNode);

    // We assume that the node is always added to the _end_ of the property list.
    if (node.hasParentProperty()) {
        ItemRow parentRow = itemRowForNode(node.parentProperty().parentModelNode());
        if (parentRow.idItem)
            parentRow.idItem->appendRow(newRow.toList());
    } else {
        appendRow(newRow.toList());
    }
}

/**
  Deletes visual representation for the node (subtree).
  */
void NavigatorTreeModel::removeSubTree(const ModelNode &node)
{
    Q_ASSERT(node.isValid());

    if (!containsNode(node))
        return;

    QList<QStandardItem*> rowList;
    ItemRow itemRow = itemRowForNode(node);
    if (itemRow.idItem->parent()) {
        rowList = itemRow.idItem->parent()->takeRow(itemRow.idItem->row());
    }

    foreach (const ModelNode &node, modelNodeChildren(node)) {
        removeSubTree(node);
    }

    qDeleteAll(rowList);

    m_nodeHash.remove(qHash(node));
    m_nodeItemHash.remove(node);
}

QList<ModelNode> NavigatorTreeModel::modelNodeChildren(const ModelNode &parentNode)
{
    QList<ModelNode> children;
    if (QmlItemNode(parentNode).isValid())
        foreach (const QmlItemNode &childNode, QmlItemNode(parentNode).children())
            children << childNode.modelNode();
    return children;
}

// along the lines of QObject::blockSignals
bool NavigatorTreeModel::blockItemChangedSignal(bool block)
{
    bool oldValue = m_blockItemChangedSignal;
    m_blockItemChangedSignal = block;
    return oldValue;
}

}

<commit_msg>QmlDesigner.Navigator: removed tooltips<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "navigatortreemodel.h"

#include <nodeabstractproperty.h>
#include <nodelistproperty.h>
#include <abstractview.h>
#include <qmlitemnode.h>
#include <invalididexception.h>

#include <QMimeData>

namespace QmlDesigner {

NavigatorTreeModel::NavigatorTreeModel(QObject *parent)
    : QStandardItemModel(parent),
      m_blockItemChangedSignal(false)
{
    invisibleRootItem()->setFlags(Qt::NoItemFlags);

    #ifdef _LOCK_ITEMS_
    setColumnCount(3);
    #else
    setColumnCount(2);
    #endif

    setSupportedDragActions(Qt::LinkAction);

    connect(this, SIGNAL(itemChanged(QStandardItem*)),
            this, SLOT(handleChangedItem(QStandardItem*)));
}

NavigatorTreeModel::~NavigatorTreeModel()
{
}

Qt::DropActions NavigatorTreeModel::supportedDropActions() const
{
    return Qt::LinkAction;
}

QStringList NavigatorTreeModel::mimeTypes() const
{
     QStringList types;
     types << "application/vnd.modelnode.list";
     return types;
}

QMimeData *NavigatorTreeModel::mimeData(const QModelIndexList &indexList) const
{
     QMimeData *mimeData = new QMimeData();
     QByteArray encodedData;

     QSet<QModelIndex> rowAlreadyUsedSet;

     QDataStream stream(&encodedData, QIODevice::WriteOnly);

     foreach (const QModelIndex &index, indexList) {
         if (!index.isValid())
             continue;
         QModelIndex idIndex = index.sibling(index.row(), 0);
         if (rowAlreadyUsedSet.contains(idIndex))
             continue;

         rowAlreadyUsedSet.insert(idIndex);
         stream << idIndex.data(Qt::UserRole).toUInt();
     }

     mimeData->setData("application/vnd.modelnode.list", encodedData);

     return mimeData;
}

static bool isAnchestorInList(const ModelNode &node, const QList<ModelNode> &nodeList)
{
    foreach (const ModelNode &nodeInList, nodeList) {
        if (nodeInList.isAncestorOf(node))
            return true;
    }

    return false;
}

bool NavigatorTreeModel::dropMimeData(const QMimeData *data,
                  Qt::DropAction action,
                  int row,
                  int column,
                  const QModelIndex &parentIndex)
{
    if (action == Qt::IgnoreAction)
        return true;
    if (action != Qt::LinkAction)
        return false;
    if (!data->hasFormat("application/vnd.modelnode.list"))
        return false;
    if (column > 1)
        return false;
    if (parentIndex.model() != this)
        return false;

    QModelIndex parentIdIndex = parentIndex;
    parentIdIndex = parentIdIndex.sibling(parentIdIndex.row(), 0);

    Q_ASSERT(parentIdIndex.isValid());

    int targetIndex = 0;
    if (row > -1) {
        targetIndex = row;
    } else {
        targetIndex = rowCount(parentIdIndex);
    }

    QByteArray encodedData = data->data("application/vnd.modelnode.list");
    QDataStream stream(&encodedData, QIODevice::ReadOnly);

    QmlItemNode parentItemNode(nodeForIndex(parentIdIndex));

    QList<ModelNode> nodeList;

    while (!stream.atEnd()) {
        uint nodeHash;
        stream >> nodeHash;
        ModelNode node(nodeForHash(nodeHash));
        if (!node.isValid() || (parentItemNode == node) || node.isAncestorOf(parentItemNode))
            continue;
        nodeList.append(node);
    }

    RewriterTransaction transaction = m_view->beginRewriterTransaction();
    foreach (const ModelNode &node, nodeList) {
        if (!isAnchestorInList(node, nodeList)) {
            if (node.parentProperty().parentModelNode() != parentItemNode) {
                QmlItemNode itemNode(node);
                if (node != parentItemNode) {
                    itemNode.setParent(parentItemNode);
                }
            }

            if (node.parentProperty().isNodeListProperty()) {
                int index = node.parentProperty().toNodeListProperty().toModelNodeList().indexOf(node);
                if (index < targetIndex) { // item is first removed from oldIndex, then inserted at new index
                    --targetIndex;
                }
                if (index != targetIndex) {
                    node.parentProperty().toNodeListProperty().slide(index, targetIndex);
                }
            }
        }
    }

    propagateInvisible(parentItemNode, isNodeInvisible(parentItemNode));

    return false; // don't let the view do drag&drop on its own
}

NavigatorTreeModel::ItemRow NavigatorTreeModel::createItemRow(const ModelNode &node)
{
    Q_ASSERT(node.isValid());

    uint hash = qHash(node);

    QStandardItem *idItem = new QStandardItem;
    idItem->setDragEnabled(true);
    idItem->setDropEnabled(node.metaInfo().isContainer());
    idItem->setEditable(true);
    idItem->setData(hash, Qt::UserRole);

    #ifdef _LOCK_ITEMS_
    QStandardItem *lockItem = new QStandardItem;
    lockItem->setDragEnabled(true);
    lockItem->setDropEnabled(node.metaInfo().isContainer());
    lockItem->setEditable(false);
    lockItem->setCheckable(true);
    lockItem->setData(hash, Qt::UserRole);
    #endif

    QStandardItem *visibilityItem = new QStandardItem;
    visibilityItem->setDropEnabled(node.metaInfo().isContainer());
    visibilityItem->setCheckable(true);
    visibilityItem->setEditable(false);
    visibilityItem->setData(hash, Qt::UserRole);

    #ifdef _LOCK_ITEMS_
    return ItemRow(idItem, lockItem, visibilityItem);
    #else
    return ItemRow(idItem, visibilityItem);
    #endif
}

void NavigatorTreeModel::updateItemRow(const ModelNode &node, ItemRow items)
{
    bool blockSignal = blockItemChangedSignal(true);

    items.idItem->setText(node.id());
    items.visibilityItem->setCheckState(node.auxiliaryData("invisible").toBool() ? Qt::Unchecked : Qt::Checked);

    blockItemChangedSignal(blockSignal);
}

/**
  Update the information shown for a node / property
  */
void NavigatorTreeModel::updateItemRow(const ModelNode &node)
{
    if (!containsNode(node))
        return;

    updateItemRow(node, itemRowForNode(node));
}

/**
  Updates the sibling position of the item, depending on the position in the model.
  */
void NavigatorTreeModel::updateItemRowOrder(const ModelNode &node)
{
    if (!containsNode(node))
        return;

    ItemRow itemRow = itemRowForNode(node);
    int currentRow = itemRow.idItem->row();
    int newRow = currentRow;
    if (node.parentProperty().parentModelNode().isValid())
        newRow = modelNodeChildren(node.parentProperty().parentModelNode()).indexOf(node);
    Q_ASSERT(newRow >= 0);

    if (currentRow != newRow) {
        QStandardItem *parentIdItem = itemRow.idItem->parent();
        QList<QStandardItem*> items = parentIdItem->takeRow(currentRow);
        parentIdItem->insertRow(newRow, items);
    }
}

void NavigatorTreeModel::handleChangedItem(QStandardItem *item)
{
    if (m_blockItemChangedSignal)
        return;

    uint nodeHash = item->data(Qt::UserRole).toUInt();
    Q_ASSERT(nodeHash && containsNodeHash(nodeHash));
    ModelNode node = nodeForHash(nodeHash);

    ItemRow itemRow = itemRowForNode(node);
    if (item == itemRow.idItem) {
        try {
            if (ModelNode::isValidId(item->text()))
                node.setId(item->text());
            else
                item->setText(node.id());
        } catch (InvalidIdException &) {
            item->setText(node.id());
        }
    } else if (item == itemRow.visibilityItem) {
        bool invisible = (item->checkState() == Qt::Unchecked);

        node.setAuxiliaryData("invisible", invisible);
        propagateInvisible(node,isNodeInvisible(node));
    }
}

void NavigatorTreeModel::propagateInvisible(const ModelNode &node, const bool &invisible)
{
    QList <ModelNode> children = node.allDirectSubModelNodes();
    foreach (ModelNode child, children) {
        child.setAuxiliaryData("childOfInvisible",invisible);
        if (!child.auxiliaryData("invisible").toBool())
            propagateInvisible(child,invisible);
    }
}

ModelNode NavigatorTreeModel::nodeForHash(uint hash) const
{
    ModelNode node = m_nodeHash.value(hash);
    Q_ASSERT(node.isValid());
    return node;
}

bool NavigatorTreeModel::containsNodeHash(uint hash) const
{
    return m_nodeHash.contains(hash);
}

bool NavigatorTreeModel::containsNode(const ModelNode &node) const
{
    return m_nodeItemHash.contains(node);
}

NavigatorTreeModel::ItemRow NavigatorTreeModel::itemRowForNode(const ModelNode &node)
{
    Q_ASSERT(node.isValid());
    return m_nodeItemHash.value(node);
}

void NavigatorTreeModel::setView(AbstractView *view)
{
    m_view = view;
    addSubTree(view->rootModelNode());
}

void NavigatorTreeModel::clearView()
{
    m_view.clear();
    m_nodeHash.clear();
    m_nodeItemHash.clear();
    clear();
}

QModelIndex NavigatorTreeModel::indexForNode(const ModelNode &node) const
{
    Q_ASSERT(node.isValid());
    if (!containsNode(node))
        return QModelIndex();
    ItemRow row = m_nodeItemHash.value(node);
    return row.idItem->index();
}

ModelNode NavigatorTreeModel::nodeForIndex(const QModelIndex &index) const
{
    Q_ASSERT(index.isValid());
    uint hash = index.data(Qt::UserRole).toUInt();
    Q_ASSERT(hash);
    Q_ASSERT(containsNodeHash(hash));
    return nodeForHash(hash);
}

bool NavigatorTreeModel::isInTree(const ModelNode &node) const
{
    return m_nodeHash.keys().contains(qHash(node));
}

bool NavigatorTreeModel::isNodeInvisible(const QModelIndex &index) const
{
    return isNodeInvisible(nodeForIndex(index));
}

bool NavigatorTreeModel::isNodeInvisible(const ModelNode &node) const
{
    bool nodeInvisible = node.auxiliaryData("invisible").toBool();
    if (node.hasAuxiliaryData("childOfInvisible"))
        nodeInvisible = nodeInvisible || node.auxiliaryData("childOfInvisible").toBool();
    return nodeInvisible;
}

/**
  Adds node & all children to the visible tree hierarchy (if node should be visible at all).

  It always adds the node to the _end_ of the list of items.
  */
void NavigatorTreeModel::addSubTree(const ModelNode &node)
{
    Q_ASSERT(node.isValid());
    Q_ASSERT(!containsNodeHash(qHash(node)));

    // only add items that are in the modelNodeChildren list (that means, visible in the editor)
    if (!node.isRootNode()
        && !modelNodeChildren(node.parentProperty().parentModelNode()).contains(node)) {
        return;
    }

    ItemRow newRow = createItemRow(node);
    m_nodeHash.insert(qHash(node), node);
    m_nodeItemHash.insert(node, newRow);

    updateItemRow(node, newRow);

    foreach (const ModelNode &childNode, modelNodeChildren(node))
        addSubTree(childNode);

    // We assume that the node is always added to the _end_ of the property list.
    if (node.hasParentProperty()) {
        ItemRow parentRow = itemRowForNode(node.parentProperty().parentModelNode());
        if (parentRow.idItem)
            parentRow.idItem->appendRow(newRow.toList());
    } else {
        appendRow(newRow.toList());
    }
}

/**
  Deletes visual representation for the node (subtree).
  */
void NavigatorTreeModel::removeSubTree(const ModelNode &node)
{
    Q_ASSERT(node.isValid());

    if (!containsNode(node))
        return;

    QList<QStandardItem*> rowList;
    ItemRow itemRow = itemRowForNode(node);
    if (itemRow.idItem->parent()) {
        rowList = itemRow.idItem->parent()->takeRow(itemRow.idItem->row());
    }

    foreach (const ModelNode &node, modelNodeChildren(node)) {
        removeSubTree(node);
    }

    qDeleteAll(rowList);

    m_nodeHash.remove(qHash(node));
    m_nodeItemHash.remove(node);
}

QList<ModelNode> NavigatorTreeModel::modelNodeChildren(const ModelNode &parentNode)
{
    QList<ModelNode> children;
    if (QmlItemNode(parentNode).isValid())
        foreach (const QmlItemNode &childNode, QmlItemNode(parentNode).children())
            children << childNode.modelNode();
    return children;
}

// along the lines of QObject::blockSignals
bool NavigatorTreeModel::blockItemChangedSignal(bool block)
{
    bool oldValue = m_blockItemChangedSignal;
    m_blockItemChangedSignal = block;
    return oldValue;
}

}

<|endoftext|>