Coverage Report

Created: 2026-10-28 06:38

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