resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmConfigureFileCommand.cxx
1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include "cmConfigureFileCommand.h"
4
5 #include <set>
6 #include <sstream>
7
8 #include <cm/string_view>
9 #include <cmext/string_view>
10
11 #include <sys/types.h>
12
13 #include "cmExecutionStatus.h"
14 #include "cmFSPermissions.h"
15 #include "cmMakefile.h"
16 #include "cmMessageType.h"
17 #include "cmNewLineStyle.h"
18 #include "cmStringAlgorithms.h"
19 #include "cmSystemTools.h"
20
21 // cmConfigureFileCommand
22 bool cmConfigureFileCommand(std::vector<std::string> const& args,
23                             cmExecutionStatus& status)
24 {
25   if (args.size() < 2) {
26     status.SetError("called with incorrect number of arguments, expected 2");
27     return false;
28   }
29
30   std::string const& inFile = args[0];
31   const std::string inputFile = cmSystemTools::CollapseFullPath(
32     inFile, status.GetMakefile().GetCurrentSourceDirectory());
33
34   // If the input location is a directory, error out.
35   if (cmSystemTools::FileIsDirectory(inputFile)) {
36     status.SetError(cmStrCat("input location\n  ", inputFile,
37                              "\n"
38                              "is a directory but a file was expected."));
39     return false;
40   }
41
42   std::string const& outFile = args[1];
43   std::string outputFile = cmSystemTools::CollapseFullPath(
44     outFile, status.GetMakefile().GetCurrentBinaryDirectory());
45
46   // If the output location is already a directory put the file in it.
47   if (cmSystemTools::FileIsDirectory(outputFile)) {
48     outputFile += "/";
49     outputFile += cmSystemTools::GetFilenameName(inFile);
50   }
51
52   if (!status.GetMakefile().CanIWriteThisFile(outputFile)) {
53     std::string e = "attempted to configure a file: " + outputFile +
54       " into a source directory.";
55     status.SetError(e);
56     cmSystemTools::SetFatalErrorOccurred();
57     return false;
58   }
59   std::string errorMessage;
60   cmNewLineStyle newLineStyle;
61   if (!newLineStyle.ReadFromArguments(args, errorMessage)) {
62     status.SetError(errorMessage);
63     return false;
64   }
65   bool copyOnly = false;
66   bool escapeQuotes = false;
67   bool useSourcePermissions = false;
68   bool noSourcePermissions = false;
69   bool filePermissions = false;
70   std::vector<std::string> filePermissionOptions;
71
72   enum class Doing
73   {
74     DoingNone,
75     DoingFilePermissions,
76     DoneFilePermissions
77   };
78
79   Doing doing = Doing::DoingNone;
80
81   static std::set<cm::string_view> noopOptions = {
82     /* Legacy.  */
83     "IMMEDIATE"_s,
84     /* Handled by NewLineStyle member.  */
85     "NEWLINE_STYLE"_s,
86     "LF"_s,
87     "UNIX"_s,
88     "CRLF"_s,
89     "WIN32"_s,
90     "DOS"_s,
91   };
92
93   std::string unknown_args;
94   bool atOnly = false;
95   for (unsigned int i = 2; i < args.size(); ++i) {
96     if (args[i] == "COPYONLY") {
97       if (doing == Doing::DoingFilePermissions) {
98         doing = Doing::DoneFilePermissions;
99       }
100       copyOnly = true;
101       if (newLineStyle.IsValid()) {
102         status.SetError("COPYONLY could not be used in combination "
103                         "with NEWLINE_STYLE");
104         return false;
105       }
106     } else if (args[i] == "ESCAPE_QUOTES") {
107       if (doing == Doing::DoingFilePermissions) {
108         doing = Doing::DoneFilePermissions;
109       }
110       escapeQuotes = true;
111     } else if (args[i] == "@ONLY") {
112       if (doing == Doing::DoingFilePermissions) {
113         doing = Doing::DoneFilePermissions;
114       }
115       atOnly = true;
116     } else if (args[i] == "NO_SOURCE_PERMISSIONS") {
117       if (doing == Doing::DoingFilePermissions) {
118         status.SetError(" given both FILE_PERMISSIONS and "
119                         "NO_SOURCE_PERMISSIONS. Only one option allowed.");
120         return false;
121       }
122       noSourcePermissions = true;
123     } else if (args[i] == "USE_SOURCE_PERMISSIONS") {
124       if (doing == Doing::DoingFilePermissions) {
125         status.SetError(" given both FILE_PERMISSIONS and "
126                         "USE_SOURCE_PERMISSIONS. Only one option allowed.");
127         return false;
128       }
129       useSourcePermissions = true;
130     } else if (args[i] == "FILE_PERMISSIONS") {
131       if (useSourcePermissions) {
132         status.SetError(" given both FILE_PERMISSIONS and "
133                         "USE_SOURCE_PERMISSIONS. Only one option allowed.");
134         return false;
135       }
136       if (noSourcePermissions) {
137         status.SetError(" given both FILE_PERMISSIONS and "
138                         "NO_SOURCE_PERMISSIONS. Only one option allowed.");
139         return false;
140       }
141
142       if (doing == Doing::DoingNone) {
143         doing = Doing::DoingFilePermissions;
144         filePermissions = true;
145       }
146     } else if (noopOptions.find(args[i]) != noopOptions.end()) {
147       /* Ignore no-op options.  */
148     } else if (doing == Doing::DoingFilePermissions) {
149       filePermissionOptions.push_back(args[i]);
150     } else {
151       unknown_args += " ";
152       unknown_args += args[i];
153       unknown_args += "\n";
154     }
155   }
156   if (!unknown_args.empty()) {
157     std::string msg = cmStrCat(
158       "configure_file called with unknown argument(s):\n", unknown_args);
159     status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, msg);
160   }
161
162   if (useSourcePermissions && noSourcePermissions) {
163     status.SetError(" given both USE_SOURCE_PERMISSIONS and "
164                     "NO_SOURCE_PERMISSIONS. Only one option allowed.");
165     return false;
166   }
167
168   mode_t permissions = 0;
169
170   if (filePermissions) {
171     if (filePermissionOptions.empty()) {
172       status.SetError(" given FILE_PERMISSIONS without any options.");
173       return false;
174     }
175
176     std::vector<std::string> invalidOptions;
177     for (auto const& e : filePermissionOptions) {
178       if (!cmFSPermissions::stringToModeT(e, permissions)) {
179         invalidOptions.push_back(e);
180       }
181     }
182
183     if (!invalidOptions.empty()) {
184       std::ostringstream oss;
185       oss << " given invalid permission ";
186       for (auto i = 0u; i < invalidOptions.size(); i++) {
187         if (i == 0u) {
188           oss << "\"" << invalidOptions[i] << "\"";
189         } else {
190           oss << ",\"" << invalidOptions[i] << "\"";
191         }
192       }
193       oss << ".";
194       status.SetError(oss.str());
195       return false;
196     }
197   }
198
199   if (noSourcePermissions) {
200     permissions |= cmFSPermissions::mode_owner_read;
201     permissions |= cmFSPermissions::mode_owner_write;
202     permissions |= cmFSPermissions::mode_group_read;
203     permissions |= cmFSPermissions::mode_world_read;
204   }
205
206   if (!status.GetMakefile().ConfigureFile(inputFile, outputFile, copyOnly,
207                                           atOnly, escapeQuotes, permissions,
208                                           newLineStyle)) {
209     status.SetError("Problem configuring file");
210     return false;
211   }
212
213   return true;
214 }