/*
 * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
 * http://www.gnu.org/licenses/lgpl-3.0.html
 *
 * $Revision: 5565 $
 * $Id: compiler.cpp 5565 2009-05-03 03:47:25Z biplab $
 * $HeadURL: svn+ssh://jenslody@svn.berlios.de/svnroot/repos/codeblocks/trunk/src/sdk/compiler.cpp $
 */

#include "sdk_precomp.h"

#ifndef CB_PRECOMP
    #include "cbexception.h"
    #include "compiler.h"
    #include "manager.h"
    #include "logmanager.h"
    #include "configmanager.h"
    #include "macrosmanager.h"
    #include "globals.h"
    #include "compilerfactory.h"

    #include <wx/intl.h>
    #include <wx/regex.h>
#endif

#include "compilercommandgenerator.h"
#include <wx/filefn.h>
#include <wx/arrimpl.cpp>
WX_DEFINE_OBJARRAY(RegExArray);

// static
wxArrayString Compiler::m_CompilerIDs; // map to guarantee unique IDs

// common regex that can be used by the different compiler for matching compiler output
// it can be used in the patterns for warnings, errors, ...
// NOTE : it is an approximation (for example the ':' can appear anywhere and several times)
const wxString Compiler::FilePathWithSpaces = _T("[][{}() \t#%$~A-Za-z0-9_:+/\\.-]+");

// version of compiler settings
// when this is different from what is saved in the config, a message appears
// to the user saying that default settings have changed and asks him if he wants to
// use his own settings or the new defaults
const wxString CompilerSettingsVersion = _T("0.0.2");

CompilerSwitches::CompilerSwitches()
{   // default based upon gnu
    includeDirs = _T("-I");
    libDirs = _T("-L");
    linkLibs = _T("-l");
    defines = _T("-D");
    genericSwitch = _T("-");
    objectExtension = _T("o");
    needDependencies = true;
    forceFwdSlashes = false;
    forceCompilerUseQuotes = false;
    forceLinkerUseQuotes = false;
    logging = clogSimple;
    libPrefix = _T("lib");
    libExtension = _T("a");
    linkerNeedsLibPrefix = false;
    linkerNeedsLibExtension = false;
    supportsPCH = true;
    PCHExtension = _T("h.gch");
    UseFlatObjects = false;
    UseFullSourcePaths = false;
} // end of constructor


wxString Compiler::CommandTypeDescriptions[ctCount] =
{
    // These are the strings that describe each CommandType enumerator...
    // No need to say that it must have the same order as the enumerators!
    _("Compile single file to object file"),
    _("Generate dependencies for file"),
    _("Compile Win32 resource file"),
    _("Link object files to executable"),
    _("Link object files to console executable"),
    _("Link object files to dynamic library"),
    _("Link object files to static library"),
    _("Link object files to native executable")
};

Compiler::Compiler(const wxString& name, const wxString& ID, const wxString& parentID)
    : m_Name(name),
    m_ID(ID.Lower()),
    m_ParentID(parentID.Lower()),
    m_pGenerator(0),
    m_Valid(false),
    m_NeedValidityCheck(true),
    m_Mirrored(false)
{
    //ctor
    MakeValidID();

    m_Switches.supportsPCH = false;
    m_Switches.forceFwdSlashes = false;
    m_VersionString = wxEmptyString;

    #if wxCHECK_VERSION(2, 9, 0)
    Manager::Get()->GetLogManager()->DebugLog(F(_T("Added compiler \"%s\""), m_Name.wx_str()));
    #else
    Manager::Get()->GetLogManager()->DebugLog(F(_T("Added compiler \"%s\""), m_Name.c_str()));
    #endif
}

