Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmOutputConverter.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 "cmOutputConverter.h"
4
5
#include <algorithm>
6
#include <cassert>
7
#include <set>
8
#include <vector>
9
10
#ifdef _WIN32
11
#  include <unordered_map>
12
#  include <utility>
13
#endif
14
15
#include "cmsys/String.h"
16
17
#include "cmList.h"
18
#include "cmState.h"
19
#include "cmStateDirectory.h"
20
#include "cmSystemTools.h"
21
#include "cmValue.h"
22
23
namespace {
24
bool PathEqOrSubDir(std::string const& a, std::string const& b)
25
0
{
26
0
  return (cmSystemTools::ComparePath(a, b) ||
27
0
          cmSystemTools::IsSubDirectory(a, b));
28
0
}
29
}
30
31
cmOutputConverter::cmOutputConverter(cmStateSnapshot const& snapshot)
32
0
  : StateSnapshot(snapshot)
33
0
{
34
0
  assert(this->StateSnapshot.IsValid());
35
0
  this->ComputeRelativePathTopSource();
36
0
  this->ComputeRelativePathTopBinary();
37
0
  this->ComputeRelativePathTopRelation();
38
0
}
39
40
void cmOutputConverter::ComputeRelativePathTopSource()
41
0
{
42
  // Walk up the buildsystem directory tree to find the highest source
43
  // directory that contains the current source directory.
44
0
  cmStateSnapshot snapshot = this->StateSnapshot;
45
0
  for (cmStateSnapshot parent = snapshot.GetBuildsystemDirectoryParent();
46
0
       parent.IsValid(); parent = parent.GetBuildsystemDirectoryParent()) {
47
0
    if (cmSystemTools::IsSubDirectory(
48
0
          snapshot.GetDirectory().GetCurrentSource(),
49
0
          parent.GetDirectory().GetCurrentSource())) {
50
0
      snapshot = parent;
51
0
    }
52
0
  }
53
0
  this->RelativePathTopSource = snapshot.GetDirectory().GetCurrentSource();
54
0
}
55
56
void cmOutputConverter::ComputeRelativePathTopBinary()
57
0
{
58
  // Walk up the buildsystem directory tree to find the highest binary
59
  // directory that contains the current binary directory.
60
0
  cmStateSnapshot snapshot = this->StateSnapshot;
61
0
  for (cmStateSnapshot parent = snapshot.GetBuildsystemDirectoryParent();
62
0
       parent.IsValid(); parent = parent.GetBuildsystemDirectoryParent()) {
63
0
    if (cmSystemTools::IsSubDirectory(
64
0
          snapshot.GetDirectory().GetCurrentBinary(),
65
0
          parent.GetDirectory().GetCurrentBinary())) {
66
0
      snapshot = parent;
67
0
    }
68
0
  }
69
70
0
  this->RelativePathTopBinary = snapshot.GetDirectory().GetCurrentBinary();
71
0
}
72
73
void cmOutputConverter::ComputeRelativePathTopRelation()
74
0
{
75
0
  if (cmSystemTools::ComparePath(this->RelativePathTopSource,
76
0
                                 this->RelativePathTopBinary)) {
77
0
    this->RelativePathTopRelation = TopRelation::InSource;
78
0
  } else if (cmSystemTools::IsSubDirectory(this->RelativePathTopBinary,
79
0
                                           this->RelativePathTopSource)) {
80
0
    this->RelativePathTopRelation = TopRelation::BinInSrc;
81
0
  } else if (cmSystemTools::IsSubDirectory(this->RelativePathTopSource,
82
0
                                           this->RelativePathTopBinary)) {
83
0
    this->RelativePathTopRelation = TopRelation::SrcInBin;
84
0
  } else {
85
0
    this->RelativePathTopRelation = TopRelation::Separate;
86
0
  }
87
0
}
88
89
std::string const& cmOutputConverter::GetRelativePathTopSource() const
90
0
{
91
0
  return this->RelativePathTopSource;
92
0
}
93
94
std::string const& cmOutputConverter::GetRelativePathTopBinary() const
95
0
{
96
0
  return this->RelativePathTopBinary;
97
0
}
98
99
void cmOutputConverter::SetRelativePathTop(std::string const& topSource,
100
                                           std::string const& topBinary)
