Coverage Report

Created: 2026-02-09 06:05

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
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
0
{
25
0
  if (args.size() < 2) {
26
0
    status.SetError("called with incorrect number of arguments, expected 2");
27
0
    return false;
28
0
  }
29
30
0
  std::string const& inFile = args[0];
31
0
  std::string const inputFile = cmSystemTools::CollapseFullPath(
32
0
    inFile, status.GetMakefile().GetCurrentSourceDirectory());
33
34
  // If the input location is a directory, error out.
35
0
  if (cmSystemTools::FileIsDirectory(inputFile)) {
36
0
    status.SetError(cmStrCat("input location\n  ", inputFile,
37
0
                             "\n"
38
0
                             "is a directory but a file was expected."));
39
0
    return false;
40
0
  }
41
42
0
  std::string const& outFile = args[1];
43
0
  std::string outputFile = cmSystemTools::CollapseFullPath(
44
0
    outFile, status.GetMakefile().GetCurrentBinaryDirectory());
45
46
  // If the output location is already a directory put the file in it.
47
0
  if (cmSystemTools::FileIsDirectory(outputFile)) {
48
0
    outputFile += "/";
49
0
    outputFile += cmSystemTools::GetFilenameName(inFile);
50
0
  }
51
52
0
  if (!status.GetMakefile().CanIWriteThisFile(outputFile)) {
53
0
    std::string e = "attempted to configure a file: " + outputFile +
54
0
      " into a source directory.";
55
0
    status.SetError(e);
56
0
    cmSystemTools::SetFatalErrorOccurred();
57
0
    return false;
58
0
  }
59
0
  std::string errorMessage;
60
0
  cmNewLineStyle newLineStyle;
61
0
  if (!newLineStyle.ReadFromArguments(args, errorMessage)) {
62
0
    status.SetError(errorMessage);
63
0
    return false;
64
0
  }
65
0
  bool copyOnly = false;
66
0
  bool escapeQuotes = false;
67
0
  bool useSourcePermissions = false;
68
0
  bool noSourcePermissions = false;
69
0
  bool filePermissions = false;
70
0
  std::vector<std::string> filePermissionOptions;
71
72
0
  enum class Doing
73
0
  {
74
0
    DoingNone,
75
0
    DoingFilePermissions,
76
0
    DoneFilePermissions
77
0
  };
78
79
0
  Doing doing = Doing::DoingNone;
80
81
0
  static std::set<cm::string_view> noopOptions = {
82
    /* Legacy.  */
83
0
    "IMMEDIATE"_s,
84
    /* Handled by NewLineStyle member.  */
85
0
    "NEWLINE_STYLE"_s,
86
0
    "LF"_s,
87
0
    "UNIX"_s,
88
0
    "CRLF"_s,
89
0
    "WIN32"_s,
90
0
    "DOS"_s,
91
0
  };
92
93
0
  std::string unknown_args;
94
0
  bool atOnly = false;
95
0
  for (unsigned int i = 2; i < args.size(); ++i) {
96
0
    if (args[i] == "COPYONLY") {
97
0
      if (doing == Doing::DoingFilePermissions) {
98
0
        doing = Doing::DoneFilePermissions;
99
0
      }
100
0
      copyOnly = true;
101
0
      if (newLineStyle.IsValid()) {
102
0
        status.SetError("COPYONLY could not be used in combination "
103
0
                        "with NEWLINE_STYLE");
104
0
        return false;
105
0
      }
106
0
    } else if (args[i] == "ESCAPE_QUOTES") {
107
0
      if (doing == Doing::DoingFilePermissions) {
108
0
        doing = Doing::DoneFilePermissions;
109
0
      }
110
0
      escapeQuotes = true;
111
0
    } else if (args[i] == "@ONLY") {
112
0
      if (doing == Doing::DoingFilePermissions) {
113
0
        doing = Doing::DoneFilePermissions;
114
0
      }
115
0
      atOnly = true;
116
0
    } else if (args[i] == "NO_SOURCE_PERMISSIONS") {
117
0
      if (doing == Doing::DoingFilePermissions) {
118
0
        status.SetError(" given both FILE_PERMISSIONS and "
119
0
                        "NO_SOURCE_PERMISSIONS. Only one option allowed.");
120
0
        return false;
121
0
      }
122
0
      noSourcePermissions = true;
123
0
    } else if (args[i] == "USE_SOURCE_PERMISSIONS") {
124
0
      if (doing == Doing::DoingFilePermissions) {
125
0
        status.SetError(" given both FILE_PERMISSIONS and "
126
0
                        "USE_SOURCE_PERMISSIONS. Only one option allowed.");
127
0
        return false;
128
0
      }
129
0
      useSourcePermissions = true;
130
0
    } else if (args[i] == "FILE_PERMISSIONS") {
131
0
      if (useSourcePermissions) {
132
0
        status.SetError(" given both FILE_PERMISSIONS and "
133
0
                        "USE_SOURCE_PERMISSIONS. Only one option allowed.");
134
0
        return false;
135
0
      }
136
0
      if (noSourcePermissions) {
137
0
        status.SetError(" given both FILE_PERMISSIONS and "
138
0
                        "NO_SOURCE_PERMISSIONS. Only one option allowed.");
139
0
        return false;
140
0
      }
141
142
0
      if (doing == Doing::DoingNone) {
143
0
        doing = Doing::DoingFilePermissions;
144
0
        filePermissions = true;
145
0
      }
146
0
    } else if (noopOptions.find(args[i]) != noopOptions.end()) {
147
      /* Ignore no-op options.  */
148
0
    } else if (doing == Doing::DoingFilePermissions) {
149
0
      filePermissionOptions.push_back(args[i]);
150
0
    } else {
151
0
      unknown_args += " ";
152
0
      unknown_args += args[i];
153
0
      unknown_args += "\n";
154
0
    }
155
0
  }
156
0
  if (!unknown_args.empty()) {
157
0
    std::string msg = cmStrCat(
158
0
      "configure_file called with unknown argument(s):\n", unknown_args);
159
0
    status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, msg);
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
}