Compiler::Compiler(const Compiler& other)
    : CompileOptionsBase(other),
    m_ParentID(other.m_ParentID.IsEmpty() ? other.m_ID : other.m_ParentID),
    m_pGenerator(0),
    m_Mirror(other.m_Mirror),
    m_Mirrored(other.m_Mirrored)
{
    m_Name = _("Copy of ") + other.m_Name;
    // generate unique ID
    // note that this copy constructor is protected and can only be called
    // by our friend CompilerFactory. It knows what it's doing ;)
    wxDateTime now = wxDateTime::UNow();
    m_ID = now.Format(_T("%c"), wxDateTime::CET);
    MakeValidID();

    m_MasterPath = other.m_MasterPath;
    m_Programs = other.m_Programs;
    m_Switches = other.m_Switches;
    m_Options = other.m_Options;
    m_IncludeDirs = other.m_IncludeDirs;
    m_LibDirs = other.m_LibDirs;
    m_CompilerOptions = other.m_CompilerOptions;
    m_LinkerOptions = other.m_LinkerOptions;
    m_LinkLibs = other.m_LinkLibs;
    m_CmdsBefore = other.m_CmdsBefore;
    m_CmdsAfter = other.m_CmdsAfter;
    m_RegExes = other.m_RegExes;
    m_VersionString = other.m_VersionString;
    for (int i = 0; i < ctCount; ++i)
    {
        m_Commands[(CommandType)i] = other.m_Commands[(CommandType)i];
    }

    m_Valid = other.m_Valid;
    m_NeedValidityCheck = other.m_NeedValidityCheck;
}

Compiler::~Compiler()
{
    //dtor
    delete m_pGenerator;
}

bool Compiler::IsValid()
{
    if (!m_NeedValidityCheck)
        return m_Valid;

    if (m_MasterPath.IsEmpty())
        return true; // still initializing, don't try to test now

    m_NeedValidityCheck = false;

    if (!SupportsCurrentPlatform())
    {
        m_Valid = false;
        return false;
    }

    wxString tmp = m_MasterPath + _T("/bin/") + m_Programs.C;
    Manager::Get()->GetMacrosManager()->ReplaceMacros(tmp);
    m_Valid = wxFileExists(tmp);
    if (!m_Valid)
    {   // and try witout appending the 'bin'
        tmp = m_MasterPath + _T("/") + m_Programs.C;
        Manager::Get()->GetMacrosManager()->ReplaceMacros(tmp);
        m_Valid = wxFileExists(tmp);
    }
    if (!m_Valid)
    {
        // look in extra paths too
        for (size_t i = 0; i < m_ExtraPaths.GetCount(); ++i)
        {
            tmp = m_ExtraPaths[i] + _T("/") + m_Programs.C;
            Manager::Get()->GetMacrosManager()->ReplaceMacros(tmp);
            m_Valid = wxFileExists(tmp);
            if (m_Valid)
                break;
        }
    }
    return m_Valid;
}

void Compiler::MakeValidID()
{
    // basically, make it XML-element compatible
    // only allow a-z, 0-9 and _
    // (it is already lowercase)
    // any non-conformant character will be removed

    wxString newID;
    if (m_ID.IsEmpty())
        m_ID = m_Name;

    size_t pos = 0;
    while (pos < m_ID.Length())
    {
        wxChar ch = m_ID[pos];
        if (wxIsalnum(ch) || ch == _T('_'))
        {
            // valid character
            newID.Append(ch);
        }
        else if (wxIsspace(ch))
        {
            // convert spaces to underscores
            newID.Append(_T('_'));
        }
        ++pos;
    }

    // make sure it's not starting with a number.
    // if it is, prepend "cb"
    if (wxIsdigit(newID.GetChar(0)))
        newID.Prepend(_T("cb"));

    if (newID.IsEmpty())
    {
        // empty? wtf?
        cbThrow(_T("Can't create a valid compiler ID for ") + m_Name);
    }
    m_ID = newID.Lower();

    // check for unique ID
    if (!IsUniqueID(m_ID))
        cbThrow(_T("Compiler ID already exists for ") + m_Name);
    m_CompilerIDs.Add(m_ID);
}

CompilerCommandGenerator* Compiler::GetCommandGenerator()
{
    return new CompilerCommandGenerator;
}

void Compiler::Init(cbProject* project)
{
    if (!m_pGenerator)
        m_pGenerator = GetCommandGenerator();
    m_pGenerator->Init(project);
}