101
0
{
102
0
  this->RelativePathTopSource = topSource;
103
0
  this->RelativePathTopBinary = topBinary;
104
0
  this->ComputeRelativePathTopRelation();
105
0
}
106
107
std::string cmOutputConverter::MaybeRelativeTo(
108
  std::string const& local_path, std::string const& remote_path) const
109
0
{
110
0
  bool localInBinary = PathEqOrSubDir(local_path, this->RelativePathTopBinary);
111
0
  bool remoteInBinary =
112
0
    PathEqOrSubDir(remote_path, this->RelativePathTopBinary);
113
114
0
  bool localInSource = PathEqOrSubDir(local_path, this->RelativePathTopSource);
115
0
  bool remoteInSource =
116
0
    PathEqOrSubDir(remote_path, this->RelativePathTopSource);
117
118
0
  switch (this->RelativePathTopRelation) {
119
0
    case TopRelation::Separate:
120
      // Checks are independent.
121
0
      break;
122
0
    case TopRelation::BinInSrc:
123
0
      localInSource = localInSource && !localInBinary;
124
0
      remoteInSource = remoteInSource && !remoteInBinary;
125
0
      break;
126
0
    case TopRelation::SrcInBin:
127
0
      localInBinary = localInBinary && !localInSource;
128
0
      remoteInBinary = remoteInBinary && !remoteInSource;
129
0
      break;
130
0
    case TopRelation::InSource:
131
      // Checks are identical.
132
0
      break;
133
0
  };
134
135
0
  bool const bothInBinary = localInBinary && remoteInBinary;
136
0
  bool const bothInSource = localInSource && remoteInSource;
137
138
0
  if (bothInBinary || bothInSource) {
139
0
    return cmSystemTools::ForceToRelativePath(local_path, remote_path);
140
0
  }
141
0
  return remote_path;
142
0
}
143
144
std::string cmOutputConverter::MaybeRelativeToTopBinDir(
145
  std::string const& path) const
146
0
{
147
0
  return this->MaybeRelativeTo(this->GetState()->GetBinaryDirectory(), path);
148
0
}
149
150
std::string cmOutputConverter::MaybeRelativeToCurBinDir(
151
  std::string const& path) const
152
0
{
153
0
  return this->MaybeRelativeTo(
154
0
    this->StateSnapshot.GetDirectory().GetCurrentBinary(), path);
155
0
}
156
157
std::string cmOutputConverter::ConvertToOutputForExisting(
158
  cm::string_view path, OutputFormat format, bool useWatcomQuote) const
159
0
{
160
#ifdef _WIN32
161
  // Cache the short paths since `GetShortPathNameW` is really expensive.
162
  static std::unordered_map<std::string, std::string> shortPathCache{};
163
164
  // If this is a windows shell, the path has a space, and the path already
165
  // exists, we can use a short-path to reference it without a space.
166
  if (this->GetState()->UseWindowsShell() &&
167
      path.find_first_of(" #") != std::string::npos) {
168
    auto origPath = std::string(path);
169
    if (cmSystemTools::FileExists(origPath)) {
170
      auto i = shortPathCache.find(origPath);
171
      if (i == shortPathCache.end()) {
172
        std::string shortPath;
173
        if (!cmSystemTools::GetShortPath(origPath, shortPath)) {
174
          // Fallback for cases when Windows refuses to resolve
175
          // the short path, like for `C:\Program Files\WindowsApps\...`.
176
          shortPath = origPath;
177
        }
178
        i = shortPathCache.emplace(std::move(origPath), std::move(shortPath))
179
              .first;
180
      }
181
      path = i->second;
182
    }
183
  }
184
#endif
185
186
  // Otherwise, perform standard conversion.
187
0
  return this->ConvertToOutputFormat(path, format, useWatcomQuote);
188
0
}
189
190
std::string cmOutputConverter::ConvertToOutputFormat(cm::string_view source,
191
                                                     OutputFormat format,
192
                                                     bool useWatcomQuote) const
