Fix win32 printing, fix my stupid json vulnerability

This commit is contained in:
2026-06-01 06:37:14 -05:00
parent 25d67053cc
commit 41d503cfb5
11 changed files with 306 additions and 158 deletions

View File

@@ -1,6 +1,6 @@
# Maintainer: Mike Nolan <tesses@tesses.net> # Maintainer: Mike Nolan <tesses@tesses.net>
pkgname=tessesframework # '-bzr', '-git', '-hg' or '-svn' pkgname=tessesframework # '-bzr', '-git', '-hg' or '-svn'
pkgver=0.0.4 pkgver=0.0.5
pkgrel=1 pkgrel=1
pkgdesc="" pkgdesc=""
arch=('x86_64' 'powerpc') arch=('x86_64' 'powerpc')
@@ -10,7 +10,7 @@ groups=()
depends=('mbedtls') depends=('mbedtls')
makedepends=('git' 'cmake' 'make' 'base-devel' 'wget') # 'bzr', 'git', 'mercurial' or 'subversion' makedepends=('git' 'cmake' 'make' 'base-devel' 'wget') # 'bzr', 'git', 'mercurial' or 'subversion'
install= install=
source=('tessesframework::git+https://git.tesses.org/tesses50/tessesframework') source=('tessesframework::git+https://git.tesses.org/tesses50/tessesframework#tag=v0.0.5')
noextract=() noextract=()
sha256sums=('SKIP') sha256sums=('SKIP')
if [[ -z "$CMAKE_TOOLCHAIN" ]]; then if [[ -z "$CMAKE_TOOLCHAIN" ]]; then

View File

@@ -5,7 +5,7 @@ echo " desc \"\"" >> "Formula/tessesframework.rb"
echo " homepage \"\"" >> "Formula/tessesframework.rb" echo " homepage \"\"" >> "Formula/tessesframework.rb"
echo " url \"https://git.tesses.org/tesses50/tessesframework/archive/$VERSION.tar.gz\"" >> "Formula/tessesframework.rb" echo " url \"https://git.tesses.org/tesses50/tessesframework/archive/$VERSION.tar.gz\"" >> "Formula/tessesframework.rb"
echo " sha256 \"$HASH\"" >> "Formula/tessesframework.rb" echo " sha256 \"$HASH\"" >> "Formula/tessesframework.rb"
echo " license \"MIT\"" >> "Formula/tessesframework.rb" echo " license \"GPLv3\"" >> "Formula/tessesframework.rb"
echo " depends_on \"cmake\" => :build" >> "Formula/tessesframework.rb" echo " depends_on \"cmake\" => :build" >> "Formula/tessesframework.rb"
echo " depends_on \"mbedtls@3\"" >> "Formula/tessesframework.rb" echo " depends_on \"mbedtls@3\"" >> "Formula/tessesframework.rb"
echo " def install" >> "Formula/tessesframework.rb" echo " def install" >> "Formula/tessesframework.rb"

View File

@@ -1,5 +1,8 @@
# Changelog # Changelog
## 0.0.5
Fix win32 printing, fix my stupid json vulnerability
## 0.0.4 ## 0.0.4
Overhaul cmake configuration, add console api, fix http code that caused issues with cgi-bin Overhaul cmake configuration, add console api, fix http code that caused issues with cgi-bin

View File

@@ -1,4 +1,7 @@
if(TESSESFRAMEWORK_ENABLE_EXAMPLES) if(TESSESFRAMEWORK_ENABLE_EXAMPLES)
add_executable(start-my-website examples/start-my-website.cpp)
target_link_libraries(start-my-website PUBLIC tessesframework)
add_executable(echo-my-name examples/echo-my-name.cpp) add_executable(echo-my-name examples/echo-my-name.cpp)
target_link_libraries(echo-my-name PUBLIC tessesframework) target_link_libraries(echo-my-name PUBLIC tessesframework)

View File