void Compiler::GenerateCommandLine(wxString& macro,
                                    ProjectBuildTarget* target,
                                    ProjectFile* pf,
                                    const wxString& file,
                                    const wxString& object,
                                    const wxString& FlatObject,
                                    const wxString& deps)
{
    if (!m_pGenerator)
        cbThrow(_T("Compiler::Init() not called or generator invalid!"));
    m_pGenerator->GenerateCommandLine(macro, target, pf, file, object, FlatObject, deps);
}

const wxArrayString& Compiler::GetCompilerSearchDirs(ProjectBuildTarget* target)
{
    static wxArrayString retIfError;
    retIfError.Clear();
    if (!m_pGenerator)
        return retIfError;

    return m_pGenerator->GetCompilerSearchDirs(target);
}

const wxArrayString& Compiler::GetLinkerSearchDirs(ProjectBuildTarget* target)
{
    static wxArrayString retIfError;
    retIfError.Clear();
    if (!m_pGenerator)
        return retIfError;

    return m_pGenerator->GetLinkerSearchDirs(target);
}

const wxString& Compiler::GetCommand(CommandType ct, const wxString& fileExtension) const
{
    size_t catchAll = 0;
    const CompilerToolsVector& vec = m_Commands[ct];

    if (!fileExtension.IsEmpty())
    {
        for (size_t i = 0; i < vec.size(); ++i)
        {
            if (vec[i].extensions.GetCount() == 0)
            {
                catchAll = i;
                continue;
            }
            for (size_t n = 0; n < vec[i].extensions.GetCount(); ++n)
            {
                if (vec[i].extensions[n] == fileExtension)
                {
                    return vec[i].command;
                }
            }
        }
    }
    return vec[catchAll].command;
}

const CompilerTool& Compiler::GetCompilerTool(CommandType ct, const wxString& fileExtension) const
{
    size_t catchAll = 0;
    const CompilerToolsVector& vec = m_Commands[ct];

    if (!fileExtension.IsEmpty())
    {
        for (size_t i = 0; i < vec.size(); ++i)
        {
            if (vec[i].extensions.GetCount() == 0)
            {
                catchAll = i;
                continue;
            }
            for (size_t n = 0; n < vec[i].extensions.GetCount(); ++n)
            {
                if (vec[i].extensions[n] == fileExtension)
                {
                    return vec[i];
                }
            }
        }
    }
    return vec[catchAll];
}

void Compiler::MirrorCurrentSettings()
{
    // run just once
    if (m_Mirrored)
        return;

    // keep the current settings safe
    // so we can compare them when saving: this way we can only save what's
    // different from the defaults

    m_Mirror.Name = m_Name;
    m_Mirror.MasterPath = m_MasterPath;
    m_Mirror.ExtraPaths = m_ExtraPaths;
    for (int i = 0; i < ctCount; ++i)
        m_Mirror.Commands[i] = m_Commands[i];
    m_Mirror.Programs = m_Programs;
    m_Mirror.Switches = m_Switches;
    m_Mirror.Options = m_Options;
    m_Mirror.RegExes = m_RegExes;

    m_Mirror.CompilerOptions_ = m_CompilerOptions;
    m_Mirror.LinkerOptions = m_LinkerOptions;
    m_Mirror.IncludeDirs = m_IncludeDirs;
    m_Mirror.ResIncludeDirs = m_ResIncludeDirs;
    m_Mirror.LibDirs = m_LibDirs;
    m_Mirror.LinkLibs = m_LinkLibs;
    m_Mirror.CmdsBefore = m_CmdsBefore;
    m_Mirror.CmdsAfter = m_CmdsAfter;

    m_Mirrored = true;
}