193
0
{
194
0
  std::string result(source);
195
  // Convert it to an output path.
196
0
  if (format == SHELL || format == NINJAMULTI) {
197
0
    result = this->ConvertDirectorySeparatorsForShell(source);
198
0
    result = this->EscapeForShell(result, true, false, useWatcomQuote,
199
0
                                  format == NINJAMULTI);
200
0
  } else if (format == RESPONSE) {
201
0
    result =
202
0
      this->EscapeForShell(result, false, false, useWatcomQuote, false, true);
203
0
  }
204
0
  return result;
205
0
}
206
207
std::string cmOutputConverter::ConvertDirectorySeparatorsForShell(
208
  cm::string_view source) const
209
0
{
210
0
  std::string result(source);
211
  // For the MSYS shell convert drive letters to posix paths, so
212
  // that c:/some/path becomes /c/some/path.  This is needed to
213
  // avoid problems with the shell path translation.
214
0
  if (this->GetState()->UseMSYSShell() && !this->LinkScriptShell) {
215
0
    if (result.size() > 2 && result[1] == ':') {
216
0
      result[1] = result[0];
217
0
      result[0] = '/';
218
0
    }
219
0
  }
220
0
  if (this->GetState()->UseWindowsShell()) {
221
0
    std::replace(result.begin(), result.end(), '/', '\\');
222
0
  }
223
0
  return result;
224
0
}
225
226
static bool cmOutputConverterIsShellOperator(cm::string_view str)
227
0
{
228
0
  static std::set<cm::string_view> const shellOperators{
229
0
    "<", ">", "<<", ">>", "|", "||", "&&", "&>", "1>", "2>", "2>&1", "1>&2"
230
0
  };
231
0
  return (shellOperators.count(str) != 0);
232
0
}
233
234
std::string cmOutputConverter::EscapeForShell(cm::string_view str,
235
                                              bool makeVars, bool forEcho,
236
                                              bool useWatcomQuote,
237
                                              bool unescapeNinjaConfiguration,
238
                                              bool forResponse) const
239
0
{
240
  // Compute the flags for the target shell environment.
241
0
  int flags = 0;
242
0
  if (this->GetState()->UseWindowsVSIDE()) {
243
0
    flags |= Shell_Flag_VSIDE;
244
0
  } else if (!this->LinkScriptShell) {
245
0
    flags |= Shell_Flag_Make;
246
0
  }
247
0
  if (unescapeNinjaConfiguration) {
248
0
    flags |= Shell_Flag_UnescapeNinjaConfiguration;
249
0
  }
250
0
  if (makeVars) {
251
0
    flags |= Shell_Flag_AllowMakeVariables;
252
0
  }
253
0
  if (forEcho) {
254
0
    flags |= Shell_Flag_EchoWindows;
255
0
  }
256
0
  if (useWatcomQuote) {
257
0
    flags |= Shell_Flag_WatcomQuote;
258
0
  }
259
0
  if (forResponse) {
260
0
    flags |= Shell_Flag_IsResponse;
261
0
  }
262
0
  if (this->GetState()->UseWatcomWMake()) {
263
0
    flags |= Shell_Flag_WatcomWMake;
264
0
  }
265
0
  if (this->GetState()->UseMinGWMake()) {
266
0
    flags |= Shell_Flag_MinGWMake;
267
0
  }
268
0
  if (this->GetState()->UseNMake()) {
269
0
    flags |= Shell_Flag_NMake;
270
0
  }
271
0
  if (this->GetState()->UseNinja()) {
272
0
    flags |= Shell_Flag_Ninja;
273
0
  }
274
0
  if (!this->GetState()->UseWindowsShell()) {
275
0
    flags |= Shell_Flag_IsUnix;
276
0
  }
277
0
  if (this->GetState()->UseFastbuildMake()) {
278
    // Fastbuild needs to escape very few characters.
279
0
    flags = Shell_Flag_Fastbuild;
280
0
  }
281
282
0
  return cmOutputConverter::EscapeForShell(str, flags);
283
0
}
284
285
std::string cmOutputConverter::EscapeForShell(cm::string_view str, int flags)
286
0
{
287
  // Do not escape shell operators.
288
0
  if (cmOutputConverterIsShellOperator(str)) {
289
0
    return std::string(str);
290
0
  }
291
292
0
  return Shell_GetArgument(str, flags);
293
0
}
294
295
std::string cmOutputConverter::EscapeForCMake(cm::string_view str,
296
                                              WrapQuotes wrapQuotes)
