/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
/*
   this file contains the implementation of the C API to CMake. Generally
   these routines just manipulate arguments and then call the associated
   methods on the CMake classes. */

#include "cmCPluginAPI.h"

#include "cmExecutionStatus.h"
#include "cmGlobalGenerator.h"
#include "cmMakefile.h"
#include "cmSourceFile.h"
#include "cmState.h"
#include "cmVersion.h"

#include <stdlib.h>

#ifdef __QNX__
#    include <malloc.h> /* for malloc/free on QNX */
#endif

extern "C"
{
    void CCONV* cmGetClientData(void* info)
    {
        return ((cmLoadedCommandInfo*) info)->ClientData;
    }

    void CCONV cmSetClientData(void* info, void* cd)
    {
        ((cmLoadedCommandInfo*) info)->ClientData = cd;
    }

    void CCONV cmSetError(void* info, const char* err)
    {
        if(((cmLoadedCommandInfo*) info)->Error)
        {
            free(((cmLoadedCommandInfo*) info)->Error);
        }
        ((cmLoadedCommandInfo*) info)->Error = strdup(err);
    }

    unsigned int CCONV cmGetCacheMajorVersion(void* arg)
    {
        cmMakefile* mf    = static_cast<cmMakefile*>(arg);
        cmState*    state = mf->GetState();
        return state->GetCacheMajorVersion();
    }
    unsigned int CCONV cmGetCacheMinorVersion(void* arg)
    {
        cmMakefile* mf    = static_cast<cmMakefile*>(arg);
        cmState*    state = mf->GetState();
        return state->GetCacheMinorVersion();
    }

    unsigned int CCONV cmGetMajorVersion(void*)
    {
        return cmVersion::GetMajorVersion();
    }

    unsigned int CCONV cmGetMinorVersion(void*)
    {
        return cmVersion::GetMinorVersion();
    }

    void CCONV cmAddDefinition(void* arg, const char* name, const char* value)
    {
        cmMakefile* mf = static_cast<cmMakefile*>(arg);
        mf->AddDefinition(name, value);
    }

    /* Add a definition to this makefile and the global cmake cache. */
    void CCONV cmAddCacheDefinition(void* arg, const char* name,
                                    const char* value, const char* doc,
                                    int type)
    {
        cmMakefile* mf = static_cast<cmMakefile*>(arg);

        switch(type)
        {
            case CM_CACHE_BOOL:
                mf->AddCacheDefinition(name, value, doc, cmStateEnums::BOOL);
                break;
            case CM_CACHE_PATH:
                mf->AddCacheDefinition(name, value, doc, cmStateEnums::PATH);
                break;
            case CM_CACHE_FILEPATH:
                mf->AddCacheDefinition(name, value, doc,
                                       cmStateEnums::FILEPATH);
                break;
            case CM_CACHE_STRING:
                mf->AddCacheDefinition(name, value, doc, cmStateEnums::STRING);
                break;
            case CM_CACHE_INTERNAL:
                mf->AddCacheDefinition(name, value, doc,
                                       cmStateEnums::INTERNAL);
                break;
            case CM_CACHE_STATIC:
                mf->AddCacheDefinition(name, value, doc, cmStateEnums::STATIC);
                break;
        }
    }

    const char* CCONV cmGetProjectName(void* arg)
    {
        cmMakefile*        mf = static_cast<cmMakefile*>(arg);
        static std::string name;
        name = mf->GetStateSnapshot().GetProjectName();
        return name.c_str();
    }

    const char* CCONV cmGetHomeDirectory(void* arg)
    {
        cmMakefile* mf = static_cast<cmMakefile*>(arg);
        return mf->GetHomeDirectory().c_str();
    }
    const char* CCONV cmGetHomeOutputDirectory(void* arg)
    {
        cmMakefile* mf = static_cast<cmMakefile*>(arg);
        return mf->GetHomeOutputDirectory().c_str();
    }
    const char* CCONV cmGetStartDirectory(void* arg)
    {
        cmMakefile* mf = static_cast<cmMakefile*>(arg);
        return mf->GetCurrentSourceDirectory().c_str();
    }
    const char* CCONV cmGetStartOutputDirectory(void* arg)
    {
        cmMakefile* mf = static_cast<cmMakefile*>(arg);
        return mf->GetCurrentBinaryDirectory().c_str();
    }
    const char* CCONV cmGetCurrentDirectory(void* arg)
    {
        cmMakefile* mf = static_cast<cmMakefile*>(arg);
        return mf->GetCurrentSourceDirectory().c_str();
    }
    const char* CCONV cmGetCurrentOutputDirectory(void* arg)
    {
        cmMakefile* mf = static_cast<cmMakefile*>(arg);
        return mf->GetCurrentBinaryDirectory().c_str();
    }
    const char* CCONV cmGetDefinition(void* arg, const char* def)
    {
        cmMakefile* mf = static_cast<cmMakefile*>(arg);
        return mf->GetDefinition(def);
    }

    int CCONV cmIsOn(void* arg, const char* name)
    {
        cmMakefile* mf = static_cast<cmMakefile*>(arg);
        return static_cast<int>(mf->IsOn(name));
    }

    /** Check if a command exists. */
    int CCONV cmCommandExists(void* arg, const char* name)
    {
        cmMakefile* mf = static_cast<cmMakefile*>(arg);
        return static_cast<int>(mf->GetState()->GetCommand(name) ? 1 : 0);
    }

    void CCONV cmAddDefineFlag(void* arg, const char* definition)
    {
        cmMakefile* mf = static_cast<cmMakefile*>(arg);
        mf->AddDefineFlag(definition);
    }

    void CCONV cmAddLinkDirectoryForTarget(void* arg, const char* tgt,
                                           const char* d)
    {
        cmMakefile* mf = static_cast<cmMakefile*>(arg);
        cmTarget*   t  = mf->FindLocalNonAliasTarget(tgt);
        if(!t)
        {
            cmSystemTools::Error(
                "Attempt to add link directories to non-existent target: ", tgt,
                " for directory ", d);
            return;
        }
        t->InsertLinkDirectory(d, mf->GetBacktrace());
    }

    void CCONV cmAddExecutable(void* arg, const char* exename, int numSrcs,
                               const char** srcs, int win32)
    {
        cmMakefile*              mf = static_cast<cmMakefile*>(arg);
        std::vector<std::string> srcs2;
        int                      i;
        for(i = 0; i < numSrcs; ++i)
        {
            srcs2.push_back(srcs[i]);
        }
        cmTarget* tg = mf->AddExecutable(exename, srcs2);
        if(win32)
        {
            tg->SetProperty("WIN32_EXECUTABLE", "ON");
        }
    }

    void CCONV cmAddUtilityCommand(void* arg, const char* utilityName,
                                   const char* command, const char* arguments,
                                   int all, int numDepends,
                                   const char** depends, int, const char**)
    {
        // Get the makefile instance.  Perform an extra variable expansion
        // now because the API caller expects it.
        cmMakefile* mf = static_cast<cmMakefile*>(arg);

        // Construct the command line for the command.
        cmCustomCommandLine commandLine;
        std::string         expand = command;
        commandLine.push_back(mf->ExpandVariablesInString(expand));
        if(arguments && arguments[0])
        {
            // TODO: Parse arguments!
            expand = arguments;
            commandLine.push_back(mf->ExpandVariablesInString(expand));
        }
        cmCustomCommandLines commandLines;
        commandLines.push_back(commandLine);

        // Accumulate the list of dependencies.
        std::vector<std::string> depends2;
        for(int i = 0; i < numDepends; ++i)
        {
            expand = depends[i];
            depends2.push_back(mf->ExpandVariablesInString(expand));
        }

        // Pass the call to the makefile instance.
        mf->AddUtilityCommand(utilityName, cmMakefile::TargetOrigin::Project,
                              (all ? false : true), nullptr, depends2,
                              commandLines);
    }
    void CCONV cmAddCustomCommand(void* arg, const char* source,
                                  const char* command, int numArgs,
                                  const char** args, int numDepends,
                                  const char** depends, int numOutputs,
                                  const char** outputs, const char* target)
    {
        // Get the makefile instance.  Perform an extra variable expansion
        // now because the API caller expects it.
        cmMakefile* mf = static_cast<cmMakefile*>(arg);

        // Construct the command line for the command.
        cmCustomCommandLine commandLine;
        std::string         expand = command;
        commandLine.push_back(mf->ExpandVariablesInString(expand));
        for(int i = 0; i < numArgs; ++i)
        {
            expand = args[i];
            commandLine.push_back(mf->ExpandVariablesInString(expand));
        }
        cmCustomCommandLines commandLines;
        commandLines.push_back(commandLine);

        // Accumulate the list of dependencies.
        std::vector<std::string> depends2;
        for(int i = 0; i < numDepends; ++i)
        {
            expand = depends[i];
            depends2.push_back(mf->ExpandVariablesInString(expand));
        }

        // Accumulate the list of outputs.
        std::vector<std::string> outputs2;
        for(int i = 0; i < numOutputs; ++i)
        {
            expand = outputs[i];
            outputs2.push_back(mf->ExpandVariablesInString(expand));
        }

        // Pass the call to the makefile instance.
        const char* no_comment = nullptr;
        mf->AddCustomCommandOldStyle(target, outputs2, depends2, source,
                                     commandLines, no_comment);
    }

    void CCONV cmAddCustomCommandToOutput(void* arg, const char* output,
                                          const char* command, int numArgs,
                                          const char** args,
                                          const char*  main_dependency,
                                          int numDepends, const char** depends)
    {
        // Get the makefile instance.  Perform an extra variable expansion
        // now because the API caller expects it.
        cmMakefile* mf = static_cast<cmMakefile*>(arg);

        // Construct the command line for the command.
        cmCustomCommandLine commandLine;
        std::string         expand = command;
        commandLine.push_back(mf->ExpandVariablesInString(expand));
        for(int i = 0; i < numArgs; ++i)
        {
            expand = args[i];
            commandLine.push_back(mf->ExpandVariablesInString(expand));
        }
        cmCustomCommandLines commandLines;
        commandLines.push_back(commandLine);

        // Accumulate the list of dependencies.
        std::vector<std::string> depends2;
        for(int i = 0; i < numDepends; ++i)
        {
            expand = depends[i];
            depends2.push_back(mf->ExpandVariablesInString(expand));
        }

        // Pass the call to the makefile instance.
        const char* no_comment     = nullptr;
        const char* no_working_dir = nullptr;
        mf->AddCustomCommandToOutput(output, depends2, main_dependency,
                                     commandLines, no_comment, no_working_dir);
    }

    void CCONV cmAddCustomCommandToTarget(void* arg, const char* target,
                                          const char* command, int numArgs,
                                          const char** args, int commandType)
    {
        // Get the makefile instance.
        cmMakefile* mf = static_cast<cmMakefile*>(arg);

        // Construct the command line for the command.  Perform an extra
        // variable expansion now because the API caller expects it.
        cmCustomCommandLine commandLine;
        std::string         expand = command;
        commandLine.push_back(mf->ExpandVariablesInString(expand));
        for(int i = 0; i < numArgs; ++i)
        {
            expand = args[i];
            commandLine.push_back(mf->ExpandVariablesInString(expand));
        }
        cmCustomCommandLines commandLines;
        commandLines.push_back(commandLine);

        // Select the command type.
        cmTarget::CustomCommandType cctype = cmTarget::POST_BUILD;
        switch(commandType)
        {
            case CM_PRE_BUILD:
                cctype = cmTarget::PRE_BUILD;
                break;
            case CM_PRE_LINK:
                cctype = cmTarget::PRE_LINK;
                break;
            case CM_POST_BUILD:
                cctype = cmTarget::POST_BUILD;
                break;
        }

        // Pass the call to the makefile instance.
        std::vector<std::string> no_byproducts;
        std::vector<std::string> no_depends;
        const char*              no_comment     = nullptr;
        const char*              no_working_dir = nullptr;
        mf->AddCustomCommandToTarget(target, no_byproducts, no_depends,
                                     commandLines, cctype, no_comment,
                                     no_working_dir);
    }

    static void addLinkLibrary(cmMakefile* mf, std::string const& target,
                               std::string const&      lib,
                               cmTargetLinkLibraryType llt)
    {
        cmTarget* t = mf->FindLocalNonAliasTarget(target);
        if(!t)
        {
            std::ostringstream e;
            e << "Attempt to add link library \"" << lib << "\" to target \""
              << target << "\" which is not built in this directory.";
            mf->IssueMessage(cmake::FATAL_ERROR, e.str());
            return;
        }

        cmTarget* tgt = mf->GetGlobalGenerator()->FindTarget(lib);
        if(tgt && (tgt->GetType() != cmStateEnums::STATIC_LIBRARY) &&
           (tgt->GetType() != cmStateEnums::SHARED_LIBRARY) &&
           (tgt->GetType() != cmStateEnums::INTERFACE_LIBRARY) &&
           !tgt->IsExecutableWithExports())
        {
            std::ostringstream e;
            e << "Target \"" << lib << "\" of type "
              << cmState::GetTargetTypeName(tgt->GetType())
              << " may not be linked into another target.  "
              << "One may link only to STATIC or SHARED libraries, or "
              << "to executables with the ENABLE_EXPORTS property set.";
            mf->IssueMessage(cmake::FATAL_ERROR, e.str());
        }

        t->AddLinkLibrary(*mf, lib, llt);
    }

    void CCONV cmAddLinkLibraryForTarget(void* arg, const char* tgt,
                                         const char* value, int libtype)
    {
        cmMakefile* mf = static_cast<cmMakefile*>(arg);

        switch(libtype)
        {
            case CM_LIBRARY_GENERAL:
                addLinkLibrary(mf, tgt, value, GENERAL_LibraryType);
                break;
            case CM_LIBRARY_DEBUG:
                addLinkLibrary(mf, tgt, value, DEBUG_LibraryType);
                break;
            case CM_LIBRARY_OPTIMIZED:
                addLinkLibrary(mf, tgt, value, OPTIMIZED_LibraryType);
                break;
        }
    }

    void CCONV cmAddLibrary(void* arg, const char* libname, int shared,
                            int numSrcs, const char** srcs)
    {
        cmMakefile*              mf = static_cast<cmMakefile*>(arg);
        std::vector<std::string> srcs2;
        int                      i;
        for(i = 0; i < numSrcs; ++i)
        {
            srcs2.push_back(srcs[i]);
        }
        mf->AddLibrary(libname,
                       (shared ? cmStateEnums::SHARED_LIBRARY
                               : cmStateEnums::STATIC_LIBRARY),
                       srcs2);
    }

    char CCONV* cmExpandVariablesInString(void* arg, const char* source,
                                          int escapeQuotes, int atOnly)
    {
        cmMakefile*        mf   = static_cast<cmMakefile*>(arg);
        std::string        barf = source;
        std::string const& result =
            mf->ExpandVariablesInString(barf, escapeQuotes, atOnly);
        return strdup(result.c_str());
    }

    int CCONV cmExecuteCommand(void* arg, const char* name, int numArgs,
                               const char** args)
    {
        cmMakefile*        mf = static_cast<cmMakefile*>(arg);
        cmListFileFunction lff;
        lff.Name = name;
        for(int i = 0; i < numArgs; ++i)
        {
            // Assume all arguments are quoted.
            lff.Arguments.push_back(
                cmListFileArgument(args[i], cmListFileArgument::Quoted, 0));
        }
        cmExecutionStatus status;
        return mf->ExecuteCommand(lff, status);
    }

    void CCONV cmExpandSourceListArguments(void* arg, int numArgs,
                                           const char** args, int* resArgc,
                                           char***      resArgv,
                                           unsigned int startArgumentIndex)
    {
        (void) arg;
        (void) startArgumentIndex;
        std::vector<std::string> result;
        int                      i;
        for(i = 0; i < numArgs; ++i)
        {
            result.push_back(args[i]);
        }
        int    resargc = static_cast<int>(result.size());
        char** resargv = nullptr;
        if(resargc)
        {
            resargv = (char**) malloc(resargc * sizeof(char*));
        }
        for(i = 0; i < resargc; ++i)
        {
            resargv[i] = strdup(result[i].c_str());
        }
        *resArgc = resargc;
        *resArgv = resargv;
    }

    void CCONV cmFreeArguments(int argc, char** argv)
    {
        int i;
        for(i = 0; i < argc; ++i)
        {
            free(argv[i]);
        }
        free(argv);
    }

    int CCONV cmGetTotalArgumentSize(int argc, char** argv)
    {
        int i;
        int result = 0;
        for(i = 0; i < argc; ++i)
        {
            if(argv[i])
            {
                result = result + static_cast<int>(strlen(argv[i]));
            }
        }
        return result;
    }

    // Source file proxy object to support the old cmSourceFile/cmMakefile
    // API for source files.
    struct cmCPluginAPISourceFile
    {
        cmCPluginAPISourceFile()
        : RealSourceFile(nullptr)
        {}
        cmSourceFile*            RealSourceFile;
        std::string              SourceName;
        std::string              SourceExtension;
        std::string              FullPath;
        std::vector<std::string> Depends;
        cmPropertyMap            Properties;
    };

    // Keep a map from real cmSourceFile instances stored in a makefile to
    // the CPluginAPI proxy source file.
    class cmCPluginAPISourceFileMap
    : public std::map<cmSourceFile*, cmCPluginAPISourceFile*>
    {
    public:
        typedef std::map<cmSourceFile*, cmCPluginAPISourceFile*> derived;
        typedef derived::iterator                                iterator;
        typedef derived::value_type                              value_type;
        ~cmCPluginAPISourceFileMap()
        {
            for(auto const& i : *this)
            {
                delete i.second;
            }
        }
    };
    cmCPluginAPISourceFileMap cmCPluginAPISourceFiles;

    void* CCONV cmCreateSourceFile(void) { return new cmCPluginAPISourceFile; }

    void* CCONV cmCreateNewSourceFile(void*)
    {
        return new cmCPluginAPISourceFile;
    }

    void CCONV cmDestroySourceFile(void* arg)
    {
        cmCPluginAPISourceFile* sf = static_cast<cmCPluginAPISourceFile*>(arg);
        // Only delete if it was created by cmCreateSourceFile or
        // cmCreateNewSourceFile and is therefore not in the map.
        if(!sf->RealSourceFile)
        {
            delete sf;
        }
    }

    void CCONV* cmGetSource(void* arg, const char* name)
    {
        cmMakefile* mf = static_cast<cmMakefile*>(arg);
        if(cmSourceFile* rsf = mf->GetSource(name))
        {
            // Lookup the proxy source file object for this source.
            cmCPluginAPISourceFileMap::iterator i =
                cmCPluginAPISourceFiles.find(rsf);
            if(i == cmCPluginAPISourceFiles.end())
            {
                // Create a proxy source file object for this source.
                cmCPluginAPISourceFile* sf = new cmCPluginAPISourceFile;
                sf->RealSourceFile         = rsf;
                sf->FullPath               = rsf->GetFullPath();
                sf->SourceName = cmSystemTools::GetFilenameWithoutLastExtension(
                    sf->FullPath);
                sf->SourceExtension =
                    cmSystemTools::GetFilenameLastExtension(sf->FullPath);

                // Store the proxy in the map so it can be re-used and deleted
                // later.
                cmCPluginAPISourceFileMap::value_type entry(rsf, sf);
                i = cmCPluginAPISourceFiles.insert(entry).first;
            }
            return i->second;
        }
        return nullptr;
    }

    void* CCONV cmAddSource(void* arg, void* arg2)
    {
        cmMakefile*             mf = static_cast<cmMakefile*>(arg);
        cmCPluginAPISourceFile* osf =
            static_cast<cmCPluginAPISourceFile*>(arg2);
        if(osf->FullPath.empty())
        {
            return nullptr;
        }

        // Create the real cmSourceFile instance and copy over saved
        // information.
        cmSourceFile* rsf    = mf->GetOrCreateSource(osf->FullPath);
        rsf->GetProperties() = osf->Properties;
        for(std::string const& d : osf->Depends)
        {
            rsf->AddDepend(d);
        }

        // Create the proxy for the real source file.
        cmCPluginAPISourceFile* sf = new cmCPluginAPISourceFile;
        sf->RealSourceFile         = rsf;
        sf->FullPath               = osf->FullPath;
        sf->SourceName             = osf->SourceName;
        sf->SourceExtension        = osf->SourceExtension;

        // Store the proxy in the map so it can be re-used and deleted later.
        cmCPluginAPISourceFiles[rsf] = sf;
        return sf;
    }

    const char* CCONV cmSourceFileGetSourceName(void* arg)
    {
        cmCPluginAPISourceFile* sf = static_cast<cmCPluginAPISourceFile*>(arg);
        return sf->SourceName.c_str();
    }

    const char* CCONV cmSourceFileGetFullPath(void* arg)
    {
        cmCPluginAPISourceFile* sf = static_cast<cmCPluginAPISourceFile*>(arg);
        return sf->FullPath.c_str();
    }

    const char* CCONV cmSourceFileGetProperty(void* arg, const char* prop)
    {
        cmCPluginAPISourceFile* sf = static_cast<cmCPluginAPISourceFile*>(arg);
        if(cmSourceFile* rsf = sf->RealSourceFile)
        {
            return rsf->GetProperty(prop);
        }
        if(!strcmp(prop, "LOCATION"))
        {
            return sf->FullPath.c_str();
        }
        return sf->Properties.GetPropertyValue(prop);
    }

    int CCONV cmSourceFileGetPropertyAsBool(void* arg, const char* prop)
    {
        cmCPluginAPISourceFile* sf = static_cast<cmCPluginAPISourceFile*>(arg);
        if(cmSourceFile* rsf = sf->RealSourceFile)
        {
            return rsf->GetPropertyAsBool(prop) ? 1 : 0;
        }
        return cmSystemTools::IsOn(cmSourceFileGetProperty(arg, prop)) ? 1 : 0;
    }

    void CCONV cmSourceFileSetProperty(void* arg, const char* prop,
                                       const char* value)
    {
        cmCPluginAPISourceFile* sf = static_cast<cmCPluginAPISourceFile*>(arg);
        if(cmSourceFile* rsf = sf->RealSourceFile)
        {
            rsf->SetProperty(prop, value);
        } else if(prop)
        {
            if(!value)
            {
                value = "NOTFOUND";
            }
            sf->Properties.SetProperty(prop, value);
        }
    }

    void CCONV cmSourceFileAddDepend(void* arg, const char* depend)
    {
        cmCPluginAPISourceFile* sf = static_cast<cmCPluginAPISourceFile*>(arg);
        if(cmSourceFile* rsf = sf->RealSourceFile)
        {
            rsf->AddDepend(depend);
        } else
        {
            sf->Depends.push_back(depend);
        }
    }

    void CCONV cmSourceFileSetName(void* arg, const char* name, const char* dir,
                                   int          numSourceExtensions,
                                   const char** sourceExtensions,
                                   int          numHeaderExtensions,
                                   const char** headerExtensions)
    {
        cmCPluginAPISourceFile* sf = static_cast<cmCPluginAPISourceFile*>(arg);
        if(sf->RealSourceFile)
        {
            // SetName is allowed only on temporary source files created by
            // the command for building and passing to AddSource.
            return;
        }
        std::vector<std::string> sourceExts;
        std::vector<std::string> headerExts;
        int                      i;
        for(i = 0; i < numSourceExtensions; ++i)
        {
            sourceExts.push_back(sourceExtensions[i]);
        }
        for(i = 0; i < numHeaderExtensions; ++i)
        {
            headerExts.push_back(headerExtensions[i]);
        }

        // Save the original name given.
        sf->SourceName = name;

        // Convert the name to a full path in case the given name is a
        // relative path.
        std::string pathname = cmSystemTools::CollapseFullPath(name, dir);

        // First try and see whether the listed file can be found
        // as is without extensions added on.
        std::string hname = pathname;
        if(cmSystemTools::FileExists(hname))
        {
            sf->SourceName = cmSystemTools::GetFilenamePath(name);
            if(!sf->SourceName.empty())
            {
                sf->SourceName += "/";
            }
            sf->SourceName +=
                cmSystemTools::GetFilenameWithoutLastExtension(name);
            std::string::size_type pos = hname.rfind('.');
            if(pos != std::string::npos)
            {
                sf->SourceExtension = hname.substr(pos + 1, hname.size() - pos);
                if(cmSystemTools::FileIsFullPath(name))
                {
                    std::string::size_type pos2 = hname.rfind('/');
                    if(pos2 != std::string::npos)
                    {
                        sf->SourceName = hname.substr(pos2 + 1, pos - pos2 - 1);
                    }
                }
            }

            sf->FullPath = hname;
            return;
        }

        // Next, try the various source extensions
        for(std::vector<std::string>::const_iterator ext = sourceExts.begin();
            ext != sourceExts.end(); ++ext)
        {
            hname = pathname;
            hname += ".";
            hname += *ext;
            if(cmSystemTools::FileExists(hname))
            {
                sf->SourceExtension = *ext;
                sf->FullPath        = hname;
                return;
            }
        }

        // Finally, try the various header extensions
        for(std::vector<std::string>::const_iterator ext = headerExts.begin();
            ext != headerExts.end(); ++ext)
        {
            hname = pathname;
            hname += ".";
            hname += *ext;
            if(cmSystemTools::FileExists(hname))
            {
                sf->SourceExtension = *ext;
                sf->FullPath        = hname;
                return;
            }
        }

        std::ostringstream e;
        e << "Cannot find source file \"" << pathname << "\"";
        e << "\n\nTried extensions";
        for(std::vector<std::string>::const_iterator ext = sourceExts.begin();
            ext != sourceExts.end(); ++ext)
        {
            e << " ." << *ext;
        }
        for(std::vector<std::string>::const_iterator ext = headerExts.begin();
            ext != headerExts.end(); ++ext)
        {
            e << " ." << *ext;
        }
        cmSystemTools::Error(e.str().c_str());
    }

    void CCONV cmSourceFileSetName2(void* arg, const char* name,
                                    const char* dir, const char* ext,
                                    int headerFileOnly)
    {
        cmCPluginAPISourceFile* sf = static_cast<cmCPluginAPISourceFile*>(arg);
        if(sf->RealSourceFile)
        {
            // SetName is allowed only on temporary source files created by
            // the command for building and passing to AddSource.
            return;
        }

        // Implement the old SetName method code here.
        if(headerFileOnly)
        {
            sf->Properties.SetProperty("HEADER_FILE_ONLY", "1");
        }
        sf->SourceName    = name;
        std::string fname = sf->SourceName;
        if(ext && strlen(ext))
        {
            fname += ".";
            fname += ext;
        }
        sf->FullPath = cmSystemTools::CollapseFullPath(fname, dir);
        cmSystemTools::ConvertToUnixSlashes(sf->FullPath);
        sf->SourceExtension = ext;
    }

    char* CCONV cmGetFilenameWithoutExtension(const char* name)
    {
        std::string sres = cmSystemTools::GetFilenameWithoutExtension(name);
        return strdup(sres.c_str());
    }

    char* CCONV cmGetFilenamePath(const char* name)
    {
        std::string sres = cmSystemTools::GetFilenamePath(name);
        return strdup(sres.c_str());
    }

    char* CCONV cmCapitalized(const char* name)
    {
        std::string sres = cmSystemTools::Capitalized(name);
        return strdup(sres.c_str());
    }

    void CCONV cmCopyFileIfDifferent(const char* name1, const char* name2)
    {
        cmSystemTools::CopyFileIfDifferent(name1, name2);
    }

    void CCONV cmRemoveFile(const char* name)
    {
        cmSystemTools::RemoveFile(name);
    }

    void CCONV cmDisplayStatus(void* arg, const char* message)
    {
        cmMakefile* mf = static_cast<cmMakefile*>(arg);
        mf->DisplayStatus(message, -1);
    }

    void CCONV cmFree(void* data) { free(data); }

    void CCONV DefineSourceFileProperty(void* arg, const char* name,
                                        const char* briefDocs,
                                        const char* longDocs, int chained)
    {
        cmMakefile* mf = static_cast<cmMakefile*>(arg);
        mf->GetState()->DefineProperty(name, cmProperty::SOURCE_FILE, briefDocs,
                                       longDocs, chained != 0);
    }

}  // close the extern "C" scope