void Compiler::SaveSettings(const wxString& baseKey)
{
    ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("compiler"));

    // save settings version
    cfg->Write(_T("settings_version"), CompilerSettingsVersion);

    wxString tmp;

    // delete old-style keys (using integer IDs)
    tmp.Printf(_T("%s/set%3.3d"), baseKey.c_str(), CompilerFactory::GetCompilerIndex(this) + 1);
    cfg->DeleteSubPath(tmp);

    tmp.Printf(_T("%s/%s"), baseKey.c_str(), m_ID.c_str());

    cfg->Write(tmp + _T("/name"), m_Name);
    cfg->Write(tmp + _T("/parent"), m_ParentID, true);

    if (m_Mirror.CompilerOptions_ != m_CompilerOptions)
    {
        wxString key = GetStringFromArray(m_CompilerOptions);
        cfg->Write(tmp + _T("/compiler_options"), key, false);
    }
    if (m_Mirror.LinkerOptions != m_LinkerOptions)
    {
        wxString key = GetStringFromArray(m_LinkerOptions);
        cfg->Write(tmp + _T("/linker_options"), key, false);
    }
    if (m_Mirror.IncludeDirs != m_IncludeDirs)
    {
        wxString key = GetStringFromArray(m_IncludeDirs);
        cfg->Write(tmp + _T("/include_dirs"), key, false);
    }
    if (m_Mirror.ResIncludeDirs != m_ResIncludeDirs)
    {
        wxString key = GetStringFromArray(m_ResIncludeDirs);
        cfg->Write(tmp + _T("/res_include_dirs"), key, false);
    }
    if (m_Mirror.LibDirs != m_LibDirs)
    {
        wxString key = GetStringFromArray(m_LibDirs);
        cfg->Write(tmp + _T("/library_dirs"), key, false);
    }
    if (m_Mirror.LinkLibs != m_LinkLibs)
    {
        wxString key = GetStringFromArray(m_LinkLibs);
        cfg->Write(tmp + _T("/libraries"), key, false);
    }
    if (m_Mirror.CmdsBefore != m_CmdsBefore)
    {
        wxString key = GetStringFromArray(m_CmdsBefore);
        cfg->Write(tmp + _T("/commands_before"), key, true);
    }
    if (m_Mirror.CmdsAfter != m_CmdsAfter)
    {
        wxString key = GetStringFromArray(m_CmdsAfter);
        cfg->Write(tmp + _T("/commands_after"), key, true);
    }

    if (m_Mirror.MasterPath != m_MasterPath)
        cfg->Write(tmp + _T("/master_path"), m_MasterPath, true);
    if (m_Mirror.ExtraPaths != m_ExtraPaths)
        cfg->Write(tmp + _T("/extra_paths"), GetStringFromArray(m_ExtraPaths, _T(";")), true);
    if (m_Mirror.Programs.C != m_Programs.C)
        cfg->Write(tmp + _T("/c_compiler"), m_Programs.C, true);
    if (m_Mirror.Programs.CPP != m_Programs.CPP)
        cfg->Write(tmp + _T("/cpp_compiler"), m_Programs.CPP, true);
    if (m_Mirror.Programs.LD != m_Programs.LD)
        cfg->Write(tmp + _T("/linker"), m_Programs.LD, true);
    if (m_Mirror.Programs.LIB != m_Programs.LIB)
        cfg->Write(tmp + _T("/lib_linker"), m_Programs.LIB, true);
    if (m_Mirror.Programs.WINDRES != m_Programs.WINDRES)
        cfg->Write(tmp + _T("/res_compiler"), m_Programs.WINDRES, true);
    if (m_Mirror.Programs.MAKE != m_Programs.MAKE)
        cfg->Write(tmp + _T("/make"), m_Programs.MAKE, true);
    if (m_Mirror.Programs.DBG != m_Programs.DBG)
        cfg->Write(tmp + _T("/debugger"), m_Programs.DBG, true);

    for (int i = 0; i < ctCount; ++i)
    {
        for (size_t n = 0; n < m_Commands[i].size(); ++n)
        {
            if (n >= m_Mirror.Commands[i].size() || m_Mirror.Commands[i][n] != m_Commands[i][n])
            {
                wxString key = wxString::Format(_T("%s/macros/%s/tool%d/"), tmp.c_str(), CommandTypeDescriptions[i].c_str(), n);
                cfg->Write(key + _T("command"), m_Commands[i][n].command);
                cfg->Write(key + _T("extensions"), m_Commands[i][n].extensions);
                cfg->Write(key + _T("generatedFiles"), m_Commands[i][n].generatedFiles);
            }
        }
    }

    // switches
    if (m_Mirror.Switches.includeDirs != m_Switches.includeDirs)
        cfg->Write(tmp + _T("/switches/includes"), m_Switches.includeDirs, true);
    if (m_Mirror.Switches.libDirs != m_Switches.libDirs)
        cfg->Write(tmp + _T("/switches/libs"), m_Switches.libDirs, true);
    if (m_Mirror.Switches.linkLibs != m_Switches.linkLibs)
        cfg->Write(tmp + _T("/switches/link"), m_Switches.linkLibs, true);
    if (m_Mirror.Switches.defines != m_Switches.defines)
        cfg->Write(tmp + _T("/switches/define"), m_Switches.defines, true);
    if (m_Mirror.Switches.genericSwitch != m_Switches.genericSwitch)
        cfg->Write(tmp + _T("/switches/generic"), m_Switches.genericSwitch, true);
    if (m_Mirror.Switches.objectExtension != m_Switches.objectExtension)
        cfg->Write(tmp + _T("/switches/objectext"), m_Switches.objectExtension, true);
    if (m_Mirror.Switches.needDependencies != m_Switches.needDependencies)
        cfg->Write(tmp + _T("/switches/deps"), m_Switches.needDependencies);
    if (m_Mirror.Switches.forceCompilerUseQuotes != m_Switches.forceCompilerUseQuotes)
        cfg->Write(tmp + _T("/switches/forceCompilerQuotes"), m_Switches.forceCompilerUseQuotes);
    if (m_Mirror.Switches.forceLinkerUseQuotes != m_Switches.forceLinkerUseQuotes)
        cfg->Write(tmp + _T("/switches/forceLinkerQuotes"), m_Switches.forceLinkerUseQuotes);
    if (m_Mirror.Switches.logging != m_Switches.logging)
        cfg->Write(tmp + _T("/switches/logging"), m_Switches.logging);
    if (m_Mirror.Switches.libPrefix != m_Switches.libPrefix)
        cfg->Write(tmp + _T("/switches/libPrefix"), m_Switches.libPrefix, true);
    if (m_Mirror.Switches.libExtension != m_Switches.libExtension)
        cfg->Write(tmp + _T("/switches/libExtension"), m_Switches.libExtension, true);
    if (m_Mirror.Switches.linkerNeedsLibPrefix != m_Switches.linkerNeedsLibPrefix)
        cfg->Write(tmp + _T("/switches/linkerNeedsLibPrefix"), m_Switches.linkerNeedsLibPrefix);
    if (m_Mirror.Switches.linkerNeedsLibExtension != m_Switches.linkerNeedsLibExtension)
        cfg->Write(tmp + _T("/switches/linkerNeedsLibExtension"), m_Switches.linkerNeedsLibExtension);
    if (m_Mirror.Switches.forceFwdSlashes != m_Switches.forceFwdSlashes)
        cfg->Write(tmp + _T("/switches/forceFwdSlashes"), m_Switches.forceFwdSlashes);
    if (m_Mirror.Switches.supportsPCH != m_Switches.supportsPCH)
        cfg->Write(tmp + _T("/switches/supportsPCH"), m_Switches.supportsPCH);
    if (m_Mirror.Switches.PCHExtension != m_Switches.PCHExtension)
        cfg->Write(tmp + _T("/switches/pchExtension"), m_Switches.PCHExtension);
    if (m_Mirror.Switches.UseFlatObjects != m_Switches.UseFlatObjects)
        cfg->Write(tmp + _T("/switches/UseFlatObjects"), m_Switches.UseFlatObjects);
    if (m_Mirror.Switches.UseFullSourcePaths != m_Switches.UseFullSourcePaths)
        cfg->Write(tmp + _T("/switches/UseFullSourcePaths"), m_Switches.UseFullSourcePaths);

    // regexes
    cfg->DeleteSubPath(tmp + _T("/regex"));
    wxString group;
    for (size_t i = 0; i < m_RegExes.Count(); ++i)
    {
        if (i < m_Mirror.RegExes.GetCount() && m_Mirror.RegExes[i] == m_RegExes[i])
            continue;

        group.Printf(_T("%s/regex/re%3.3d"), tmp.c_str(), i + 1);
        RegExStruct& rs = m_RegExes[i];
        cfg->Write(group + _T("/description"), rs.desc, true);
        if (rs.lt != 0)
            cfg->Write(group + _T("/type"), rs.lt);
            cfg->Write(group + _T("/regex"), rs.regex, true);
        if (rs.msg[0] != 0)
            cfg->Write(group + _T("/msg1"), rs.msg[0]);
        if (rs.msg[1] != 0)
            cfg->Write(group + _T("/msg2"), rs.msg[1]);
        if (rs.msg[2] != 0)
            cfg->Write(group + _T("/msg3"), rs.msg[2]);
        if (rs.filename != 0)
            cfg->Write(group + _T("/filename"), rs.filename);
        if (rs.line != 0)
            cfg->Write(group + _T("/line"), rs.line);
    }

    // custom vars
    wxString configpath = tmp + _T("/custom_variables/");
    cfg->DeleteSubPath(configpath);
    const StringHash& v = GetAllVars();
    for (StringHash::const_iterator it = v.begin(); it != v.end(); ++it)
    {
        cfg->Write(configpath + it->first, it->second);
    }
}