297
0
{
298
  // Always double-quote the argument to take care of most escapes.
299
0
  std::string result = (wrapQuotes == WrapQuotes::Wrap) ? "\"" : "";
300
0
  for (char const c : str) {
301
0
    if (c == '"') {
302
      // Escape the double quote to avoid ending the argument.
303
0
      result += "\\\"";
304
0
    } else if (c == '$') {
305
      // Escape the dollar to avoid expanding variables.
306
0
      result += "\\$";
307
0
    } else if (c == '\\') {
308
      // Escape the backslash to avoid other escapes.
309
0
      result += "\\\\";
310
0
    } else {
311
      // Other characters will be parsed correctly.
312
0
      result += c;
313
0
    }
314
0
  }
315
0
  if (wrapQuotes == WrapQuotes::Wrap) {
316
0
    result += "\"";
317
0
  }
318
0
  return result;
319
0
}
320
321
std::string cmOutputConverter::EscapeWindowsShellArgument(cm::string_view arg,
322
                                                          int shell_flags)
323
0
{
324
0
  return Shell_GetArgument(arg, shell_flags);
325
0
}
326
327
cmOutputConverter::FortranFormat cmOutputConverter::GetFortranFormat(
328
  cm::string_view value)
329
0
{
330
0
  FortranFormat format = FortranFormatNone;
331
0
  if (!value.empty()) {
332
0
    for (std::string const& fi : cmList(value)) {
333
0
      if (fi == "FIXED") {
334
0
        format = FortranFormatFixed;
335
0
      }
336
0
      if (fi == "FREE") {
337
0
        format = FortranFormatFree;
338
0
      }
339
0
    }
340
0
  }
341
0
  return format;
342
0
}
343
344
cmOutputConverter::FortranPreprocess cmOutputConverter::GetFortranPreprocess(
345
  cm::string_view value)
346
0
{
347
0
  if (value.empty()) {
348
0
    return FortranPreprocess::Unset;
349
0
  }
350
351
0
  return cmIsOn(value) ? FortranPreprocess::Needed
352
0
                       : FortranPreprocess::NotNeeded;
353
0
}
354
355
void cmOutputConverter::SetLinkScriptShell(bool linkScriptShell)
356
0
{
357
0
  this->LinkScriptShell = linkScriptShell;
358
0
}
359
360
cmState* cmOutputConverter::GetState() const
361
0
{
362
0
  return this->StateSnapshot.GetState();
363
0
}
364
365
/*
366
367
Notes:
368
369
Make variable replacements open a can of worms.  Sometimes they should
370
be quoted and sometimes not.  Sometimes their replacement values are
371
already quoted.
372
373
VS variables cause problems.  In order to pass the referenced value
374
with spaces the reference must be quoted.  If the variable value ends
375
in a backslash then it will escape the ending quote!  In order to make
376
the ending backslash appear we need this:
377
378
  "$(InputDir)\"
379
380
However if there is not a trailing backslash then this will put a
381
quote in the value so we need:
382
383
  "$(InputDir)"
384
385
Make variable references are platform specific so we should probably
386
just NOT quote them and let the listfile author deal with it.
387
388
*/
389
390
/*
391
TODO: For windows echo:
392
393
To display a pipe (|) or redirection character (< or >) when using the
394
echo command, use a caret character immediately before the pipe or
395
redirection character (for example, ^>, ^<, or ^| ). If you need to
396
use the caret character itself (^), use two in a row (^^).
397
*/
398
399
/* Some helpers to identify character classes */
400
static bool Shell_CharIsWhitespace(char c)
401
0
{
402
0
  return ((c == ' ') || (c == '\t'));
403
0
}
404
405
static bool Shell_CharNeedsQuotesOnUnix(char c)
406
0
{
407
0
  return ((c == '\'') || (c == '`') || (c == ';') || (c == '#') ||
408
0
          (c == '&') || (c == '$') || (c == '(') || (c == ')') || (c == '~') ||
409
0
          (c == '<') || (c == '>') || (c == '|') || (c == '*') || (c == '^') ||
410
0
          (c == '\\'));
411
0
}
412
413
static bool Shell_CharNeedsQuotesOnWindows(char c)
414
0
{
415
0
  return ((c == '\'') || (c == '#') || (c == '&') || (c == '<') ||
416
0
          (c == '>') || (c == '|') || (c == '^'));
417
0
}
418
419
static bool Shell_CharIsMakeVariableName(char c)
420
0
{
421
0
  return c && (c == '_' || cmsysString_isalpha((static_cast<int>(c))));
422
0
}
423
424
bool cmOutputConverter::Shell_CharNeedsQuotes(char c, int flags)
425
0
{
426
  /* On Windows the built-in command shell echo never needs quotes.  */
427
0
  if (!(flags & Shell_Flag_IsUnix) && (flags & Shell_Flag_EchoWindows)) {
428
0
    return false;
429
0
  }
430
431
  /* On all platforms quotes are needed to preserve whitespace.  */
432
0
  if (Shell_CharIsWhitespace(c)) {
433
0
    return true;
434
0
  }
435
436
0
  if (flags & Shell_Flag_Fastbuild) {
437
0
    return false;
438
0
  }
439
440
  /* Quote hyphens in response files */
441
0
  if (flags & Shell_Flag_IsResponse) {
442
0
    if (c == '-') {
443
0
      return true;
444
0
    }
445
0
  }
446
447
0
  if (flags & Shell_Flag_IsUnix) {
448
    /* On UNIX several special characters need quotes to preserve them.  */
449
0
    if (Shell_CharNeedsQuotesOnUnix(c)) {
450
0
      return true;
451
0
    }
452
0
  } else {
453
    /* On Windows several special characters need quotes to preserve them.  */
454
0
    if (Shell_CharNeedsQuotesOnWindows(c) ||
455
0
        (c == ';' && (flags & Shell_Flag_VSIDE))) {
456
0
      return true;
457
0
    }
458
0
  }
459
0
  return false;
460
0
}
461
462
cm::string_view::iterator cmOutputConverter::Shell_SkipMakeVariables(
463
  cm::string_view::iterator c, cm::string_view::iterator end)
