Coverage Report

Created: 2026-02-09 06:05

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