void Compiler::LoadSettings(const wxString& baseKey)
{
    // before loading any compiler settings, keep the current settings safe
    // so we can compare them when saving: this way we can only save what's
    // different from the defaults
    MirrorCurrentSettings();

    ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("compiler"));

    // read settings version
    wxString version = cfg->Read(_T("settings_version"));
    bool versionMismatch = version != CompilerSettingsVersion;

    wxString tmp;

    // if using old-style keys (using integer IDs), notify user about the changes
    static bool saidAboutCompilerIDs = false;
    tmp.Printf(_T("%s/set%3.3d"), baseKey.c_str(), CompilerFactory::GetCompilerIndex(this) + 1);
    if (cfg->Exists(tmp + _T("/name")))
    {
        if (!saidAboutCompilerIDs)
        {
            saidAboutCompilerIDs = true;
            cbMessageBox(_("Compilers now use unique names instead of integer IDs.\n"
                            "Projects will be updated accordingly on load, mostly automatic."),
                            _("Information"),
                            wxICON_INFORMATION);
        }
        // at this point, we 'll be using the old style configuration to load settings
    }
    else // it's OK to use new style
        tmp.Printf(_T("%s/%s"), baseKey.c_str(), m_ID.c_str());

    if (!cfg->Exists(tmp + _T("/name")))
        return;

    wxString sep = wxFileName::GetPathSeparator();