464
0
{
465
0
  while ((c != end && (c + 1) != end) && (*c == '$' && *(c + 1) == '(')) {
466
0
    cm::string_view::iterator skip = c + 2;
467
0
    while ((skip != end) && Shell_CharIsMakeVariableName(*skip)) {
468
0
      ++skip;
469
0
    }
470
0
    if ((skip != end) && *skip == ')') {
471
0
      c = skip + 1;
472
0
    } else {
473
0
      break;
474
0
    }
475
0
  }
476
0
  return c;
477
0
}
478
479
/*
480
Allowing make variable replacements opens a can of worms.  Sometimes
481
they should be quoted and sometimes not.  Sometimes their replacement
482
values are already quoted or contain escapes.
483
484
Some Visual Studio variables cause problems.  In order to pass the
485
referenced value with spaces the reference must be quoted.  If the
486
variable value ends in a backslash then it will escape the ending
487
quote!  In order to make the ending backslash appear we need this:
488
489
  "$(InputDir)\"
490
491
However if there is not a trailing backslash then this will put a
492
quote in the value so we need:
493
494
  "$(InputDir)"
495
496
This macro decides whether we quote an argument just because it
497
contains a make variable reference.  This should be replaced with a
498
flag later when we understand applications of this better.
499
*/
500
#define KWSYS_SYSTEM_SHELL_QUOTE_MAKE_VARIABLES 0
501
502
bool cmOutputConverter::Shell_ArgumentNeedsQuotes(cm::string_view in,
503
                                                  int flags)