@@ -1,3 +1,3 @@
set(TESSESFRAMEWORK_MAJOR_VERSION 0) set(TESSESFRAMEWORK_MAJOR_VERSION 0)
set(TESSESFRAMEWORK_MINOR_VERSION 0) set(TESSESFRAMEWORK_MINOR_VERSION 0)
set(TESSESFRAMEWORK_PATCH_VERSION 4) set(TESSESFRAMEWORK_PATCH_VERSION 5)

View File

@@ -0,0 +1,8 @@
#include "TessesFramework/TessesFramework.hpp"
int main(int argc, char **argv) {
Tesses::Framework::TF_Init();
Tesses::Framework::Platform::ShellFileOrUrl("https://tesses.net/");
return 0;
}

View File

@@ -20,60 +20,61 @@
#include <cstdio> #include <cstdio>
namespace Tesses::Framework { namespace Tesses::Framework {
enum class ConsoleColor { enum class ConsoleColor {
CC_BLACK, CC_BLACK,
CC_RED, CC_RED,
CC_GREEN, CC_GREEN,
CC_YELLOW, CC_YELLOW,
CC_BLUE, CC_BLUE,
CC_MAGENTA, CC_MAGENTA,
CC_CYAN, CC_CYAN,
CC_WHITE CC_WHITE
}; };
enum class ClearBehaviour { enum class ClearBehaviour {
CB_CURSORANDBELOW, CB_CURSORANDBELOW,
CB_CURSORANDABOVE, CB_CURSORANDABOVE,
CB_ENTIRESCREEN CB_ENTIRESCREEN
}; };
struct Console { struct Console {
static bool IsTTY(); static bool IsTTY();
static std::pair<int,int> GetSize(); static std::pair<int, int> GetSize();
static void ProgressBar(double); static void ProgressBar(double);
static void ProgressBar(int); static void ProgressBar(int);
static void Write(std::string text); static void Write(std::string text);
static void WriteLine(std::string text); static void WriteLine(std::string text);
static void WriteView(std::string_view text); static void WriteView(std::string_view text);
static void WriteLineView(std::string_view text); static void WriteLineView(std::string_view text);
static void Error(std::string text); static void Error(std::string text);
static void ErrorLine(std::string text); static void ErrorLine(std::string text);
static void ErrorView(std::string_view text); static void ErrorView(std::string_view text);
static void ErrorLineView(std::string_view text); static void ErrorLineView(std::string_view text);
static int Read(); static int Read();
static std::string ReadLine(); static std::string ReadLine();
static bool GetEcho(); static bool GetEcho();
static bool SetEcho(bool echo); static bool SetEcho(bool echo);
static bool GetCanonical(); static bool GetCanonical();
static bool SetCanonical(bool can); static bool SetCanonical(bool can);
static bool GetSignals(); static bool GetSignals();
static bool SetSignals(bool sig); static bool SetSignals(bool sig);
static void SetPosition(int x, int y); static void SetPosition(int x, int y);
static void SetBackgroundColor(ConsoleColor c, bool alt); static void SetBackgroundColor(ConsoleColor c, bool alt);
static void SetForegroundColor(ConsoleColor c, bool alt); static void SetForegroundColor(ConsoleColor c, bool alt);
static void Clear(); static void Clear();
static void MoveToHome(); static void MoveToHome();
static void ClearRetainPosition(ClearBehaviour clearBehaviour); static void ClearRetainPosition(ClearBehaviour clearBehaviour);
static void Flush(); static void Reset();
static void SetInvertedColors(bool inverted);
static void Flush();
//returns std::string::npos if no tty // returns std::string::npos if no tty
static size_t List(std::vector<std::string>& strs); static size_t List(std::vector<std::string> &strs);
static std::string ReadPassword();
static std::string ReadPassword(); private:
static void WriteToStream(std::string_view text, bool isStderr);
private: };
static void WriteToStream(std::string_view text, bool isStderr); } // namespace Tesses::Framework
};
}

View File

