text: /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
     file Copyright.txt or https://cmake.org/licensing for details.  */
  #include "cmLoadCacheCommand.h"
  
  #include <set>
  
  #include "cmsys/FStream.hxx"
  
  #include "cmExecutionStatus.h"
  #include "cmMakefile.h"
  #include "cmStateTypes.h"
  #include "cmSystemTools.h"
  #include "cmake.h"
  
  static bool ReadWithPrefix(std::vector<std::string> const& args,
                             cmExecutionStatus& status);
  
  static void CheckLine(cmMakefile& mf, std::string const& prefix,
                        std::set<std::string> const& variablesToRead,
                        const char* line);
  
  bool cmLoadCacheCommand(std::vector<std::string> const& args,
                          cmExecutionStatus& status)
  {
    if (args.empty()) {
      status.SetError("called with wrong number of arguments.");
      return false;
    }
  
    if (args.size() >= 2 && args[1] == "READ_WITH_PREFIX") {
      return ReadWithPrefix(args, status);
    }
  
    if (status.GetMakefile().GetCMakeInstance()->GetWorkingMode() ==
        cmake::SCRIPT_MODE) {
      status.SetError(
        "Only load_cache(READ_WITH_PREFIX) may be used in script mode");
      return false;
    }
  
    // Cache entries to be excluded from the import list.
    // If this set is empty, all cache entries are brought in
    // and they can not be overridden.
    bool excludeFiles = false;
    std::set<std::string> excludes;
  
    for (std::string const& arg : args) {
      if (excludeFiles) {
        excludes.insert(arg);
      }
      if (arg == "EXCLUDE") {
        excludeFiles = true;
      }
      if (excludeFiles && (arg == "INCLUDE_INTERNALS")) {
        break;
      }
    }
  
    // Internal cache entries to be imported.
    // If this set is empty, no internal cache entries are
    // brought in.
    bool includeFiles = false;
    std::set<std::string> includes;
  
    for (std::string const& arg : args) {
      if (includeFiles) {
        includes.insert(arg);
      }
      if (arg == "INCLUDE_INTERNALS") {
        includeFiles = true;
      }
      if (includeFiles && (arg == "EXCLUDE")) {
        break;
      }
    }
  
    cmMakefile& mf = status.GetMakefile();
  
    // Loop over each build directory listed in the arguments.  Each
    // directory has a cache file.
    for (std::string const& arg : args) {
      if ((arg == "EXCLUDE") || (arg == "INCLUDE_INTERNALS")) {
        break;
      }
      mf.GetCMakeInstance()->LoadCache(arg, false, excludes, includes);
    }
  
    return true;
  }
  
  static bool ReadWithPrefix(std::vector<std::string> const& args,
                             cmExecutionStatus& status)
  {
    // Make sure we have a prefix.
    if (args.size() < 3) {
      status.SetError("READ_WITH_PREFIX form must specify a prefix.");
      return false;
    }
  
    // Make sure the cache file exists.
    std::string cacheFile = args[0] + "/CMakeCache.txt";
    if (!cmSystemTools::FileExists(cacheFile)) {
      std::string e = "Cannot load cache file from " + cacheFile;
      status.SetError(e);
      return false;
    }
  
    // Prepare the table of variables to read.
    std::string const prefix = args[2];
    std::set<std::string> const variablesToRead(args.begin() + 3, args.end());
  
    // Read the cache file.
    cmsys::ifstream fin(cacheFile.c_str());
  
    cmMakefile& mf = status.GetMakefile();
  
    // This is a big hack read loop to overcome a buggy ifstream
    // implementation on HP-UX.  This should work on all platforms even
    // for small buffer sizes.
    const int bufferSize = 4096;
    char buffer[bufferSize];
    std::string line;
    while (fin) {
      // Read a block of the file.
      fin.read(buffer, bufferSize);
      if (fin.gcount()) {
        // Parse for newlines directly.
        const char* i = buffer;
        const char* end = buffer + fin.gcount();
        while (i != end) {
          const char* begin = i;
          while (i != end && *i != '\n') {
            ++i;
          }
          if (i == begin || *(i - 1) != '\r') {
            // Include this portion of the line.
            line += std::string(begin, i - begin);
          } else {
            // Include this portion of the line.
            // Don't include the \r in a \r\n pair.
            line += std::string(begin, i - 1 - begin);
          }
          if (i != end) {
            // Completed a line.
            CheckLine(mf, prefix, variablesToRead, line.c_str());
            line.clear();
  
            // Skip the newline character.
            ++i;
          }
        }
      }
    }
    if (!line.empty()) {
      // Partial last line.
      CheckLine(mf, prefix, variablesToRead, line.c_str());
    }
  
    return true;
  }
  
  static void CheckLine(cmMakefile& mf, std::string const& prefix,
                        std::set<std::string> const& variablesToRead,
                        const char* line)
  {
    // Check one line of the cache file.
    std::string var;
    std::string value;
    cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED;
    if (cmake::ParseCacheEntry(line, var, value, type)) {
      // Found a real entry.  See if this one was requested.
      if (variablesToRead.find(var) != variablesToRead.end()) {
        // This was requested.  Set this variable locally with the given
        // prefix.
        var = prefix + var;
        if (!value.empty()) {
          mf.AddDefinition(var, value);
        } else {
          mf.RemoveDefinition(var);
        }
      }
    }
  }
  