Coverage Report

Created: 2026-07-30 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmMathCommand.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 "cmMathCommand.h"
4
5
#include <cstdio>
6
#include <limits>
7
#include <stdexcept>
8
9
#include <cm/string_view>
10
#include <cmext/string_view>
11
12
#include <cm3p/kwiml/int.h>
13
14
#include "cmDiagnostics.h"
15
#include "cmExecutionStatus.h"
16
#include "cmExprParserHelper.h"
17
#include "cmMakefile.h"
18
#include "cmStringAlgorithms.h"
19
#include "cmValue.h"
20
21
namespace {
22
bool HandleExprCommand(std::vector<std::string> const& args,
23
                       cmExecutionStatus& status);
24
bool HandleIncDecCommand(std::vector<std::string> const& args,
25
                         cmExecutionStatus& status, int amount,
26
                         long long overflowFrom, long long overflowTo,
27
                         cm::string_view verbing);
28
}
29
30
bool cmMathCommand(std::vector<std::string> const& args,
31
                   cmExecutionStatus& status)
32
0
{
33
0
  if (args.empty()) {
34
0
    status.SetError("must be called with at least one argument.");
35
0
    return false;
36
0
  }
37
0
  std::string const& subCommand = args[0];
38
0
  if (subCommand == "EXPR") {
39
0
    return HandleExprCommand(args, status);
40
0
  }
41
0
  if (subCommand == "INCREMENT") {
42
0
    return HandleIncDecCommand(
43
0
      args, status, 1, std::numeric_limits<long long>::max(),
44
0
      std::numeric_limits<long long>::min(), "incrementing"_s);
45
0
  }
46
0
  if (subCommand == "DECREMENT") {
47
0
    return HandleIncDecCommand(
48
0
      args, status, -1, std::numeric_limits<long long>::min(),
49
0
      std::numeric_limits<long long>::max(), "decrementing"_s);
50
0
  }
51
0
  std::string e = "does not recognize sub-command " + subCommand;
52
0
  status.SetError(e);
53
0
  return false;
54
0
}
55
56
namespace {
57
bool HandleExprCommand(std::vector<std::string> const& args,
58
                       cmExecutionStatus& status)
59
0
{
60
0
  if ((args.size() != 3) && (args.size() != 5)) {
61
0
    status.SetError("EXPR called with incorrect arguments.");
62
0
    return false;
63
0
  }
64
65
0
  enum class NumericFormat
66
0
  {
67
0
    UNINITIALIZED,
68
0
    DECIMAL,
69
0
    HEXADECIMAL,
70
0
  };
71
72
0
  std::string const& outputVariable = args[1];
73
0
  std::string const& expression = args[2];
74
0
  size_t argumentIndex = 3;
75
0
  NumericFormat outputFormat = NumericFormat::UNINITIALIZED;
76
77
0
  status.GetMakefile().AddDefinition(outputVariable, "ERROR");
78
79
0
  if (argumentIndex < args.size()) {
80
0
    std::string const messageHint = "sub-command EXPR ";
81
0
    std::string const& option = args[argumentIndex++];
82
0
    if (option == "OUTPUT_FORMAT") {
83
0
      if (argumentIndex < args.size()) {
84
0
        std::string const& argument = args[argumentIndex++];
85
0
        if (argument == "DECIMAL") {
86
0
          outputFormat = NumericFormat::DECIMAL;
87
0
        } else if (argument == "HEXADECIMAL") {
88
0
          outputFormat = NumericFormat::HEXADECIMAL;
89
0
        } else {
90
0
          std::string error = messageHint + "value \"" + argument +
91
0
            "\" for option \"" + option + "\" is invalid.";
92
0
          status.SetError(error);
93
0
          return false;
94
0
        }
95
0
      } else {
96
0
        std::string error =
97
0
          messageHint + "missing argument for option \"" + option + "\".";
98
0
        status.SetError(error);
99
0
        return false;
100
0
      }
101
0
    } else {
102
0
      std::string error =
103
0
        messageHint + "option \"" + option + "\" is unknown.";
104
0
      status.SetError(error);
105
0
      return false;
106
0
    }
107
0
  }
108
109
0
  if (outputFormat == NumericFormat::UNINITIALIZED) {
110
0
    outputFormat = NumericFormat::DECIMAL;
111
0
  }
112
113
0
  cmExprParserHelper helper;
114
0
  if (!helper.ParseString(expression.c_str(), 0)) {
115
0
    status.SetError(helper.GetError());
116
0
    return false;
117
0
  }
118
119
0
  char buffer[1024];
120
0
  char const* fmt;
121
0
  switch (outputFormat) {
122
0
    case NumericFormat::HEXADECIMAL:
123
0
      fmt = "0x%" KWIML_INT_PRIx64;
124
0
      break;
125
0
    case NumericFormat::DECIMAL:
126
0
      CM_FALLTHROUGH;
127
0
    default:
128
0
      fmt = "%" KWIML_INT_PRId64;
129
0
      break;
130
0
  }
131
0
  snprintf(buffer, sizeof(buffer), fmt, helper.GetResult());
132
133
0
  std::string const& w = helper.GetWarning();
134
0
  if (!w.empty()) {
135
0
    status.GetMakefile().IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, w);
136
0
  }
137
138
0
  status.GetMakefile().AddDefinition(outputVariable, buffer);
139
0
  return true;
140
0
}
141
142
bool HandleIncDecCommand(std::vector<std::string> const& args,
143
                         cmExecutionStatus& status, int amount,
144
                         long long overflowFrom, long long overflowTo,
145
                         cm::string_view verbing)
146
0
{
147
0
  std::string const messageHint = cmStrCat("sub-command ", args[0], " ");
148
0
  if (args.size() != 2) {
149
0
    status.SetError(cmStrCat(messageHint, "wrong number of arguments"));
150
0
    return false;
151
0
  }
152
0
  auto value = status.GetMakefile().GetDefinition(args[1]);
153
0
  if (!value) {
154
0
    status.SetError(
155
0
      cmStrCat(messageHint, "variable \"", args[1], "\" is not defined"));
156
0
    return false;
157
0
  }
158
0
  if (value->empty()) {
159
0
    status.SetError(
160
0
      cmStrCat(messageHint, "value \"\" is not a valid integer"));
161
0
    return false;
162
0
  }
163
0
  std::size_t pos = 0;
164
0
  long long intValue = 0;
165
0
  try {
166
0
    intValue = std::stoll(*value, &pos, 10);
167
0
  } catch (std::invalid_argument&) {
168
    // Do nothing, leave pos as is, which will trigger the error
169
0
  }
170
0
  if (pos != value->length()) {
171
0
    status.SetError(
172
0
      cmStrCat(messageHint, "value \"", *value, "\" is not a valid integer"));
173
0
    return false;
174
0
  }
175
0
  auto newValue = intValue + amount;
176
0
  if (intValue == overflowFrom) {
177
0
    status.GetMakefile().IssueDiagnostic(
178
0
      cmDiagnosticCategory::CMD_AUTHOR,
179
0
      cmStrCat("signed integer overflow while ", verbing, ":\n  ", intValue,
180
0
               "\n"));
181
    // Overflow is undefined behavior in C++, so define it manually
182
0
    newValue = overflowTo;
183
0
  }
184
0
  status.GetMakefile().AddDefinition(args[1], std::to_string(newValue));
185
0
  return true;
186
0
}
187
}