504
0
{
505
  /* The empty string needs quotes.  */
506
0
  if (in.empty()) {
507
0
    return true;
508
0
  }
509
510
  /* Scan the string for characters that require quoting.  */
511
0
  for (cm::string_view::iterator cit = in.begin(), cend = in.end();
512
0
       cit != cend; ++cit) {
513
    /* Look for $(MAKEVAR) syntax if requested.  */
514
0
    if (flags & Shell_Flag_AllowMakeVariables) {
515
#if KWSYS_SYSTEM_SHELL_QUOTE_MAKE_VARIABLES
516
      cm::string_view::iterator skip = Shell_SkipMakeVariables(cit, cend);
517
      if (skip != cit) {
518
        /* We need to quote make variable references to preserve the
519
           string with contents substituted in its place.  */
520
        return true;
521
      }
522
#else
523
      /* Skip over the make variable references if any are present.  */
524
0
      cit = Shell_SkipMakeVariables(cit, cend);
525
526
      /* Stop if we have reached the end of the string.  */
527
0
      if (cit == cend) {
528
0
        break;
529
0
      }
530
0
#endif
531
0
    }
532
533
    /* Check whether this character needs quotes.  */
534
0
    if (Shell_CharNeedsQuotes(*cit, flags)) {
535
0
      return true;
536
0
    }
537
0
  }
538
539
  /* On Windows some single character arguments need quotes.  */
540
0
  if (flags & Shell_Flag_IsUnix && in.size() == 1) {
541
0
    char c = in[0];
542
0
    if ((c == '?') || (c == '&') || (c == '^') || (c == '|') || (c == '#')) {
543
0
      return true;
544
0
    }
545
0
  }
546
547
  /* UNC paths in MinGW Makefiles need quotes.  */
548
0
  if ((flags & Shell_Flag_MinGWMake) && (flags & Shell_Flag_Make)) {
549
0
    if (in.size() > 1 && in[0] == '\\' && in[1] == '\\') {
550
0
      return true;
551
0
    }
552
0
  }
553
554
0
  return false;
555
0
}
556
557
std::string cmOutputConverter::Shell_GetArgument(cm::string_view in, int flags)
558
0
{
559
  /* Output will be at least as long as input string.  */
560
0
  std::string out;
561
0
  out.reserve(in.size());
562
563
  /* Keep track of how many backslashes have been encountered in a row.  */
564
0
  int windows_backslashes = 0;
565
566
  /* Whether the argument must be quoted.  */
567
0
  int needQuotes = Shell_ArgumentNeedsQuotes(in, flags);
568
0
  if (needQuotes) {
569
    /* Add the opening quote for this argument.  */
570
0
    if (flags & Shell_Flag_WatcomQuote) {
571
0
      if (flags & Shell_Flag_IsUnix) {
572
0
        out += '"';
573
0
      }
574
0
      out += '\'';
575
0
    } else {
576
0
      out += '"';
577
0
    }
578
0
  }
579
580
  /* Scan the string for characters that require escaping or quoting.  */
581
0
  for (cm::string_view::iterator cit = in.begin(), cend = in.end();
582
0
       cit != cend; ++cit) {
583
    /* Look for $(MAKEVAR) syntax if requested.  */
584
0
    if (flags & Shell_Flag_AllowMakeVariables) {
585
0
      cm::string_view::iterator skip = Shell_SkipMakeVariables(cit, cend);
586
0
      if (skip != cit) {
587
        /* Copy to the end of the make variable references.  */
588
0
        while (cit != skip) {
589
0
          out += *cit++;
590
0
        }
591
592
        /* The make variable reference eliminates any escaping needed
593
           for preceding backslashes.  */
594
0
        windows_backslashes = 0;
595
596
        /* Stop if we have reached the end of the string.  */
597
0
        if (cit == cend) {
598
0
          break;
599
0
        }
600
0
      }
601
0
    }
602
603
    /* Check whether this character needs escaping for the shell.  */
604
0
    if (flags & Shell_Flag_IsUnix) {
605
      /* On Unix a few special characters need escaping even inside a
606
         quoted argument.  */
607
0
      if (*cit == '\\' || *cit == '"' || *cit == '`' || *cit == '$') {
608
        /* This character needs a backslash to escape it.  */
609
0
        out += '\\';
610
0
      }
611
0
    } else if (flags & Shell_Flag_EchoWindows) {
612
      /* On Windows the built-in command shell echo never needs escaping.  */
613
0
    } else {
614
      /* On Windows only backslashes and double-quotes need escaping.  */
615
0
      if (*cit == '\\') {
616
        /* Found a backslash.  It may need to be escaped later.  */
617
0
        ++windows_backslashes;
618
0
      } else if (*cit == '"') {
619
        /* Found a double-quote.  Escape all immediately preceding
620
           backslashes.  */
621
0
        while (windows_backslashes > 0) {
622
0
          --windows_backslashes;
623
0
          out += '\\';
624
0
        }
625
626
        /* Add the backslash to escape the double-quote.  */
627
0
        out += '\\';
628
0
      } else {
629
        /* We encountered a normal character.  This eliminates any
630
           escaping needed for preceding backslashes.  */
631
0
        windows_backslashes = 0;
632
0
      }
633
0
    }
634
635
    /* Check whether this character needs escaping for a make tool.  */
636
0
    if (*cit == '$') {
637
0
      if (flags & Shell_Flag_Make) {
638
        /* In Makefiles a dollar is written $$.  The make tool will
639
           replace it with just $ before passing it to the shell.  */
640
0
        out += "$$";
641
0
      } else if (flags & Shell_Flag_VSIDE) {
642
        /* In a VS IDE a dollar is written "$".  If this is written in
643
           an un-quoted argument it starts a quoted segment, inserts
644
           the $ and ends the segment.  If it is written in a quoted
645
           argument it ends quoting, inserts the $ and restarts
646
           quoting.  Either way the $ is isolated from surrounding
647
           text to avoid looking like a variable reference.  */
648
0
        out += "\"$\"";
649
0
      } else if (flags & Shell_Flag_Fastbuild) {
650
0
        out += "^$";
651
0
      } else {
652
        /* Otherwise a dollar is written just $. */
653
0
        out += '$';
654
0
      }
655
0
    } else if (*cit == '#') {
656
0
      if ((flags & Shell_Flag_Make) && (flags & Shell_Flag_WatcomWMake)) {
657
        /* In Watcom WMake makefiles a pound is written $#.  The make
658
           tool will replace it with just # before passing it to the
659
           shell.  */
660
0
        out += "$#";
661
0
      } else {
662
        /* Otherwise a pound is written just #. */
663
0
        out += '#';
664
0
      }
665
0
    } else if (*cit == '%') {
666
0
      if ((flags & Shell_Flag_VSIDE) ||
667
0
          ((flags & Shell_Flag_Make) &&
668
0
           ((flags & Shell_Flag_MinGWMake) || (flags & Shell_Flag_NMake)))) {
669
        /* In the VS IDE, NMake, or MinGW make a percent is written %%.  */
670
0
        out += "%%";
671
0
      } else {
672
        /* Otherwise a percent is written just %. */
673
0
        out += '%';
674
0
      }
675
0
    } else if (*cit == ';') {
676
0
      if (flags & Shell_Flag_VSIDE) {
677
        /* In VS a semicolon is written `";"` inside a quoted argument.
678
           It ends quoting, inserts the `;`, and restarts quoting.  */
679
0
        out += "\";\"";
680
0
      } else {
681
        /* Otherwise a semicolon is written just ;. */
682
0
        out += ';';
683
0
      }
684
0
    } else if (*cit == '\n') {
685
0
      if (flags & Shell_Flag_Ninja) {
686
0
        out += "$\n";
687
0
      } else {
688
0
        out += '\n';
689
0
      }
690
0
    } else if (*cit == '^' && (flags & Shell_Flag_Fastbuild)) {
691
0
      out += "^^";
692
0
    } else {
693
      /* Store this character.  */
694
0
      out += *cit;
695
0
    }
696
0
  }
697
698
0
  if (needQuotes) {
699
    /* Add enough backslashes to escape any trailing ones.  */
700
0
    while (windows_backslashes > 0) {
701
0
      --windows_backslashes;
702
0
      out += '\\';
703
0
    }
704
705
    /* Add the closing quote for this argument.  */
706
0
    if (flags & Shell_Flag_WatcomQuote) {
707
0
      out += '\'';
708
0
      if (flags & Shell_Flag_IsUnix) {
709
0
        out += '"';
710
0
      }
711
0
    } else {
712
0
      out += '"';
713
0
    }
714
0
  }
715
716
0
  if (flags & Shell_Flag_UnescapeNinjaConfiguration) {
717
0
    std::string ninjaConfigReplace;
718
0
    if (flags & Shell_Flag_IsUnix) {
719
0
      ninjaConfigReplace += '\\';
720
0
    }
721
0
    ninjaConfigReplace += "$${CONFIGURATION}";
722
0
    cmSystemTools::ReplaceString(out, ninjaConfigReplace, "${CONFIGURATION}");
723
0
  }
724
725
0
  return out;
726
0
}