@@ -68,4 +68,6 @@ class Process {
~Process(); ~Process();
}; };
void ShellFileOrUrl(std::string fileOrUrl);
} // namespace Tesses::Framework::Platform } // namespace Tesses::Framework::Platform

View File

@@ -1,19 +1,19 @@
/* /*
TessesFramework a library to make C++ easier for me, used in CrossLang: TessesFramework a library to make C++ easier for me, used in CrossLang:
https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan https://git.tesses.org/tesses50/crosslang Copyright (C) 2026 Mike Nolan
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "TessesFramework/Console.hpp" #include "TessesFramework/Console.hpp"
@@ -34,6 +34,58 @@
namespace Tesses::Framework { namespace Tesses::Framework {
class ConsoleHelpers {
bool canon;
bool echo;
bool signals;
#if defined(_WIN32)
WORD colorFlags;
#endif
public:
ConsoleHelpers() {
canon = Console::GetCanonical();
echo = Console::GetEcho();
signals = Console::GetSignals();
#if defined(_WIN32)
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConsole == INVALID_HANDLE_VALUE)
return;
CONSOLE_SCREEN_BUFFER_INFO bi;
GetConsoleScreenBufferInfo(hConsole, &bi);
colorFlags = bi.wAttributes;
#endif
}
void reset() {
#if defined(_WIN32)
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConsole == INVALID_HANDLE_VALUE)
return;
SetConsoleTextAttribute(hConsole, colorFlags);
#else
if (Console::IsTTY()) {
printf("\x1b[0m");
fflush(stdout);
}
#endif
}
~ConsoleHelpers() {
Console::SetCanonical(canon);
Console::SetEcho(echo);
Console::SetSignals(signals);
reset();
}
};
ConsoleHelpers consoleHelpers;
size_t Console::List(std::vector<std::string> &strs) { size_t Console::List(std::vector<std::string> &strs) {
if (!IsTTY()) if (!IsTTY())
return std::string::npos; return std::string::npos;
@@ -54,11 +106,9 @@ size_t Console::List(std::vector<std::string> &strs) {
for (int item = 0; item < size.second; ++item) { for (int item = 0; item < size.second; ++item) {
if (item == offsetInPage) { if (item == offsetInPage) {
Console::SetBackgroundColor(ConsoleColor::CC_WHITE, true); Console::SetInvertedColors(true);
Console::SetForegroundColor(ConsoleColor::CC_BLACK, false);
} else { } else {
Console::SetBackgroundColor(ConsoleColor::CC_BLACK, false); Console::SetInvertedColors(false);
Console::SetForegroundColor(ConsoleColor::CC_WHITE, true);
} }
size_t myOffset = (size_t)item + page * (size.second); size_t myOffset = (size_t)item + page * (size.second);
@@ -72,8 +122,7 @@ size_t Console::List(std::vector<std::string> &strs) {
} }
} }
Console::SetBackgroundColor(ConsoleColor::CC_BLACK, false); Console::SetInvertedColors(false);
Console::SetForegroundColor(ConsoleColor::CC_WHITE, true);
int code = Console::Read(); int code = Console::Read();
@@ -102,49 +151,82 @@ size_t Console::List(std::vector<std::string> &strs) {
} }
Console::SetEcho(echo); Console::SetEcho(echo);
Console::SetCanonical(canonical); Console::SetCanonical(canonical);
Console::SetBackgroundColor(ConsoleColor::CC_BLACK, false); Console::SetInvertedColors(false);
Console::SetForegroundColor(ConsoleColor::CC_WHITE, true);
return i; return i;
} }
void Console::Reset() { consoleHelpers.reset(); }
#if defined(_WIN32)
static bool vt100_inverted = false;
static void _setcol(ConsoleColor col, bool alt, bool bg) {
if (bg) {
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConsole == INVALID_HANDLE_VALUE)
return;
CONSOLE_SCREEN_BUFFER_INFO bi;
GetConsoleScreenBufferInfo(hConsole, &bi);
bi.wAttributes &= ~(BACKGROUND_RED | BACKGROUND_GREEN |
BACKGROUND_BLUE | BACKGROUND_INTENSITY);
if (col == ConsoleColor::CC_RED || col == ConsoleColor::CC_YELLOW ||
col == ConsoleColor::CC_MAGENTA || col == ConsoleColor::CC_WHITE)
bi.wAttributes |= BACKGROUND_RED;
if (col == ConsoleColor::CC_GREEN || col == ConsoleColor::CC_YELLOW ||
col == ConsoleColor::CC_CYAN || col == ConsoleColor::CC_WHITE)
bi.wAttributes |= BACKGROUND_GREEN;
if (col == ConsoleColor::CC_BLUE || col == ConsoleColor::CC_CYAN ||
col == ConsoleColor::CC_MAGENTA || col == ConsoleColor::CC_WHITE)
bi.wAttributes |= BACKGROUND_BLUE;
if (alt)
bi.wAttributes |= BACKGROUND_INTENSITY;
SetConsoleTextAttribute(hConsole, bi.wAttributes);
} else {
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConsole == INVALID_HANDLE_VALUE)
return;
CONSOLE_SCREEN_BUFFER_INFO bi;
GetConsoleScreenBufferInfo(hConsole, &bi);
bi.wAttributes &= ~(FOREGROUND_RED | FOREGROUND_GREEN |
FOREGROUND_BLUE | FOREGROUND_INTENSITY);
if (col == ConsoleColor::CC_RED || col == ConsoleColor::CC_YELLOW ||
col == ConsoleColor::CC_MAGENTA || col == ConsoleColor::CC_WHITE)
bi.wAttributes |= FOREGROUND_RED;
if (col == ConsoleColor::CC_GREEN || col == ConsoleColor::CC_YELLOW ||
col == ConsoleColor::CC_CYAN || col == ConsoleColor::CC_WHITE)
bi.wAttributes |= FOREGROUND_GREEN;
if (col == ConsoleColor::CC_BLUE || col == ConsoleColor::CC_CYAN ||
col == ConsoleColor::CC_MAGENTA || col == ConsoleColor::CC_WHITE)
bi.wAttributes |= FOREGROUND_BLUE;
if (alt)
bi.wAttributes |= FOREGROUND_INTENSITY;
SetConsoleTextAttribute(hConsole, bi.wAttributes);
}
}
#endif
void Console::SetForegroundColor(ConsoleColor col, bool alt) { void Console::SetForegroundColor(ConsoleColor col, bool alt) {
if (!IsTTY()) if (!IsTTY())
return; return;
#if __has_include(<termios.h>)
if (alt) {
printf("\x1b[%im", ((int)col) + 90); // this should be Write
} else {
printf("\x1b[%im", ((int)col) + 30); // this too
}
#elif defined(_WIN32)
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); #if defined(_WIN32)
if (hConsole == INVALID_HANDLE_VALUE)
return;
CONSOLE_SCREEN_BUFFER_INFO bi;
GetConsoleScreenBufferInfo(hConsole, &bi); _setcol(col, alt, vt100_inverted);
bi.wAttributes &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
FOREGROUND_INTENSITY);
if (col == ConsoleColor::CC_RED || col == ConsoleColor::CC_YELLOW ||
col == ConsoleColor::CC_MAGENTA || col == ConsoleColor::CC_WHITE)
bi.wAttributes |= FOREGROUND_RED;
if (col == ConsoleColor::CC_GREEN || col == ConsoleColor::CC_YELLOW ||
col == ConsoleColor::CC_CYAN || col == ConsoleColor::CC_WHITE)
bi.wAttributes |= FOREGROUND_GREEN;
if (col == ConsoleColor::CC_BLUE || col == ConsoleColor::CC_CYAN ||
col == ConsoleColor::CC_MAGENTA || col == ConsoleColor::CC_WHITE)
bi.wAttributes |= FOREGROUND_BLUE;
if (alt)
bi.wAttributes |= FOREGROUND_INTENSITY;
SetConsoleTextAttribute(hConsole, bi.wAttributes);
#else #else
if (alt) { if (alt) {
@@ -158,40 +240,9 @@ void Console::SetForegroundColor(ConsoleColor col, bool alt) {
void Console::SetBackgroundColor(ConsoleColor col, bool alt) { void Console::SetBackgroundColor(ConsoleColor col, bool alt) {
if (!IsTTY()) if (!IsTTY())
return; return;
#if __has_include(<termios.h>)
if (alt) {
printf("\x1b[%im", ((int)col) + 100); // this should be Write
} else {
printf("\x1b[%im", ((int)col) + 40); // this too
}
#elif defined(_WIN32)
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); #if defined(_WIN32)
if (hConsole == INVALID_HANDLE_VALUE) _setcol(col, alt, !vt100_inverted);
return;
CONSOLE_SCREEN_BUFFER_INFO bi;
GetConsoleScreenBufferInfo(hConsole, &bi);
bi.wAttributes &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE |
BACKGROUND_INTENSITY);
if (col == ConsoleColor::CC_RED || col == ConsoleColor::CC_YELLOW ||
col == ConsoleColor::CC_MAGENTA || col == ConsoleColor::CC_WHITE)
bi.wAttributes |= BACKGROUND_RED;
if (col == ConsoleColor::CC_GREEN || col == ConsoleColor::CC_YELLOW ||
col == ConsoleColor::CC_CYAN || col == ConsoleColor::CC_WHITE)
bi.wAttributes |= BACKGROUND_GREEN;
if (col == ConsoleColor::CC_BLUE || col == ConsoleColor::CC_CYAN ||
col == ConsoleColor::CC_MAGENTA || col == ConsoleColor::CC_WHITE)
bi.wAttributes |= BACKGROUND_BLUE;
if (alt)
bi.wAttributes |= BACKGROUND_INTENSITY;
SetConsoleTextAttribute(hConsole, bi.wAttributes);
#else #else
if (alt) { if (alt) {
printf("\x1b[%im", ((int)col) + 100); // this should be Write printf("\x1b[%im", ((int)col) + 100); // this should be Write
@@ -201,6 +252,41 @@ void Console::SetBackgroundColor(ConsoleColor col, bool alt) {
#endif #endif
} }
void Console::SetInvertedColors(bool inverted) {
if (!IsTTY())
return;
#if defined(_WIN32)
if (inverted != vt100_inverted) {
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConsole == INVALID_HANDLE_VALUE)
return;
CONSOLE_SCREEN_BUFFER_INFO bi;
GetConsoleScreenBufferInfo(hConsole, &bi);
bi.wAttributes =
(bi.wAttributes & 0xFF00) |
((bi.wAttributes & (FOREGROUND_RED | FOREGROUND_GREEN |
FOREGROUND_BLUE | FOREGROUND_INTENSITY))
<< 4) |
((bi.wAttributes & (BACKGROUND_RED | BACKGROUND_GREEN |
BACKGROUND_BLUE | BACKGROUND_INTENSITY)) >>
4);
SetConsoleTextAttribute(hConsole, bi.wAttributes);
}
vt100_inverted = inverted;
#else
if (inverted)
Write("\x1b[7m");
else
Write("\x1b[27m");
Flush();
#endif
}
bool Console::SetEcho(bool echo) { bool Console::SetEcho(bool echo) {
if (!IsTTY()) if (!IsTTY())
return false; return false;
@@ -607,24 +693,33 @@ void Console::WriteToStream(std::string_view view, bool isStderr) {
} }
if (text.size() > 0 && text.back() == 'm') { if (text.size() > 0 && text.back() == 'm') {
if (text.size() == 1) {
try { consoleHelpers.reset();
auto num = } else {
std::stol(text.substr(0, text.size() - 1)); try {
if (num >= 30 && num <= 37) { auto num =
SetForegroundColor((ConsoleColor)(num - 30), std::stol(text.substr(0, text.size() - 1));
false); if (num == 0)
} else if (num >= 40 && num <= 47) { consoleHelpers.reset();
SetBackgroundColor((ConsoleColor)(num - 40), if (num == 7)
false); Console::SetInvertedColors(true);
} else if (num >= 90 && num <= 97) { if (num == 27)
SetForegroundColor((ConsoleColor)(num - 90), Console::SetInvertedColors(false);
true); if (num >= 30 && num <= 37) {
} else if (num >= 100 && num <= 107) { SetForegroundColor((ConsoleColor)(num - 30),
SetBackgroundColor((ConsoleColor)(num - 100), false);
true); } else if (num >= 40 && num <= 47) {
SetBackgroundColor((ConsoleColor)(num - 40),
false);
} else if (num >= 90 && num <= 97) {
SetForegroundColor((ConsoleColor)(num - 90),
true);
} else if (num >= 100 && num <= 107) {
SetBackgroundColor(
(ConsoleColor)(num - 100), true);
}
} catch (...) {
} }
} catch (...) {
} }
} }

View File

@@ -691,4 +691,38 @@ std::shared_ptr<Tesses::Framework::Streams::Stream> Process::GetStderrStream() {
#endif #endif
} }
void ShellFileOrUrl(std::string fileOrUrl) {
#if defined(GEKKO) || defined(__PS2__) || defined(__SWITCH__)
throw std::runtime_error("Platform not supported");
#elif !defined(TESSESFRAMEWORK_ENABLE_PROCESS)
throw std::runtime_error("Process not enabled");
#elif defined(_WIN32)
auto exec = Tesses::Framework::Platform::Environment::GetRealExecutablePath(
(std::string) "cmd");
Process p(exec.ToString(), {"cmd", "/c", "start", fileOrUrl});
if (p.Start())
if (p.WaitForExit() != 0)
throw std::runtime_error("Exit code did not indicate success");
#elif defined(__APPLE__)
auto exec = Tesses::Framework::Platform::Environment::GetRealExecutablePath(
(std::string) "open");
Process p(exec.ToString(), {"open", fileOrUrl});
if (p.Start())
if (p.WaitForExit() != 0)
throw std::runtime_error("Exit code did not indicate success");
#else
if (fileOrUrl == "--help" || fileOrUrl == "--version" ||
fileOrUrl == "--manual")
throw std::invalid_argument(
"Invalid argument \"" + fileOrUrl +
"\", this function expects a file or url not xdg-open flags");
auto exec = Tesses::Framework::Platform::Environment::GetRealExecutablePath(
(std::string) "xdg-open");
Process p(exec.ToString(), {"xdg-open", fileOrUrl});
if (p.Start())
if (p.WaitForExit() != 0)
throw std::runtime_error("Exit code did not indicate success");
#endif
}
} // namespace Tesses::Framework::Platform } // namespace Tesses::Framework::Platform

View File

@@ -15,9 +15,9 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "TessesFramework/Serialization/Json.hpp" #include "TessesFramework/Serialization/Json.hpp"
#include "TessesFramework/Http/HttpUtils.hpp" #include "TessesFramework/Http/HttpUtils.hpp"
#include "TessesFramework/TessesFramework.hpp"
#include <functional> #include <functional>
#include "TessesFramework/Common.hpp" #include "TessesFramework/Common.hpp"
@@ -129,6 +129,7 @@ JToken Json::Decode(std::string str) {
v |= HttpUtils::HexToNibble(str[i]) v |= HttpUtils::HexToNibble(str[i])
<< ((3 - i2) * 4); << ((3 - i2) * 4);
} }
i--;
uint32_t v2 = v; uint32_t v2 = v;
@@ -274,7 +275,8 @@ JToken Json::Decode(std::string str) {
break; break;
} else { } else {
throw std::runtime_error("Invalid JSON object."); throw std::runtime_error("Invalid JSON object. " +
std::to_string(tokenIndex));
} }
} }
return obj; return obj;