cmCAPI cmStaticCAPI = {
    cmGetClientData,
    cmGetTotalArgumentSize,
    cmFreeArguments,
    cmSetClientData,
    cmSetError,
    cmAddCacheDefinition,
    cmAddCustomCommand,
    cmAddDefineFlag,
    cmAddDefinition,
    cmAddExecutable,
    cmAddLibrary,
    cmAddLinkDirectoryForTarget,
    cmAddLinkLibraryForTarget,
    cmAddUtilityCommand,
    cmCommandExists,
    cmExecuteCommand,
    cmExpandSourceListArguments,
    cmExpandVariablesInString,
    cmGetCacheMajorVersion,
    cmGetCacheMinorVersion,
    cmGetCurrentDirectory,
    cmGetCurrentOutputDirectory,
    cmGetDefinition,
    cmGetHomeDirectory,
    cmGetHomeOutputDirectory,
    cmGetMajorVersion,
    cmGetMinorVersion,
    cmGetProjectName,
    cmGetStartDirectory,
    cmGetStartOutputDirectory,
    cmIsOn,

    cmAddSource,
    cmCreateSourceFile,
    cmDestroySourceFile,
    cmGetSource,
    cmSourceFileAddDepend,
    cmSourceFileGetProperty,
    cmSourceFileGetPropertyAsBool,
    cmSourceFileGetSourceName,
    cmSourceFileGetFullPath,
    cmSourceFileSetName,
    cmSourceFileSetName2,
    cmSourceFileSetProperty,

    cmCapitalized,
    cmCopyFileIfDifferent,
    cmGetFilenameWithoutExtension,
    cmGetFilenamePath,
    cmRemoveFile,
    cmFree,

    cmAddCustomCommandToOutput,
    cmAddCustomCommandToTarget,
    cmDisplayStatus,
    cmCreateNewSourceFile,
    DefineSourceFileProperty,
};
