Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmJSONState.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
4
#include "cmJSONState.h"
5
6
#include <iterator>
7
#include <memory>
8
#include <sstream>
9
10
#include <cm3p/json/reader.h>
11
#include <cm3p/json/value.h>
12
#include <cm3p/json/version.h>
13
14
#include "cmsys/FStream.hxx"
15
16
#include "cmStringAlgorithms.h"
17
#include "cmSystemTools.h"
18
19
cmJSONState::cmJSONState(std::string jsonFile, Json::Value* root)
20
0
  : Filename(std::move(jsonFile))
21
0
{
22
0
  cmsys::ifstream fin(this->Filename.c_str(), std::ios::in | std::ios::binary);
23
0
  if (!fin) {
24
0
    this->AddError(cmStrCat("File not found: ", this->Filename));
25
0
    return;
26
0
  }
27
  // If there's a BOM, toss it.
28
0
  cmsys::FStream::ReadBOM(fin);
29
30
  // Save the entire document.
31
0
  std::streampos finBegin = fin.tellg();
32
0
  this->doc = std::string(std::istreambuf_iterator<char>(fin),
33
0
                          std::istreambuf_iterator<char>());
34
0
  if (this->doc.empty()) {
35
0
    this->AddError("A JSON document cannot be empty");
36
0
    return;
37
0
  }
38
0
  fin.seekg(finBegin);
39
40
0
  Json::CharReaderBuilder builder;
41
0
  Json::CharReaderBuilder::strictMode(&builder.settings_);
42
0
  std::string errMsg;
43
44
0
#if JSONCPP_VERSION_HEXA >= 0x01090600
45
  // Has StructuredError
46
0
  std::unique_ptr<Json::CharReader> const reader(builder.newCharReader());
47
0
  reader->parse(doc.data(), doc.data() + doc.size(), root, &errMsg);
48
0
  std::vector<Json::CharReader::StructuredError> structuredErrors =
49
0
    reader->getStructuredErrors();
50
0
  for (auto const& structuredError : structuredErrors) {
51
0
    this->AddErrorAtOffset(structuredError.message,
52
0
                           structuredError.offset_start);
53
0
  }
54
#else
55
  // No StructuredError Available, Use error string from jsonCpp
56
  if (!Json::parseFromStream(builder, fin, root, &errMsg)) {
57
    errMsg = cmStrCat("JSON Parse Error: ", this->Filename, ":\n", errMsg);
58
    this->AddError(errMsg);
59
  }
60
#endif
61
0
}
62
63
void cmJSONState::AddError(std::string const& errMsg)
64
0
{
65
0
  this->errors.emplace_back(errMsg);
66
0
}
67
68
void cmJSONState::AddErrorAtValue(std::string const& errMsg,
69
                                  Json::Value const* value)
70
0
{
71
0
  if (value && !value->isNull()) {
72
0
    this->AddErrorAtOffset(errMsg, value->getOffsetStart());
73
0
  } else {
74
0
    this->AddError(errMsg);
75
0
  }
76
0
}
77
78
void cmJSONState::AddErrorAtOffset(std::string const& errMsg,
79
                                   std::ptrdiff_t offset)
80
0
{
81
0
  if (doc.empty()) {
82
0
    this->AddError(errMsg);
83
0
  } else {
84
0
    Location loc = LocateInDocument(offset);
85
0
    this->errors.emplace_back(loc, errMsg);
86
0
  }
87
0
}
88
89
std::string cmJSONState::GetErrorMessage(bool showContext)
90
0
{
91
0
  std::string message;
92
0
  std::string filenameName = cmSystemTools::GetFilenameName(this->Filename);
93
0
  for (auto const& error : this->errors) {
94
0
    Location loc = error.GetLocation();
95
0
    if (!filenameName.empty() && loc.line > 0) {
96
0
      message = cmStrCat(message, filenameName, ':', loc.line, ": ");
97
0
    }
98
0
    message = cmStrCat(message, error.GetErrorMessage(), '\n');
99
0
    if (showContext && loc.line > 0) {
100
0
      message = cmStrCat(message, GetJsonContext(loc), '\n');
101
0
    }
102
0
  }
103
0
  message.pop_back();
104
0
  return message;
105
0
}
106
107
std::string cmJSONState::key()
108
0
{
109
0
  if (!this->parseStack.empty()) {
110
0
    return this->parseStack.back().first;
111
0
  }
112
0
  return "";
113
0
}
114
115
std::string cmJSONState::key_after(std::string const& k)
116
0
{
117
0
  for (auto it = this->parseStack.begin(); it != this->parseStack.end();
118
0
       ++it) {
119
0
    if (it->first == k && (++it) != this->parseStack.end()) {
120
0
      return it->first;
121
0
    }
122
0
  }
123
0
  return "";
124
0
}
125
126
Json::Value const* cmJSONState::value_after(std::string const& k)
127
0
{
128
0
  for (auto it = this->parseStack.begin(); it != this->parseStack.end();
129
0
       ++it) {
130
0
    if (it->first == k && (++it) != this->parseStack.end()) {
131
0
      return it->second;
132
0
    }
133
0
  }
134
0
  return nullptr;
135
0
}
136
137
void cmJSONState::push_stack(std::string const& k, Json::Value const* value)
138
0
{
139
0
  this->parseStack.emplace_back(k, value);
140
0
}
141
142
void cmJSONState::pop_stack()
143
0
{
144
0
  this->parseStack.pop_back();
145
0
}
146
147
std::string cmJSONState::GetJsonContext(Location loc)
148
0
{
149
0
  std::string line;
150
0
  std::stringstream sstream(doc);
151
0
  for (int i = 0; i < loc.line; ++i) {
152
0
    std::getline(sstream, line, '\n');
153
0
  }
154
0
  return cmStrCat(line, '\n', std::string(loc.column - 1, ' '), '^');
155
0
}
156
157
cmJSONState::Location cmJSONState::LocateInDocument(ptrdiff_t offset)
158
0
{
159
0
  int line = 1;
160
0
  int col = 1;
161
0
  char const* beginDoc = doc.data();
162
0
  char const* last = beginDoc + offset;
163
0
  for (; beginDoc != last; ++beginDoc) {
164
0
    switch (*beginDoc) {
165
0
      case '\r':
166
0
        if (beginDoc + 1 != last && beginDoc[1] == '\n') {
167
0
          continue; // consume CRLF as a single token.
168
0
        }
169
0
        CM_FALLTHROUGH; // CR without a following LF is same as LF
170
0
      case '\n':
171
0
        col = 1;
172
0
        ++line;
173
0
        break;
174
0
      default:
175
0
        ++col;
176
0
        break;
177
0
    }
178
0
  }
179
0
  return { line, col };
180
0
}