//    if (m_ID > 255) // name changes are allowed only for user compilers
    m_Name = cfg->Read(tmp + _T("/name"), m_Name);

    m_MasterPath = cfg->Read(tmp + _T("/master_path"), m_MasterPath);
    m_ExtraPaths = GetArrayFromString(cfg->Read(tmp + _T("/extra_paths"), _T("")), _T(";"));
    m_Programs.C = cfg->Read(tmp + _T("/c_compiler"), m_Programs.C);
    m_Programs.CPP = cfg->Read(tmp + _T("/cpp_compiler"), m_Programs.CPP);
    m_Programs.LD = cfg->Read(tmp + _T("/linker"), m_Programs.LD);
    m_Programs.LIB = cfg->Read(tmp + _T("/lib_linker"), m_Programs.LIB);
    m_Programs.WINDRES = cfg->Read(tmp + _T("/res_compiler"), m_Programs.WINDRES);
    m_Programs.MAKE = cfg->Read(tmp + _T("/make"), m_Programs.MAKE);
    m_Programs.DBG = cfg->Read(tmp + _T("/debugger"), m_Programs.DBG);

    // set member variable containing the version string with the configurated toolchain executables, not only
    // with the default ones, otherwise we might have an empty version-string
    // Some MinGW installations do not includee "mingw32-gcc" !!
    SetVersionString();

    SetCompilerOptions(GetArrayFromString(cfg->Read(tmp + _T("/compiler_options"), wxEmptyString)));
    SetLinkerOptions(GetArrayFromString(cfg->Read(tmp + _T("/linker_options"), wxEmptyString)));
    SetIncludeDirs(GetArrayFromString(cfg->Read(tmp + _T("/include_dirs"), wxEmptyString)));
    SetResourceIncludeDirs(GetArrayFromString(cfg->Read(tmp + _T("/res_include_dirs"), wxEmptyString)));
    SetLibDirs(GetArrayFromString(cfg->Read(tmp + _T("/library_dirs"), wxEmptyString)));
    SetLinkLibs(GetArrayFromString(cfg->Read(tmp + _T("/libraries"), _T(""))));
    SetCommandsBeforeBuild(GetArrayFromString(cfg->Read(tmp + _T("/commands_before"), wxEmptyString)));
    SetCommandsAfterBuild(GetArrayFromString(cfg->Read(tmp + _T("/commands_after"), wxEmptyString)));

    for (int i = 0; i < ctCount; ++i)
    {
        wxArrayString keys = cfg->EnumerateSubPaths(tmp + _T("/macros/") + CommandTypeDescriptions[i]);
        for (size_t n = 0; n < keys.size(); ++n)
        {
            unsigned long index = 0;
            if (keys[n].Mid(4).ToULong(&index)) // skip 'tool'
            {
                while (index >= m_Commands[i].size())
                    m_Commands[i].push_back(CompilerTool());
                CompilerTool& tool = m_Commands[i][index];

                wxString key = wxString::Format(_T("%s/macros/%s/tool%lu/"), tmp.c_str(), CommandTypeDescriptions[i].c_str(), index);
                tool.command = cfg->Read(key + _T("command"));
                tool.extensions = cfg->ReadArrayString(key + _T("extensions"));
                tool.generatedFiles = cfg->ReadArrayString(key + _T("generatedFiles"));
            }
        }
    }

    // switches
    m_Switches.includeDirs = cfg->Read(tmp + _T("/switches/includes"), m_Switches.includeDirs);
    m_Switches.libDirs = cfg->Read(tmp + _T("/switches/libs"), m_Switches.libDirs);
    m_Switches.linkLibs = cfg->Read(tmp + _T("/switches/link"), m_Switches.linkLibs);
    m_Switches.defines = cfg->Read(tmp + _T("/switches/define"), m_Switches.defines);
    m_Switches.genericSwitch = cfg->Read(tmp + _T("/switches/generic"), m_Switches.genericSwitch);
    m_Switches.objectExtension = cfg->Read(tmp + _T("/switches/objectext"), m_Switches.objectExtension);
    m_Switches.needDependencies = cfg->ReadBool(tmp + _T("/switches/deps"), m_Switches.needDependencies);
    m_Switches.forceCompilerUseQuotes = cfg->ReadBool(tmp + _T("/switches/forceCompilerQuotes"), m_Switches.forceCompilerUseQuotes);
    m_Switches.forceLinkerUseQuotes = cfg->ReadBool(tmp + _T("/switches/forceLinkerQuotes"), m_Switches.forceLinkerUseQuotes);
    m_Switches.logging = (CompilerLoggingType)cfg->ReadInt(tmp + _T("/switches/logging"), m_Switches.logging);
    m_Switches.libPrefix = cfg->Read(tmp + _T("/switches/libPrefix"), m_Switches.libPrefix);
    m_Switches.libExtension = cfg->Read(tmp + _T("/switches/libExtension"), m_Switches.libExtension);
    m_Switches.linkerNeedsLibPrefix = cfg->ReadBool(tmp + _T("/switches/linkerNeedsLibPrefix"), m_Switches.linkerNeedsLibPrefix);
    m_Switches.linkerNeedsLibExtension = cfg->ReadBool(tmp + _T("/switches/linkerNeedsLibExtension"), m_Switches.linkerNeedsLibExtension);
    m_Switches.forceFwdSlashes = cfg->ReadBool(tmp + _T("/switches/forceFwdSlashes"), m_Switches.forceFwdSlashes);
    m_Switches.supportsPCH = cfg->ReadBool(tmp + _T("/switches/supportsPCH"), m_Switches.supportsPCH);
    m_Switches.PCHExtension = cfg->Read(tmp + _T("/switches/pchExtension"), m_Switches.PCHExtension);
    m_Switches.UseFlatObjects = cfg->ReadBool(tmp + _T("/switches/UseFlatObjects"), m_Switches.UseFlatObjects);
    m_Switches.UseFullSourcePaths = cfg->ReadBool(tmp + _T("/switches/UseFullSourcePaths"), m_Switches.UseFullSourcePaths);

    // regexes

    // because we 're only saving changed regexes, we can't just iterate like before.
    // instead, we must iterate all child-keys and deduce the regex index number from
    // the key name
    wxArrayString keys = cfg->EnumerateSubPaths(tmp + _T("/regex/"));
    wxString group;
    long index = 0;
    for (size_t i = 0; i < keys.GetCount(); ++i)
    {
        wxString key = keys[i];

        // reNNN
        if (!key.StartsWith(_T("re")))
            continue;
        key.Remove(0, 2);
        if (!key.ToLong(&index, 10))
            continue;

        // 'index' now holds the regex index.
        // read everything and either assign it to an existing regex
        // if the index exists, or add a new regex

        group.Printf(_T("%s/regex/re%3.3ld"), tmp.c_str(), index);
        if (!cfg->Exists(group+_T("/description")))
            continue;

        RegExStruct rs;
        rs.desc = cfg->Read(group + _T("/description"));
        rs.lt = (CompilerLineType)cfg->ReadInt(group + _T("/type"), 0);
        rs.regex = cfg->Read(group + _T("/regex"));
        rs.msg[0] = cfg->ReadInt(group + _T("/msg1"), 0);
        rs.msg[1] = cfg->ReadInt(group + _T("/msg2"), 0);
        rs.msg[2] = cfg->ReadInt(group + _T("/msg3"), 0);
        rs.filename = cfg->ReadInt(group + _T("/filename"), 0);
        rs.line = cfg->ReadInt(group + _T("/line"), 0);

        if (index <= (long)m_RegExes.GetCount())
            m_RegExes[index - 1] = rs;
        else
            m_RegExes.Add(rs);
    }

    // custom vars
    wxString configpath = tmp + _T("/custom_variables/");
    UnsetAllVars();
    wxArrayString list = cfg->EnumerateKeys(configpath);
    for (unsigned int i = 0; i < list.GetCount(); ++i)
        SetVar(list[i], cfg->Read(configpath + _T('/') + list[i]), false);

    if (versionMismatch)
    {
        wxString msg;
        msg << _("Some compiler settings defaults have changed in this version.\n"
                "It is recommended that you allow updating of your settings to "
                "the new defaults.\n"
                "Only disallow this if you don't want to lose any customizations "
                "you have done to this compiler's settings.\n\n"
                "Note that the only settings that are affected are those found in "
                "\"Advanced compiler options\"...\n\n"
                "Do you want to update your current settings to the new defaults?");
        // don't ask if the compiler is not valid (i.e. not installed), just update
        if (!IsValid() || cbMessageBox(msg, m_Name, wxICON_QUESTION | wxYES_NO) == wxID_YES)
        {
            for (int i = 0; i < ctCount; ++i)
                m_Commands[i] = m_Mirror.Commands[i];
            m_Switches = m_Mirror.Switches;
            m_Options = m_Mirror.Options;
            m_RegExes = m_Mirror.RegExes;
        }
    }
}

CompilerLineType Compiler::CheckForWarningsAndErrors(const wxString& line)
{
    m_ErrorFilename.Clear();
    m_ErrorLine.Clear();
    m_Error.Clear();

    for (size_t i = 0; i < m_RegExes.Count(); ++i)
    {
        RegExStruct& rs = m_RegExes[i];
        if (rs.regex.IsEmpty())
            continue;
        wxRegEx regex(rs.regex);
        if (regex.Matches(line))
        {
            if (rs.filename > 0)
                 m_ErrorFilename = UnixFilename(regex.GetMatch(line, rs.filename));
            if (rs.line > 0)
                m_ErrorLine = regex.GetMatch(line, rs.line);
            for (int x = 0; x < 3; ++x)
            {
                if (rs.msg[x] > 0)
                {
                    if (!m_Error.IsEmpty())
                        m_Error << _T(" ");
                    m_Error << regex.GetMatch(line, rs.msg[x]);
                }
            }
            return rs.lt;
        }
    }
    return cltNormal; // default return value
}
