| /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying | 
 |    file LICENSE.rst or https://cmake.org/licensing for details.  */ | 
 | #include "cmConfigureFileCommand.h" | 
 |  | 
 | #include <set> | 
 | #include <sstream> | 
 |  | 
 | #include <cm/string_view> | 
 | #include <cmext/string_view> | 
 |  | 
 | #include <sys/types.h> | 
 |  | 
 | #include "cmExecutionStatus.h" | 
 | #include "cmFSPermissions.h" | 
 | #include "cmMakefile.h" | 
 | #include "cmMessageType.h" | 
 | #include "cmNewLineStyle.h" | 
 | #include "cmStringAlgorithms.h" | 
 | #include "cmSystemTools.h" | 
 |  | 
 | // cmConfigureFileCommand | 
 | bool cmConfigureFileCommand(std::vector<std::string> const& args, | 
 |                             cmExecutionStatus& status) | 
 | { | 
 |   if (args.size() < 2) { | 
 |     status.SetError("called with incorrect number of arguments, expected 2"); | 
 |     return false; | 
 |   } | 
 |  | 
 |   std::string const& inFile = args[0]; | 
 |   std::string const inputFile = cmSystemTools::CollapseFullPath( | 
 |     inFile, status.GetMakefile().GetCurrentSourceDirectory()); | 
 |  | 
 |   // If the input location is a directory, error out. | 
 |   if (cmSystemTools::FileIsDirectory(inputFile)) { | 
 |     status.SetError(cmStrCat("input location\n  ", inputFile, | 
 |                              "\n" | 
 |                              "is a directory but a file was expected.")); | 
 |     return false; | 
 |   } | 
 |  | 
 |   std::string const& outFile = args[1]; | 
 |   std::string outputFile = cmSystemTools::CollapseFullPath( | 
 |     outFile, status.GetMakefile().GetCurrentBinaryDirectory()); | 
 |  | 
 |   // If the output location is already a directory put the file in it. | 
 |   if (cmSystemTools::FileIsDirectory(outputFile)) { | 
 |     outputFile += "/"; | 
 |     outputFile += cmSystemTools::GetFilenameName(inFile); | 
 |   } | 
 |  | 
 |   if (!status.GetMakefile().CanIWriteThisFile(outputFile)) { | 
 |     std::string e = "attempted to configure a file: " + outputFile + | 
 |       " into a source directory."; | 
 |     status.SetError(e); | 
 |     cmSystemTools::SetFatalErrorOccurred(); | 
 |     return false; | 
 |   } | 
 |   std::string errorMessage; | 
 |   cmNewLineStyle newLineStyle; | 
 |   if (!newLineStyle.ReadFromArguments(args, errorMessage)) { | 
 |     status.SetError(errorMessage); | 
 |     return false; | 
 |   } | 
 |   bool copyOnly = false; | 
 |   bool escapeQuotes = false; | 
 |   bool useSourcePermissions = false; | 
 |   bool noSourcePermissions = false; | 
 |   bool filePermissions = false; | 
 |   std::vector<std::string> filePermissionOptions; | 
 |  | 
 |   enum class Doing | 
 |   { | 
 |     DoingNone, | 
 |     DoingFilePermissions, | 
 |     DoneFilePermissions | 
 |   }; | 
 |  | 
 |   Doing doing = Doing::DoingNone; | 
 |  | 
 |   static std::set<cm::string_view> noopOptions = { | 
 |     /* Legacy.  */ | 
 |     "IMMEDIATE"_s, | 
 |     /* Handled by NewLineStyle member.  */ | 
 |     "NEWLINE_STYLE"_s, | 
 |     "LF"_s, | 
 |     "UNIX"_s, | 
 |     "CRLF"_s, | 
 |     "WIN32"_s, | 
 |     "DOS"_s, | 
 |   }; | 
 |  | 
 |   std::string unknown_args; | 
 |   bool atOnly = false; | 
 |   for (unsigned int i = 2; i < args.size(); ++i) { | 
 |     if (args[i] == "COPYONLY") { | 
 |       if (doing == Doing::DoingFilePermissions) { | 
 |         doing = Doing::DoneFilePermissions; | 
 |       } | 
 |       copyOnly = true; | 
 |       if (newLineStyle.IsValid()) { | 
 |         status.SetError("COPYONLY could not be used in combination " | 
 |                         "with NEWLINE_STYLE"); | 
 |         return false; | 
 |       } | 
 |     } else if (args[i] == "ESCAPE_QUOTES") { | 
 |       if (doing == Doing::DoingFilePermissions) { | 
 |         doing = Doing::DoneFilePermissions; | 
 |       } | 
 |       escapeQuotes = true; | 
 |     } else if (args[i] == "@ONLY") { | 
 |       if (doing == Doing::DoingFilePermissions) { | 
 |         doing = Doing::DoneFilePermissions; | 
 |       } | 
 |       atOnly = true; | 
 |     } else if (args[i] == "NO_SOURCE_PERMISSIONS") { | 
 |       if (doing == Doing::DoingFilePermissions) { | 
 |         status.SetError(" given both FILE_PERMISSIONS and " | 
 |                         "NO_SOURCE_PERMISSIONS. Only one option allowed."); | 
 |         return false; | 
 |       } | 
 |       noSourcePermissions = true; | 
 |     } else if (args[i] == "USE_SOURCE_PERMISSIONS") { | 
 |       if (doing == Doing::DoingFilePermissions) { | 
 |         status.SetError(" given both FILE_PERMISSIONS and " | 
 |                         "USE_SOURCE_PERMISSIONS. Only one option allowed."); | 
 |         return false; | 
 |       } | 
 |       useSourcePermissions = true; | 
 |     } else if (args[i] == "FILE_PERMISSIONS") { | 
 |       if (useSourcePermissions) { | 
 |         status.SetError(" given both FILE_PERMISSIONS and " | 
 |                         "USE_SOURCE_PERMISSIONS. Only one option allowed."); | 
 |         return false; | 
 |       } | 
 |       if (noSourcePermissions) { | 
 |         status.SetError(" given both FILE_PERMISSIONS and " | 
 |                         "NO_SOURCE_PERMISSIONS. Only one option allowed."); | 
 |         return false; | 
 |       } | 
 |  | 
 |       if (doing == Doing::DoingNone) { | 
 |         doing = Doing::DoingFilePermissions; | 
 |         filePermissions = true; | 
 |       } | 
 |     } else if (noopOptions.find(args[i]) != noopOptions.end()) { | 
 |       /* Ignore no-op options.  */ | 
 |     } else if (doing == Doing::DoingFilePermissions) { | 
 |       filePermissionOptions.push_back(args[i]); | 
 |     } else { | 
 |       unknown_args += " "; | 
 |       unknown_args += args[i]; | 
 |       unknown_args += "\n"; | 
 |     } | 
 |   } | 
 |   if (!unknown_args.empty()) { | 
 |     std::string msg = cmStrCat( | 
 |       "configure_file called with unknown argument(s):\n", unknown_args); | 
 |     status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, msg); | 
 |   } | 
 |  | 
 |   if (useSourcePermissions && noSourcePermissions) { | 
 |     status.SetError(" given both USE_SOURCE_PERMISSIONS and " | 
 |                     "NO_SOURCE_PERMISSIONS. Only one option allowed."); | 
 |     return false; | 
 |   } | 
 |  | 
 |   mode_t permissions = 0; | 
 |  | 
 |   if (filePermissions) { | 
 |     if (filePermissionOptions.empty()) { | 
 |       status.SetError(" given FILE_PERMISSIONS without any options."); | 
 |       return false; | 
 |     } | 
 |  | 
 |     std::vector<std::string> invalidOptions; | 
 |     for (auto const& e : filePermissionOptions) { | 
 |       if (!cmFSPermissions::stringToModeT(e, permissions)) { | 
 |         invalidOptions.push_back(e); | 
 |       } | 
 |     } | 
 |  | 
 |     if (!invalidOptions.empty()) { | 
 |       std::ostringstream oss; | 
 |       oss << " given invalid permission "; | 
 |       for (auto i = 0u; i < invalidOptions.size(); i++) { | 
 |         if (i == 0u) { | 
 |           oss << "\"" << invalidOptions[i] << "\""; | 
 |         } else { | 
 |           oss << ",\"" << invalidOptions[i] << "\""; | 
 |         } | 
 |       } | 
 |       oss << "."; | 
 |       status.SetError(oss.str()); | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   if (noSourcePermissions) { | 
 |     permissions |= cmFSPermissions::mode_owner_read; | 
 |     permissions |= cmFSPermissions::mode_owner_write; | 
 |     permissions |= cmFSPermissions::mode_group_read; | 
 |     permissions |= cmFSPermissions::mode_world_read; | 
 |   } | 
 |  | 
 |   if (!status.GetMakefile().ConfigureFile(inputFile, outputFile, copyOnly, | 
 |                                           atOnly, escapeQuotes, permissions, | 
 |                                           newLineStyle)) { | 
 |     status.SetError("Problem configuring file"); | 
 |     return false; | 
 |   } | 
 |  | 
 |   return true; | 
 | } |